目录
GC 回收机制
垃圾回收(Garbage Collection, GC)
垃圾回收算法
JVM 分代回收策略
1. 新生代
2. 老年代
GC Log 分析
引用
GC 回收机制
垃圾回收(Garbage Collection, GC)
垃圾就是内存中已经没有用的对象,JVM 中的垃圾回收器(Garbage Collector)会自动回收,Java 虚拟中使用“可达性分析”算法来决定对象是否可以被回收。如下图:
以 GC Root 作为起始点, 从这些节点开始向下搜索,所走过的路径称为引用链,最后通过判断对象的引用链是否可达来决定对象是否可以被回收。例如,上图中对象 A, B, C, D, E 与 GC Root 之间都存在一条直接或间接的引用链,这也代表他们与 GC Root 之间是可达的,因此它们是不能被 GC 回收掉的。而对象 M, K 虽然被对象 J 引用到,但是并不存在一条引用链连接 GC Root,所以 GC 回收时,只要遍历到 J, K, M 这三个对象,就会将它们回收。
在 Java 中,有以下几种对象可以作为 GC Root:
1. Java 虚拟机栈(局部变量表)中引用的对象;
2. 方法区中静态引用指向的对象;
3. 仍处于存活状态中的线程对象;
4. Native 方法中 JNI 引用的对象;
垃圾回收算法
1. 标记清除算法(Mark and Sweep GC)
从 “GC Roots” 集合开始,将内存整个遍历一次,保留所有可以被 GC Roots 直接或间接引用到的对象,而剩下的对象都当作垃圾并回收。
过程分两步:
Mark 标记阶段:找到内存中所有的 GC Root 对象,只要和 GC Root 直接或间接相连则标记为灰色(存活对象),否则标记为黑色(垃圾对象);
Sweep 清除阶段:遍历完所有的 GC Root 之后,则将标记为垃圾的对象直接删除。
优点:实现简单,不需要将对象进行移动;
缺点:需要中断进程内其它组件的执行(即,Stop the world),并且可能产生内存碎片,提高了垃圾回收的频率。
2. 复制算法(Copying)
将现有的内存空间分为两块,每次只使用其中一块。在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后清除正在使用的内存块中的对象,交换两个内存的角色,完成垃圾回收。
优点:按顺序分配内存即可,实现简单、运行高效,不用考虑内存碎片;
缺点:可用的内存大小缩小为原来的一半,对象存活率高时会频繁进行复制。
3. 标记-压缩算法(Mark-Compact)
需要先从根节点开始对所有可达对象做一次标记,之后并不简单的清除未标记的对象,而是将所有的存活对象压缩到内存的一端,最后清理边界外所有的空间。
过程分两步:
Mark 标记阶段:找到内存中所有的 GC Root 对象,只要和 GC Root 直接或间接相连则标记为灰色(存活对象),否则标记为黑色(垃圾对象);
Compact 压缩阶段:将剩余存活对象按顺序压缩到内存的某一端。
优点:即避免了碎片的产生,又不需要两块相同的内存空间,其性价比比较高;
缺点:所谓压缩操作,仍需要进行局部对象移动,一定程度上还是降低了效率。
JVM 分代回收策略
Java 虚拟机根据对象存活的周期不同,把堆内存划分为新生代、老年代,这就是 JVM 的内存分代策略。
1. 新生代
新生成的对象优先存放在新时代中,存活率很低。新生代中,常规的一次 GC 一般可以回收 70%~ 90% 的空间,回收率很高。所有,在新时代中采用的GC 回收算法是复制算法。
新生代细分为3个部分:Eden, Survivor0(简称 S0), Survivor1(简称 S1),这三部分按照 8 : 1 : 1的比例来划分新生代。
2. 老年代
一个对象如果在新生代存活了足够长的时间(15次 GC 后仍存活)而没有被清理掉,则会被复制到老年代。老年代的内存大小一般比新生代大,能存放更多的对象。如果对象比较大(如长字符串或者大数组),并且新生代的剩余空间不足,则这个大对象会直接被分配到老年代。可以使用
-XX:PretenureSizeThreshold
来控制直接升入老年代的对象大小。因为对象的生命周期较长,不需要过多的复制操作,所以一般采用标记压缩的回收算法。
GC Log 分析
为了让上层应用开发人员更加方便的调试 Java 程序,JVM 提供了相应的 GC 日志。在 GC 执行垃圾回收事件的过程中,会有各种相应的 log 被打印出来。其中新生代和老年代所打印的日志是有区别的:
新生代 GC:
这一区域的 GC 叫做 Minor GC。因为 Java 对象大多都具备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较块。
老年代 GC:
发生在这一区域的 GC 也叫做 Major GC 或者 Full GC。当出现 Major GC,经常会伴随至少一次的 Minor GC。
引用
通过 GC Roots 的引用可达性来判断对象是否存活。
JVM 中的引用关系根据引用强度的由强到弱,可分为:
强引用(Strong Reference)
软引用(Soft Reference)
弱引用(Weak Reference)
虚引用(Phantom Reference)