垃圾回收机制
自动垃圾收集
自动垃圾收集是查看堆内存,识别正在使用哪些对象以及哪些对象未被删除以及删除未使用对象的过程。
使用中的对象或引用的对象意味着程序的某些部分仍然维护指向该对象的指针。
程序的任何部分都不再引用未使用的对象或未引用的对象,因此可以回收未引用对象使用的内存。
如何确定内存需要被回收
该过程的第一步为标记。这是垃圾收集器识别哪些内存正在使用而哪些不在使用的地方。
不同类型内存的判断方式
- 对象回收–引用计数
- 对象回收–可达性分析
- 方法区回收
可达性分析算法
简单来说,将对象及其引用关系看做一个图,选定活动的对象作为GC Roots;
然后跟踪引用链条,如果一个对象和GC Roots之间不可达,也就是不存在引用,那么即可认为是可回收对象。
可以作为GC Roots的对象
- 虚拟机栈中正在引用的对象
- 本地方法栈中正在引用的对象
- 方法区中静态属性引用的对象
- 方法区中静态常量引用的对象
引用类型和可达性级别
引用类型
- 强引用(StrongReference): 最常见的普通对象引用,只要还有强引用指向一个对象,就不会回收。
- 软引用(SoftReference): JVM认为内存不足是,才会去试图回收软引用指向的对象。(缓存场景)
- 弱引用(WeekReference): 虽然是引用,但随时可能被回收掉。
- 虚引用(PhantomReference): 不能通过它访问对象。供了对象被finalize以后,执行执行逻辑的机制。
可达性级别
- 强可达(Strongly Reachable): 一个对象可以有一个或多个线程可以不通过其他引用访问到的情况。
- 软可达(Softly Reachable): 就是当我们只能通过软引用才能访问到对象的状态。
- 弱可达(Weekly Reachable): 只能通过弱引用访问时的状态。当弱引用被清除的时候,就符合销毁条件。
- 幻象可达(Phantom Reachable): 不存在其他引用,并且finalize过了,只有幻想引用指向这个对象。
- 不可达(unreachable): 意味着对象可以被清除了。
垃圾收集算法
-
标记-清除(Mark-Sweep)算法: 首先标识出所有要回收的对象,然后进行清除。
标记、清除过程效率有限,有内存碎片化问题,不适合特别大的堆;收集算法基本基于标记-清除的思路进行改进。
-
复制(Copy)算法: 划分两块同等大小的区域,收集时将活着的对象复制到另一块区域。
拷贝过程中将对象顺序放置,就可以避免内存碎片化。复制+预留内存,有一定的浪费。
-
标记-整理(Mark-Compact)算法: 类似于标记-清除,但为了避免内存碎片化,它会在清理过程中将对象进行移动,以确保移动后的对象占用连续的内存空间。
分代收集
根据对象的存活周期,将内存划分为几个区域,不同区域采用合适的垃圾收集算法。
新对象会分配到Eden,如果超过-XX:+PretenureSizeThreshold:设置大对象直接进入老年代的阈值。
垃圾收集器
- 串行收集器 -Serial GC -XX:+UseSerialGC 单个线程来执行所有垃圾收集的工作,适用于单处理器机器。Client模式下JVM的默认选项。
- 串行收集器 -Serial Old -XX:+UseSerialGC 可以在老年代使用,它采用了标记-整理(Mark-Compact)算法,区别于新生代的复制算法。
-
并行收集器 -Parallel GC -XX:+UseSerialGC
-
串行收集器 -Parallel Old -XX:+UseSerialGC
server模式JVM的默认GC选择, 整体算法和Serial比较相似,区别是新生代和老年代GC都是并行进行;可以设置GC时间或吞吐量等值,可以自动进行适应性调整Eden,Survivor大小和MaxTenuringThreshold的值。
可称为吞吐量优先的GC:吞吐量=用户代码运行时间/(用户代码运行时间+GC时间)
-
-XX:ParallelGCThreads: 设置用于垃圾回收的线程数。通常情况下可以和CPU数量相等。
-
-XX:MaxGCPauseMills: 设置最大垃圾收集停顿时间。它的值是一个大于0的整数。
-
-XX:GCTimeRadio: 设置吞吐量大小。它的值是一个0-100之间的整数。
-
-XX:+UseAdaptiveSizePolicy: 打开自适应GC策略。以达到在堆大小、吞吐量和停顿时间之间的平衡点。
-
并发收集器 -CMS(Concurrent Mark Sweep) GC -XX:+UseConcMarkSweepGC 专用于老年代,基于标记-清除算法,设计目标是尽量减少停顿时间。
采用的标记-清除算法,存在着内存碎片化问题,长时间运行等情况下发生full GC,导致恶劣的停顿。
CMS会占用更多CPU资源,并和用户线程争抢。
减少了停顿时间,这一点对于互联网web等对时间敏感的系统非常重要。
- 并行收集器 -ParNew GC -XX:+UseParNewGC 新生代GC实现,它实际是Serial GC的多线程版本。可以控制线程数量,参数:-XX:ParallelGCThreads。最常见的应用场景是配合老年代的CMS GC工作。参数:-XX:+UseConcMarkSweepGC。
- 并发收集器 -G1 -XX:+UserG1GC 针对大堆内存设计的收集器,兼顾吞吐量和停顿时间,JDK9后默认选型,目标是替代CMS;G1可以将堆分成固定大小的区域,Region之间是复制算法,但整体上实际可看做是标记-整理算法。可以有效地避免内存碎片。红色新生代(Eden和Survivor),淡蓝色老年代。找不到大内存时执行FullGC。