JVM将整个堆划分为Young区、Old区和Perm区,分别存放不同年龄的对象,这三个区存放的对象有如下区别:
- Young:分为Eden区和两个Survivor区,其中所有新创建的对象都在Eden区,当Eden区满后会出发minor GC将Eden区仍然存好的对象复制到其中一个Survivor区中,另外一个Survivor区中的存活对象也复制到这个Survivor中,以保证始终有一个Survivor区是空的。一般情况下,Young区的大小为整个堆区的1/4,Survivor区为整个Young区的1/8
- Old区:存放Young区的Survivor满后出发minor GC后仍然存活的对象,当Eden区满后会将对象存放到Survivor区中,如果Survivor区仍然存不下这些对象,GC收集器会将这些对象直接存放到Old区。如果在Survivor区中的对象足够老,也直接存放到Old区。如果Old区也满了,将会触发Full GC,回收整个堆内存
- Perm区:存放的主要是类的Class对象,如果一个类被频繁的加载,也可能会导致Perm区满,Perm区的垃圾回收也是由Full GC触发的
Serial Collector
JVM在client模式下默认的GC方式。可以通过-XX:+UseSerialGC来指定使用该收集算法,该算法是串行的,GC动作是单线程完成,因此GC时其他应用程序会全部停止(所谓的“stop-the-world”)。
当Minor GC时,除了将Eden区的非活动对象回收以外,还会把一些老对象也复制到Old区中。这个老对象的定义是通过配置参数MaxTenuringThreshold来控制的,如-XX:MaxTenuringThreshold=5,则如果这个对象已经被Minor GC回收过5次后仍然存活,那么这个对象在这次Minor GC后会直接被放入Old区。还有一种情况,当这次Minor GC时Survivor区中的To Space放不下这些对象时,这些对象也将直接放入Old区。如果Old区或者Perm区空间不足,将会触发Full GC,Full GC会检查堆中的所有对象,清除所有垃圾对象,如果是Perm区,会清除已经被卸载的classloader中加载的类的信息。
Parallel Collector
根据Minor GC 和 Full GC的不同分为三种。
1)ParNewGC
回收策略和Serial Collector类似,只是回收操作是多线程并行回收,但同样会导致“stop-the-world”。通过-XX:+UseParNewGC来指定,通过UseAdaptiveSizePolicy来配置Eden、From Space和To Space的TenuringThreshold大小。
2)ParallelGC
在Server下默认的GC方式,通过-XX:+UseParallelGC参数来指定,并行回收的线程数由-XX:ParallelGCThreads指定(如果CPU的核数小于8,线程数可以和核数一样,如果大于8,值为3 + core * 5 / 8)。Eden、From Space和To Space的大小通过SurvivorRatio来控制(-XX:SurvivorRatio=8表示Eden区和FromSpace的大小为8:1,默认情况就是这个值)。
当在Eden区中申请内存空间时,如果Eden区不够,那么看当前申请的空间是否大于等于Eden的一半,如果大于则这次申请的空间直接在Old中分配,如果小于则触发Minor GC。在触发GC之前首先会检查每次晋升到Old区的平均大小是否大于Old区的剩余空间,如大于则再触发Full GC。这次触发GC后会按照这个规则再重新检查一次,也就是Full GC会执行两次。Full GC时清空堆中所有的垃圾对象和Perm区中已经被卸载的类信息,并进行压缩。这里有几个配置项了解一下
AlwaysTenure:默认为flase,表示只要Minor GC时存活就晋升到Old区
NeverTenure:默认为false,表示永远晋升到Old区
UseAdaptiveSizePolicy:上面的两个都没设置时,使用该配置项,在启动时以InitialTenuringThreshold值作为存活次数的阈值,每次GC后会动态调整。-XX: - UseAdaptiveSizePolicy表示不使用该配置
3)ParallelOldGC
通过-XX:+UseParallelOldGC参数来指定,并行回收的线程数由-XX:ParallelOldGCThreads指定(如果CPU的核数小于8,线程数可以和核数一样,如果大于8,值为3 + core * 5 / 8)。和ParallelGC只有一点不同,Full GC时清空堆中部分的垃圾对象,进行部分的空间压缩,并不是所有垃圾对象。
CMS Collector
通过-XX:+UseConcMarkSweepGC参数来指定,并发回收的线程数由-XX:ParallelCMSThreads指定(默认值为4)。该收集算法因为是并发的,所以不会导致“stop-the-world”,这一点在很多场景下是很有用的,例如WebServer,不会导致用户无法访问页面。触发规则为检查Old区或者Perm区的使用率,当达到一定比例时(CMSInitialOccupancyFraction指定)触发CMS GC,触发时会回收Old区中的内存空间。触发Full GC的两种情况:
1)Eden分配失败,Minor GC后分配到To Space,ToSpace不够再分配到Old区,Old区不够再出发Full GC
2)当CMS GC正在进行时向Old申请内存失败则会直接触发Full GC。
G1
java9默认回收器。通过-XX:+UseG1GC参数来指定。可设置预期的停顿时间,不会产生内存碎片。G1将整个堆划分成多个等大的独立区域(region),保留了新生代、老年代,但是不再是物理隔离,而是部分region的集合。G1跟踪各个Region里的垃圾回收价值大小(回收所获得的空间大小以及所需时间的经验值;维护了一个优先列表,每次根据允许的回收时间,优先回收价值最大的region(用最短的时间回收最多的垃圾),这也是Garbage-First名称的由来
GC | 优点 | 缺点 |
---|---|---|
Serial(串行) | 适合内存有限的情况下 | 回收慢 |
Parallel(并行) | 效率高 | 当Heap过大时,应用程序暂停时间较长 |
CMS(并发) | Old区回收暂停时间短 | 产生内存碎片、整个GC耗时长、比较耗CPU |
G1 | 可设置预期的停顿时间,不产生内存碎片 | 适合大内存,一般6G以上,小内存不适用 |
如何选择垃圾回收器
官网是给出了建议:
- If the application has a small data set (up to approximately 100 MB), then select the serial collector with the option -XX:+UseSerialGC.
- 小内存使用Serial即可
- If the application will be run on a single processor and there are no pause-time requirements, then select the serial collector with the option -XX:+UseSerialGC.
- 单核处理器,并且没有停顿时间要求,使用Serial
- If (a) peak application performance is the first priority and (b) there are no pause-time requirements or pauses of one second or longer are acceptable, then let the VM select the collector or select the parallel collector with -XX:+UseParallelGC.
- 追求高性能,但是对停顿时间没要求,使用Parallel
- If response time is more important than overall throughput and garbage collection pauses must be kept shorter than approximately one second, then select a concurrent collector with -XX:+UseG1GC or -XX:+UseConcMarkSweepGC.
- 响应时间比吞吐量更主要,并且停顿时间不能太长,则使用G1或者CMS;如果分配的堆内存达到6G或以上,则使用G1
关于各种垃圾回收器的调优,官网很详细,需要一点英文基础
GC策略参数调整: