首页 > 深入理解Java虚拟机 > 正文

[总结]-第三章 垃圾收集器与内存分配策略

佛若2018-09-120人围观

[总结]-第三章 垃圾收集器与内存分配策略

一、知识点

1、对象是否存活的判定

  • 引用计数法
  • 可达性分析(GC Roots)

判定对象死亡需要经过2次标记:

  • 第一次标记:可达性分析后没有与GC Roots相连接的引用链。
  • 第二次标记:GC对F-Queue中的对象进行标记(低优先级的Finalizer线程会去执行F-Queue中对象的finalize()方法)

2、引用分类

  • 强引用(Strong Reference)
    • 主要强引用还在,则垃圾收集器不会回收被引用的对象。
  • 软引用(Soft Reference)
    • 内存不足即将OOM时,回收。比如:缓存数据。
  • 弱引用(Weak Reference)
    • 只要gc就回收。
  • 虚引用(Phantom Reference)
    • 可以判断被关联的对象是否被GC

3、finalize( )

  • 任何一个对象的finalize()方法都只会被系统自动调用一次。(注意是对象,并不是类);
  • finalize()如果耗时过长,GC并不会等待。它只是通知另一个线程(Finalizer)去执行而已;
  • 知道即可,不建议使用;

4、垃圾收集算法

  • 引用计数器
    • 优点:实时性,只要对象的引用计数器为0,立刻回收;
    • 缺点:互相引用就永远无法去除。
  • 标记、清除(一般在老年代适用)
    • 标记:找到所有可访问的对象,做个标记;
    • 清除:遍历堆,把未标记的对象清除;
    • 缺点:1、标记、清除效率都低;2、空间内存碎片;3、STW;
  • 标记、整理(一般在老年代适用)
    • 标记:找到所有可访问的对象,做个标记;
    • 整理:将存活对象向一端移动,最后清除端边界以外的对象;
  • 复制(一般在新生代适用)
    • 优点:GC时,将存活的对象复制到另外一半空间中,删除当前空间所有遗留对象即可。简单高效。
    • 缺点:需要浪费一半空间。
    • 衍生:Eden(1)+ Survivor(2),默认 Eden:Survivor:Survivor = 8:1:1。
  • 分代收集
    • 新生代使用复制算法;
    • 年老代使用标记清除或标记整理算法。

5、安全点和安全区域

  • 安全点:以“是否具有让程序长时间执行的特征”来选定。比如方法调用、循环跳转、异常跳转等。程序运行到安全点后会停下来进行GC;

  • 安全区域:线程进入SafeRegion,如果此时JVM发起GC,则不管进入安全区域的线程。在线程离开安全区域之前,检查系统是否已经完成了根节点枚举或整个GC过程。如完成,则线程继续执行,如果没完成,则必须等待。

6、垃圾收集器

CMS(Concurrent Mark Sweep)

  • 定义:
    • 以获取最短回收停顿为目标的收集器;
    • 基于“标记-清除”算法。
  • 步骤:
    • 初始标记(STW):标记GC Roots能关联的对象,速度很快。
    • 并发标记:进行GC Roots Tracing(追踪)的过程,耗时较长。好在可以并发。
    • 重新标记(STW):修正并发标记期间,产生变动的那一部分对象的标记记录。
    • 并发清除:耗时较长。好在可以并发。
  • 优点:
    • 并发收集、低停顿。
  • 缺点:
    • CMS收集器对CPU资源非常敏感:CMS默认启动的回收线程数是(CPU数量+3)/4。
    • CMS收集器无法处理浮动垃圾:需要等待下一次GC时再清理。
    • 基于“标记清除”算法,会有大量空间碎片

G1(Garbage-First)

  • 定义:
    • 将整个java堆划成多个大小相等的独立区域(Region);
    • 通过维护一个优先列表,来有计划的回收价值最大的Region;
    • G1中每个Region都有 一个与之对应的Remembered Set,存储使用此对象的引用信息。
  • 步骤:
    • 初始标记
    • 并发标记
    • 最终标记:修正在并发标记期间,产生变动的那一部分标记记录。虚拟机会将变化计入Remembered Set Logs中(并发标记期间)。最终标记需要将Remembered Set Logs 合并到Remembered Set中。
    • 筛选回收:对各个Region的回收价值和成本进行排序,根据用户期望的GC停顿时间来制定计划后开始GC。
  • 优点:
    • 并行与并发
    • 分代收集
    • 空间整合:基于标记整理或复制算法,不会有空间碎片。
    • 可预测的停顿
  • 缺点:
    • 如果应用追求吞吐量,则G1并没有什么特别的好处。

7、新生代GC与老年代GC

  • 新生代GC(MinorGC):指发生在新生代的垃圾收集动作,因为Java对象大多都具备朝生夕灭的特性,所以MinorGC特别频繁,一般回收速度也比较快。

    • 当Eden区内存不足时,触发MinorGC。
    • 历次晋升的平均大小 < 老年代的连续空间,触发MinorGC。
  • 老年代GC(MajorGC/FullGC):指发生在老年代的GC,出现了MajorGC,经常会伴随至少一次的MinorGC。速度一般会比MinorGC慢10倍以上。

    • 历次晋升的平均大小 > 老年代的连续空间,触发FullGC。

只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行MinorGC,否则将进行FullGC。

8、进入老年代

  • 大对象直接进入老年代

    • -XX:PretenureSizeThreshold参数,比此值大的会直接在老年代分配。
    • 目的是避免在Eden区及两个Survivor区之间发生大量的内存复制。
  • 长期存活的对象进入老年代

    • -XX:MaxTenuringThreshold默认15,每次MinorGC后,年龄+1。
  • 动态对象年龄

    • 如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就直接进入老年代。