jvm垃圾回收器
一、垃圾回收器分类
1、次收集器(scavengeGC或者minorGC)
针对新生代young的收集器,触发的频率非常频繁,回收的效率上也相对较高。一般当Eden区域内存空间分配不足的时候,会触发minorGC。
程序运行过程中,当new新对象并且需要在Eden区申请分配内存失败时,会触发一次minorGC,对Eden区进行一次清理,清除非存活的对象,并且将存活的对象拷贝的survivor区域中,整理From和To区域的对象。
此收集动作发生在年轻代的Eden区,不会影响到年老代,由于大部分的new对象都是从Eden区开始的,同时,Eden也不会分配的过大。所以GC的频率上会很高。在算法选择上,优先考虑速度快、效率高的,尽快的回收Eden。
2、全收集器(FullGC)
针对老年代old的收集器,频率相对较低。执行效率上,比minorGC慢。一般,出现FullGC都伴随着至少一次的minorGC(老年代中的对象,大部分都是从年轻代过来的幸存对象)。当在老年代中申请分配空间失败时候,触发
一次FullGC。
当老年代堆空间满了的时候,会触发FullGC。
一般,显式的调用system.gc(),通知jvm去触发一次FullGC,至于什么时候执行,不知道。
二、垃圾回收算法及常用匹配
1、常见的垃圾回收算法
1.1、引用计数
若对象有一个引用,则计数加1。删除一个引用时,计数减1。当垃圾回收的时候,对那些计数为0的对象进行清理。此算法缺陷是无法处理对象的循环引用问题。
2.2、复制
将内存空间划分为两块相等的区域,每次只使用其中的一块,保证一块的干净。在进行垃圾回收的时候,遍历扫描当前使用的区域,将正在使用的对象复制到另外一块去。此算法只处理正在使用的对象,
且复制到另一块中也能进行相应的内存整理,保证内存的连续性。此算法的缺陷是内存占用较大,需要两倍的内存空间。
2.3、标记-清除(mark-sweep)
算法分两步:标记过程中,从根节点出发,开始标记可达的对象,标记出所有的被引用的对象。清除过程中,扫描遍历堆空间,将那些没有标记的对象清理掉。
此算法缺陷是需要暂停整个应用,且会产生大量的内存碎片(保证不了内存的连续性)。
2.4、标记-整理(mark-compact)
算法结合了标记-清除和复制算法的优点。分两步执行:标记过程中,从根节点出发,开始标记可达的对象,标记出所有的被引用的对象。整理阶段,扫描遍历堆空间,将那些没有标记的对象清理掉,
并且将存活的对象复制压缩到堆中的一块,顺序存放,保证内存的连续性。
2、分代垃圾回收
2.1、年轻代的收集器
2.1.1、串行收集器(Serial)
Serial收集器是HotSpot运行在Client模式下的默认设置。在收集器工作的时候,只用一条线程去处理完成GC操作,且在GC过程中,会暂停其他所有的工作线程(Stop-the-World),只执行GC线程。
可以使用参数-XX:+UseSerialGC设置打开。
单线程收集,简单而且高效。
2.2.2、并行收集器(ParNew)
串行收集器Serial的多线程版本,除了GC工作时是多线程处理,其他特性均一致。老年代启用CMS收集器(-XX: +UseConcMarkSweepGC)时候,年轻代的默认选项。
在单CPU的的服务器中,由于多线程存在线程切换开销,所以效率上比不上Serial收集器。但是随着CPU核心数的增加,收集的效率上也会提升。可使用用-XX:ParallelGCThreads=<N>控制收集器工作线程数。
2.2.3、Parallel Scavenge 收集器
与ParNew类似,使用复制算法,也是并行多线程收集器。
其他收集器注重时间效率上,Parallel Scavenge关注点在于系统吞吐量,达到一个可控的吞吐量。所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,
即系统吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。
GC时间效率越好,就越适用于有大量用户交互的程序设计中,快速的响应能提高用户的体验。而高吞吐量适用于不关注用户交互的系统中,如后台的数据运算。
主要的参数有三个:-XX:MaxGCPauseMillis控制最大垃圾回收停顿时间,毫秒数。-XX:GCTimeRatio垃圾收集占总时间比率,直接的吞吐量大小,通常整数,范围0~100。
-XX:+UseAdaptiveSizePolicy启用自适应开关,VM会根据当前系统运行情况收集性能监控信息,动态的调整年轻代大小,Eden和Survivor区域比例,以及对象进入老年代的阈值等。
2.2、老年代的收集器
2.2.1、串行收集器(Serial Old)
Serial 的老年代版本,使用标记-整理算法。
2.2.2、Parallel Old收集器
Parallel Scavenge的老年代版本,多线程收集,使用标记-整理算法。同Parallel Scavenge 一样,注重于系统吞吐量。
2.2.3、CMS收集器(Concurrent Mark Sweep)
理论上的并行收集器,也就是说理论上GC线程和用户线程可以并行。多线程分阶段回收,某阶段还是会有Stop-the-world,只是会尽可能减少停顿时间。
使用标记-清除算法,主要分为四步:
(1)初始标记:标记从根节点GC Roots能直达的对象,会STW。在Java中,可作为GC Roots对象的包括如下几种:
①. 虚拟机栈(栈桢中的本地变量表)中的引用的对象 ;
②. 方法区中的类静态属性引用的对象 ;
③. 方法区中的常量引用的对象 ;
④. 本地方法栈中JNI的引用的对象;
(2)并发标记:与用户线程同时运行,从初始标记的对象中,继续标记可达的对象
(3)重新标记:会STW。扫描整个堆空间,包括young和old。为什么要扫描young?如果老年代的对象被新生代引用的话,也会被标记为存活对象。可使用参数-XX:+CMSScavengeBeforeRemark
对新生代执行一次回收,然后将存活对象移到幸存区或者老年代,再进行重新标记的时候,幸存者较少,能提高效率。
(4)并发清理:与用户线程同时运行,清除未标记对象。由于是跟用户线程并发运行,伴随着程序的运行,这段时间可能也会有大量的垃圾产生,但是这部分垃圾在标记之后,所以在
本次的垃圾回收中,不会去处理。留待下一次的GC处理。这部分产生的垃圾,就是“浮动垃圾”。
CMS 默认启动的回收线程数=(CPU 数目+3)4,参数-XX:ParallelGCThreads可设置线程数。
CMS是一种预处理垃圾回收器,它不能等到old内存用尽时回收,需要在内存用尽前,完成回收操作,否则会导致并发回收失败;所以cms垃圾回收器开始执行回收操作,有一个触发阈值,默认是老年代或永久带达到92%;
CMS会产生内存碎片,参数-XX:+UseCMSCompactAtFullCollection 搭配使用-XX:CMSFullGCsBeforeCompaction=N。
-XX:+UseCMSCompactAtFullCollection表示Full GC后执行一次碎片整理,默认值为true
-XX:CMSFullGCsBeforeCompaction=N表示,多少次Full GC不执行碎片整理后,下一次必会执行一次碎片整理,默认值是0。
2.3、分区收集 - G1收集器
3、收集器的常用匹配