垃圾收集器
如果说垃圾收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现
(图中有连线的地方代表可以配合使用)
Serial收集器
单线程收集器,“单线程”的意义不仅仅说明它只会使用一个CPU或一个收集线程去完成垃圾收集工作;
更重要的是它在垃圾收集的时候,必须暂停其他工作线程,直到垃圾收集完毕;-----stop-the-world
Serial收集器也并不是只有缺点;Serial收集器由于简单并且高效;
对于单CPU环境来说,由于Serial收集器没有线程间的交互,专心做垃圾收集自然可以做获得最高的垃圾收集效率
使用方式:
-XX:+UseSerialGC
ParNew 收集器
ParNew收集器是Serial收集器的多线程版本;
除了使用多线程收集以外,其余行为和Serial收集器是一样的(收集算法、stopTheWorld)
ParNew收集器在单CPU服务器上的垃圾收集效率绝对不会比Serial收集器高;
但是在多CPU服务器上,效果会明显比Serial好
使用方式:
-XX:+UseParNewGC
Parallel Scavenge收集器
Parallel Scavenge收集器是一个新生代的收集器,并且使用复制算法,而且是一个并行的多线程收集器
Parallel Scavenge收集器的关注的点和其他收集器是不一样的;
其他收集器是尽量缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标是达到一个可控制的吞吐量(Throughput);
吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
(虚拟机总共运行100分钟,垃圾收集时间为1分钟,那么吞吐量就是99%)
注意:
停顿时间越短越就越适合需要与用户交互的程序,良好的响应速度能够提升用户体验;
而高吞吐量则可以高效的利用CPU时间,尽快的完成程序的计算任务,主要是和在后台运行不需要太多交互的任务
Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,分别控制:
最大垃圾收集停顿时间-XX:MaxGCPauseMillis
吞吐量大小-XX:GCTimeRatio
与Parallel Scavenge收集器有关的还有一个参数:-XX:+UseAdaptiveSizePolicy(有了这个参数之后,就不要手工指定年轻代、Eden、Suvisor区的比例,晋升老年代的对象年龄等,因为**虚拟机会根据系统运行情况进行自适应调节**)
使用方式:
-XX:+UseParallelGC
-XX:+UseParallelGC -XX:+UseAdaptiveSizePolicy
使用方式:
-XX:+UseSerialGC
Parallel Old是parallel Scavenge的多线程版本,使用的是标记-整理算法;
Parallel Old 收集器的工作过程:
使用方式:
-XX:+UseParallelOldGC
CMS收集器是以获取最短垃圾收集停顿时间为目标的收集器;
目前很大一部分的java应用集中在互联网的B/S系统服务器上,这类应用尤其注重服务器的响应速度,希望系统停顿时间最短,给用户带来良好的体验;
CMS收集器就非常符合这类应用的需求;
CMS收集器使用的算法是标记-清除算法实现的,整个过程分4个步骤:
1、 初始标记 2、 并发标记 3、 重新标记 4、 并发清除
其中初始标记和重新标记都需要stopTheWorld
1:初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快; 2:并发标记阶段就是进行GC Roots Tracing(根搜索算法)的过程; 3:重新标记阶段是为了修改并发标记期间因用户程序继续运行而导致产生变动的那一部分对象的标记记录,**这个阶段停顿的时长要稍微比初始标记停顿的时间稍微长一点,但是远比并发标记的时间小很多**; 4:整个过程耗时最长的是**并发标记和并发清除**,但是他们都是可以与用户线程一起工作的,所以CMS收集器是停顿时间最短的垃圾收集器
CMS垃圾收集器缺点:
1:CMS收集器对CPU资源特别的敏感;
CMS在并发阶段,虽然不会导致用户线程停顿,但是会因为占用一部分线程而导致应用程序变慢,总吞吐量变低;
2:使用标记-清除算法,会产生内存碎片(配合-XX:+UseCMSCompactAtFullCollection使用)[意思是:此参数设置在垃圾收集器后是否需要一次内存碎片整理过程,仅在CMS收集器时有效]
原使用方式:
-XX:+UseConcMarkSweepGC
优质使用方式:
-XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection
G1收集器是最新的垃圾收集器,能效最好的收集器;
考虑到之前的垃圾收集器的优缺点,希望能够有这样一款收集器能够做到:
• 可以像CMS收集器一样,GC操作与应用的线程一起并发执行
• 紧凑的空闲内存区间且没有很长的GC停顿时间.
• 需要可预测的GC暂停耗时.
• 不想牺牲太多吞吐量性能.
• 启动后不需要请求更大的Java堆.
G1的特点
内存划分
之前介绍的分代收集器将整个堆分为年轻代、老年代、永久代,每个空间都是确定的;
G1是将整个堆空间划分成大小想等的小块(每一块成为region),每一块的内存是连续的。和分代收集算法一样,G1中每个快也会充当Eden、Suvisor、Old三种角色,但是他们不是固定的,这样使得内存的使用更加灵活;
G1收集器去掉了原有的分代分配方式,而是采用:
在G1中有个特殊的区域叫做:Humongous区域。如果一个对象的空间超过了分区容量的50%以上,G1就认为这是一个巨型对象;默认巨型对象是需要存储在老年代中的,但是如果这个巨型对象只是短期存在,那么会对垃圾收集器造成负面影响;为了解决这个问题,G1专门划分了一个区域(Humongous)用来存储大对象;
注意:如果一个H区装不下一个巨型对象,那么G1会寻找连续的H分区来存储。为了能找到连续的H区,有时候不得不启动Full GC。
G1提供了两种GC模式,Young GC和Mixed GC,两种都是Stop The World(STW)的
G1中的young GC
Young GC主要是对Eden区进行GC,它在Eden空间耗尽时会被触发。
在这种情况下,Eden空间的数据移动到Survivor空间中,如果Survivor空间不够,Eden空间的部分数据会直接晋升到年老代空间。
Survivor区的数据移动到新的Survivor区中,也有部分数据晋升到老年代空间中。最终Eden空间的数据为空,GC停止工作,应用线程继续执行
垃圾回收前:
垃圾回收后:
发生在年轻代的GC算法,一般对象(除了巨型对象)都是在eden region中分配内存,当所有eden region被耗尽无法申请内存时,就会触发一次young gc,这种触发机制和之前的young gc差不多,执行完一次young gc,活跃对象会被拷贝到survivor region或者晋升到old region中,空闲的region会被放入空闲列表中,等待下次被使用。
参数 含义 -XX:MaxGCPauseMillis 设置G1收集过程目标时间,默认值200ms -XX:G1NewSizePercent 新生代最小值,默认值5% -XX:G1MaxNewSizePercent 新生代最大值,默认值60%
G1中的Mixed GC
当越来越多的对象晋升到老年代old region时,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,即mixed gc,该算法并不是一个old gc,除了回收整个young region,还会回收一部分的old region,这里需要注意:是一部分老年代,而不是全部老年代。
Mixed GC触发阈值:
-XX:InitiatingHeapOccupancyPercent,当老年代大小占整个堆大小百分比达到该阈值时,会触发一次mixed gc
(一个Region的大小可以通过参数-XX:G1HeapRegionSize设定,取值范围从1M到32M,且是2的指数。如果不设定,那么G1会根据Heap大小自动决定)
Full GC
如果对象内存分配速度过快,mixed gc来不及回收,导致老年代被填满,就会触发一次full gc,G1的full gc算法就是单线程执行的serial old gc,会导致异常长时间的暂停时间,需要进行不断的调优,尽可能的避免full gc.
G1收集器中的一些参数