GC是影响JVM性能的重要因素,不同jvm厂商、版本实现了不同垃圾收集器和算法,各有优缺点。本文就jvm内存划分做进一步补充说明并聊聊各种常见的垃圾收集器,它们的基本算法和使用场景以及一些GC调优的方法。
结合jvm内存划分我们知道GC通常是运行在堆内存上的,有时方法区也会运行GC。要进行垃圾回收,我们首先要知道哪些对象可以被回收,这里主要有 引用计数法 和 可达性分析 两种方法标记对象是否需要回收。
对于方法区的GC复杂一点,首先Bootstrap Classloader加载的类通常是不会被回收的,其他普通加载器加载的类的回收要求加载该类的加载器被回收了才能进行,对于大量使用动态类型的应用要注意方法区内存避免方法区OOM。
具体清理对象的时候通常有三种算法,复制、标记清除、标记整理:
垃圾收集过程
上面算法不是单独运行在虚拟机上的,通常他们都会配合使用。对于垃圾收集通常会将内存分为 Eden
, Survivor
, Tenured
区如上图所示。通常新生代(New Generation)指Eden和Survivor,老年代(Old Generation)指Tenured。
minor GC
被引用存活下来的对象将被复制到一个Survivor区,并且生命值加1 major GC
。达到某个阈值对整个内存进行整理的成为 Full GC
Stop-The-World
转态,但它实现简单高效实用很多场景,运行在新生代。(Serial Old是老年代收集器采用标记整理算法)。 -XX:+UseSerialGC
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC
另外还有一些正在研发中各有特点的GC,如 ZGC Oracle开源的超级GC,支持Tbytes级别的超大堆,而且大部分情况下GC延时低于10ms。 Epsilon GC 只有开销但是不做GC,用来做性能测试。
GC优化是JVM优化的一个场景,通常JVM优化的很多方面最终都会落到GC优化上。遇到所有优化的场景一定要明确要优化的目标参数是什么。GC中通常关注三个方面,内存占用(footprint)、延时(latency)、吞吐量(throughput)。有时不合理的GC策略也能可能会造成OOM。
面对GC调优通常需要确定确实有调优的必要,然后使用jstat查看GC相关状态,开启GC日志并分析定位问题。分析选择的垃圾回收器是否符合应用的表现特征,是minor gc过长还是major gc过长,尝试使用CMS,G1 这种低延迟的垃圾回收器。