垃圾回收机制

  1. 释放垃圾占用的空间,防止内存泄露。
  2. 有效的使用可以使用的空间,对于堆中已经死亡和长时间没有使用的对象进行清理和回收。

问题1:为什么新生代需要分为Eden和两个S区?

内存模型

之前在java内存细分中提及到堆内存的细分

  • 新生代:Eden、SurvivorA、SurvivorB
  • 老年代

这个问题首先要从几种回收算法来看

  • 标记清除:先标记不可达,在清理,效率不高,内存碎片化,适合在老年代进行垃圾回收,比如CMS收集器就是采用该算法进行回收的
  • 标记整理:先标记,存活的对象整理到一起,标记和整理的效率都不高,适合老年代进行垃圾收集
  • 复制算法

    好好说说这个复制算法。

    这个复制算法是怎么一回事呢,它将内存空间一分为二,一个称之为From区,一个称之为To区域。

    1. 对象首先在From区分配
    2. From区内存不够之后,根据可达性讲存活的对象复制到To区域
    3. 清空From区
    4. 交换From和To区的指针,仍然在From区分配,显然这个时候From区域内存也是整齐的

复制算法有一个显而易见的缺点,就是有一半部分内存是无法使用的,因为指针交换的特性,From和To区域分配不一样大小的内存也不现实。

为了改善这种情况,HotSpot虚拟机引入Eden区,作为两个S区域的共享部分来优化复制算法。

引入Eden区域之后:新生代的内存划分:Eden-80%,两个S区域各占10%。

运作过程:

  1. 新的对象首先在Eden区分配内存
  2. 需要发生Young-GC的之后,将Eden区和From区中存活的对象统统复制到To区域
  3. 清空Eden和From区域。之后交换From和To的指针。
  4. 仍然在Eden区分配内存

问题2:如何判断哪些对象应该进入老年代

  1. 对象GC年龄,从From到To的次数是对象的GC年龄,记录在对象头的MarkWord中
  2. 可以通过虚拟机设置进入老年代的年龄,默认是15,也是最大的。
  3. 动态年龄判断

    静态的年龄判断是有一定缺点的,设置小了可能会频繁触发Young-GC,“过早晋升”,设置大了就实去了分代的意义

    具体是如何动态的呢?

    • 叠加各个年龄对象所占内存的总和。一旦大于当前Survivor大小的的一半,取晋升阈值和和累加超过一半的年龄中的最小值作为最终的晋升阈值来决定哪些对象晋升到老年代。

问题3:如何判断一个对象是否死亡

主要方法有两个:

  1. 引用计数法,无法解决循环引用的问题
  2. 可达性分析,从GC-root触发看对象是否可达。

    GC-root主要有

    • 虚拟机栈中的引用指向的对象
    • 方法区中类静态变量引用的对象
    • 方法区中的常量引用的对象
    • Native方法引用的对象
可见是否被回收和引用使用关系的

引用类型

GC策略

简介

强引用(Strong Reference)

永远不会回收(GC ROOT可引用到的前提下)

最基本的引用Object obj=new Object()

软引用(Soft Reference)

OOM之前回收

SoftReference

弱引用(Weak Reference)

下一次GC前

WeakReference

虚引用(Phantom Reference)

未知,也就是随时可能被回收

PhantomReference

针对软引用的特性:可能会被用在一些高速缓存中,如果将要OOM则回收。

针对弱引用:WeakHashMap可以在映射没有删除之前

并不是不可达即死亡

标记为不可达的对象,要判断其是否有必要执行finalize()方法,判断标准主要是

  • 有无调用过该方法
  • 有无覆盖过该方法

如果有一个满足则认为有比较执行该方法,随即将他们放入一个队列中执行,这是对象最后一次挽救自己的机会,如果仍然不可达,二次标记后回收。

该方法因为自身的缺陷已经被废弃。

问题4:堆内存分配策略

  1. Eden区域优先为对象分配内存,Eden区域内存空间不足,会发起Young-GC将一部分时间存活长的对象晋升到老年代,这里会涉及到GC的分配担保机制。

    分配担保机制

    在每次发生Young-GC之前,确保老年代最大连续空间可以放得下新生代所有的对象

    • 这个条件如果满足,说明这是一次安全的Young-G。
    • 如果不满足的话,在JDK1.6之前,可以通过虚拟机设置是否允许担保失败即进行一次有风险的尝试。1.6某个版本更新之后,采用的是,如果老年代最大连续空间大于历次晋升对象的平均大小即允许尝试。
分配担保机制的存在还是为了降低Full-GC的频率
  1. 大对象直接进入老年代。

    大对象占据的内存比较大,触发Young-GC的概率也会提升,同时由于分配担保机制的存在,大对象也有可能直接触发Full-GC,同时复制也比较耗时。

  2. 长期存活的对象进入老年代,如问题2
  3. 动态年龄判断,如上述。

问题5:方法区的GC

主要涉及常量和不再使用的类的回收

  • 常量:不再被其他任何地方引用的时候
  • 无用类:

    • 堆上无实例
    • 加载器被回收
    • Class对象不再被使用

zygan
0 声望0 粉丝