Minor GC,Major GC,Full GC -- hotspot VM GC讲解
针对HotSpot VM的实现,它里面的GC其实准确分类只有两大种:

- Partial GC:并不收集整个GC堆的模式
- Young GC:只收集young gen的GC
- Old GC:只收集old gen的GC。只有CMS的concurrent collection是这个模式
- Mixed GC:收集整个young gen以及部分old gen的GC。只有G1有这个模式
- Full GC:收集整个堆,包括young gen、old gen、perm gen(如果存在的话)等所有部分的模式。
Major GC通常是跟full GC是等价的,收集整个GC堆。但因为HotSpot VM发展了这么多年,外界对各种名词的解读已经完全混乱了,当有人说“major GC”的时候一定要问清楚他想要指的是上面的full GC还是old GC。
对于 Hotspot 中常见的收集器 :serial GC, parallel GC, CMS(concurrent Mark Sweep),G1(garbage first garbage collector)这是收集器算法,就是选择何时触发minor GC,和 full GC.
比如:
serial GC单线程 收集器,它要是执行,那么应用线程都得终止。触发条件:
young GC:当young gen中的eden区分配满的时候触发。注意young GC中有部分存活对象会晋升到old gen,所以young GC后old gen的占用量通常会有所升高。full GC:当准备要触发一次young GC时,如果发现统计数据说之前young GC的平均晋升大小比目前old gen剩余的空间大,则不会触发young GC而是转为触发full GC(因为HotSpot VM的GC里,除了CMS的concurrent collection之外,其它能收集old gen的GC都会同时收集整个GC堆,包括young gen,所以不需要事先触发一次单独的young GC);或者,如果有perm gen的话,要在perm gen分配空间但已经没有足够空间时,也要触发一次full GC;或者System.gc()、heap dump带GC,默认也是触发full GC。
Minor GC的过程:
1)新生成的对象在Eden区完成内存分配
2)当Eden区满了,再创建对象,会因为申请不到空间,触发minorGC,进行young(eden+1survivor)区的垃圾回收。(为什么是eden+1survivor:两个survivor中始终有一个survivor是空的,空的那个被标记成To Survivor)
3)minorGC时,Eden不能被回收的对象被放入到空的survivor(也就是放到To Survivor,同时Eden肯定会被清空),另一个survivor(From Survivor)里不能被GC回收的对象也会被放入这个survivor(To Survivor),始终保证一个survivor是空的。(MinorGC完成之后,To Survivor 和 From Survivor的标记互换)
4)当做第3步的时候,如果发现存放对象的那个survivor满了,则这些对象被copy到old区,或者survivor区没有满,但是有些对象已经足够Old(通过XX:MaxTenuringThreshold参数来设置),也被放入Old区
5)当Old区被放满的之后,进行完整的垃圾回收,即 Full GC
6)Full GC时,整理的是Old Generation里的对象,把存活的对象放入到Permanent Generation里。
--串行(–XX:+UseSerialGC )
Out of Box算法,年轻代串行复制,年老代串行标记整理,主要用于桌面应用
--并行(–XX:+UseParallelGC )
年轻代暂停应用程序,多个垃圾收集线程并行的复制收集,年老代暂停应用程序,与串行收集器一样,单垃圾收集线程标记整理。JDK 6.0启用该算法后,默认启用了-XX:+UseParallelOldGC,性能大为提高
--并发(Concurrent Low Pause Collector)( –XX:+UseConcMarkSweepGC )
启用该参数,默认启用了-XX:+UseParNewGC;简单的说,并发是指用户线程与垃圾收集线程并发,程序在继续运行,而垃圾收集程序运行于其他CPU上。
Parallel Scavenge(-XX:+UseParallelGC)框架下,默认是在要触发full GC前先执行一次young GC,并且两次GC之间能让应用程序稍微运行一小下,以期降低full GC的暂停时间(因为young GC会尽量清理了young gen的死对象,减少了full GC的工作量)。
Parallel GC 执行也是终止所有的应用线程,但是这个收集器是多线程的,目的就是利用CPU的多核资源。---目前客户端应用程序采用上述两种收集器。服务器端一般采用CMS 和 G1垃圾收集器,Java9 将默认采用G1垃圾收集器。
这张图来自于《深入理解Java虚拟机--jvm最高特性与最佳实践 周志明》,这张图也是很多Java初学者了解Hotspot GC需要记住了解的图。
下面 就说明一下图中这些minor GC,先说serialNew 和 serialOld 这个是单线程的,也就是stop all of world,当然其他的GC,比如parallelOld 和 parNew虽是多线程收集,但是还是stop the world.serialold 或者 serialNew 单线程,回收效率较低,那么为什么现在还在使用???那是因为其算法简单,停止所有应用线程去专心收集垃圾,收集效率极高,几乎把你所有没有使用到的对象垃圾都给你收回来。专心做收集,那么你的内存空间能多大呢?还不得几十毫秒或者几百毫秒就给你搞定的事情。停顿也是极短的时间。所以这个垃圾回收器一直都在使用。在client程序中也是主导的。
parNew 和 parallel Old 垃圾回收器和serial很相近,无非就是使用了多线程,目的就是借助处理器的多核能力。(毕竟现在处理器都是多核的)
parallel Scavenge 这个较为特殊的GC,其目的是为了实现最大的吞吐量,吞吐量=用户任务处理时间 / (用户处理时间 + 垃圾回收时间);说白了我就是给用户开口让其设置参数
来限制垃圾回收的时间,怎么限制垃圾回收时间,就是让垃圾不是那么彻底的回收干净,牺牲一定的yong 代空间,那么我让你回收时间缩短,增加吞吐量。
CMS consurrent mark sweep 这个回收器是在old 代的,主要和其他yong代配合使用,上述图中,只要垃圾回收器之间有连接线,那么就可以配合使用。CMS看其名称就知道基于标记-清除算法,标记清除-清除算法,有好处,啥好处?就是省空间,不复制,不节省空间嘛。但是产生垃圾碎片,导致存储空间不连续。有没有好的垃圾清除算法(垃圾清除算法主要就是标记算法,复制算法,标记-清除算法,标记整理算法),好的算法就是标记-整理算法,但是复杂呀!!!CMS是要降低停顿时间,你搞个那么复杂的算法,回收时间咋控制。
CMS工作主要分为四个步骤,初始标记--并发标记--重复标记--并发清除;初始标记和重复标记是stop the word,为啥??? 标记的时候为了标记的准确同时对整个old空间都做标记,应用线程不得停顿嘛!并发标记的时候是基于初始标记的结果,所以用户线程可以运行,所以这个过程是并发的。重复标记是同一个道理,回收的时候,就是多线程并发回收。CMS在回收的时候由于分多个过程,回收时间也稍稍长,那么这个过程中,用户线程并发性能回收影响。
在CMS收集垃圾的时候,会给用户线程留点空间,用于用户存储old数据,如果在垃圾回收的过程中,这块小区域满了,那么用户线程都要被停止。
G1垃圾回收器,garbage first,就是讲堆区划分很多的区域块,优先回收优先级较高的区域,而且会综合之前回收的经验,统计便于收集或者这块区域较为重要,或者垃圾较多的区域,对其优先收集。而且在手机过程中,其他区域是可以提供给用户开销的,所以G1几乎使得用户线程不停顿。
三、Java内存的调优参数
-Xmx1024m:
设置JVM最大可用内存为1024M。
-Xms1024m:
设置JVM促使内存为1024M。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn512m:
设置年轻代大小为512M。(持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。)
-Xss128k:
设置每个线程的堆栈大小。这个值可以根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成。
-XX:NewRatio=4
设置年轻代(包括Eden和两个Survivor区)与年老代的比值(总的大小是Xms的值)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5。
举个例子,-Xms 设置为 1024m,-Xmx 也设置为 1024m的情况下:
·年轻代 = 1024M/5 = 204.8M
·年老代 = 1024M/5*4 = 819.2M
如果-Xms和-Xmx的值设置的不一样,可以添加 -XX:MinHeapFreeRatio=<minimum> 和 -XX:MaxHeapFreeRatio=<maximum> 参数,使内存的大小能够在 大于 -Xms 和 小于 -Xmx 之间的范围内自动调整,所以内存中会有Virtual的空间(我是这样理解的,不是太清楚,这里需要大家指教)
By default, the virtual machine grows or shrinks the heap at each collection to try to keep the proportion of free space to live objects at each collection within a specific range. This target range is set as a percentage by the parameters -XX:MinHeapFreeRatio=<minimum> and -XX:MaxHeapFreeRatio=<maximum>, and the total size is bounded below by -Xms and above by -Xmx .
-XX:SurvivorRatio=4:
设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxPermSize=16m:
设置持久代大小为16m。
-XX:MaxTenuringThreshold=0:
设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。