java虚拟机(六)--垃圾收集器和内存分配策略

  目前没有完美的收集器,不一样的厂商、版本的虚拟机提供的垃圾收集器会有很大的差异,用户根据本身应用特色和要求组合出各个年代所使用html

的收集器。基于jdk1.7Update14以后的虚拟机。java

官方文档:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/toc.htmlweb

HotSpot的垃圾收集器

上图是做用于新生代和老年代的收集器,连线表明能够搭配使用。算法

分类:

一、串行收集器:Serial、Serial Old数组

  GC的时候须要暂停其余线程的工做,启动一个GC线程进行垃圾回收安全

二、并行收集器:Parallel Scavenge、Parallel Old,吞吐量优先服务器

  指的是多条垃圾收集线程并行工做,可是用户线程仍是被暂停了。适合科学计算、后台处理等弱交互场景多线程

三、并发收集器:CMS、G1,停顿时间优先并发

  指的是垃圾收集线程和用户线程同时执行(不必定是并行,能够是交替运行),不会出现Stop-the-world。适合对响应时间有要求的场景,如oracle

web应用。

停顿时间:

  垃圾收集器在GC的时候中断应用程序的时间。-XX:MaxGCPauseMills

吞吐量:

  这里的吞吐量为GC时间和应用运行的时间的比值。-XX:GCTimeRatio=<n>,GC时间占比为1/(1+n)

新生代采用复制算法,而老年代采用标记-整理、标记-清除算法

一、Serial:串行

  采用复制算法的单线程收集器,进行GC的时候须要暂停其余线程的工做(被称为Stop The World),直到它收集结束。Serial依然是运行在

Client模式下的默认新生代收集器,由于简单、高效。用户桌应用场景中,分配给虚拟机管理的内存通常来讲不会很大,收集几十兆甚至一两百

兆的新生代停顿时间在几十毫秒最多一百毫秒,只要不是频繁发生,这点停顿是彻底能够接受的。单个CPU环境下比较适合,Web应用不可能使用。

  -XX:+UseSerialGC  -XX:+UseSerialOldGC

二、ParNew

  ParNew收集器其实就是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集外,其他行为和Serial收集器彻底同样,一些参数和回收策

略都相同。它是Server模式下的首选的新生代收集器,因为除了Serial收集器外,目前只有它能与CMS收集器配合工做。它默认开启的收集线程数与

CPU数量相同,在CPU数量很是多的状况下,可使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数。

三、Parallel Scavenge

  新生代,用复制算法,并行的多线程收集器,CMS等收集器的关注点是尽量缩短垃圾收集时用户线程的停顿时间,而Parallel收集器的目标则

是打到一个可控制的吞吐量。另外,Parallel收集器是虚拟机运行在Server模式下的默认垃圾收集器。

  停顿时间短适合须要与用户交互的程序,良好的响应速度能提高用户体验;高吞吐量则能够高效率利用CPU时间,尽快完成运算任务,主要适合

后台运算而不须要太多交互的任务。

经过如下命令查看当前运行的jvm启用的是否为Parallel垃圾回收器

[root@iZuf6fkfhthmdm1nwdg5isZ bin]# jinfo -flag UseParallelGC 24642
-XX:+UseParallelGC
[root@iZuf6fkfhthmdm1nwdg5isZ bin]# jinfo -flag UseParallelOldGC 24642
-XX:+UseParallelOldGC

Server模式:

  jvm自动判断机制,根据当前系统CPU和内存等状况,决定启动Server模式,仍是Client模式。通常内存>2G,就是Server模式了

  -XX:+UseParallelGC  -XX:+UseParallelOldGC

-XX:ParallelGCThreads=<n>多少个线程,CPU>8 N=5/8,CPU<8 N=CPU

四、Serial Old

  Serial收集器的老年代版本,一样是一个单线程收集器,使用“标记-整理算法”,这个收集器的主要意义也是在于给Client模式下的虚拟机使用。

五、Parallel Old

  Parallel收集器的老年代版本,使用多线程和“标记-整理”算法。这个收集器在JDK 1.6以后的出现,“吞吐量优先收集器”终于有了比较

名副其实的应用组合,在注重吞吐量以及CPU资源敏感的场合,均可以优先考虑Parallel收集器+Parallel Old收集器的组合。

六、CMS:Concurrent Mark Sweep 很是适用B/S系统

  以获取最短GC停顿时间为目标的老年代收集器。目前很大一部分Java应用集中在互联网站或者B/S系统的服务端上,注重服务的响应速度,但愿

统停顿时间最短,以给用户带来较好的体验,CMS收集器就非常符合这类应用的需求。CMS收集器基于“标记-清除”算法实现的。

  -XX:+UserConcMarkSweepGC  -XX:+UseParNewGC

咱们在Catalina.xml中经过JAVA_OPTS设置:-XX:+UserConcMarkSweepGC,而后查看

[root@iZuf6fkfhthmdm1nwdg5isZ bin]# jinfo -flag UseConcMarkSweepGC 29720
-XX:+UseConcMarkSweepGC
[root@iZuf6fkfhthmdm1nwdg5isZ bin]# jinfo -flag UseParNewGC 29720
-XX:+UseParNewGC

运行过程分为四步:

  初始标记:会有GC停顿,只是标记GC Root,速度很快,会发生Stop-The-Word

  并发标记:进行GC Roots Tracing的过程

  并发预清理

  从新标记:会有GC停顿,修正并发标记期间标记产生变更的那部分对象的标记,会发生Stop-The-Word

  并发清除:

  并发重置:

  并发标记、并发清除是耗时最长的可是能够与用户线程同时工做

缺点:

  一、CPU敏感,占用CPU资源,应用程序吞吐量降低

  二、并发清理阶段用户线程还在运行,就会产生浮动垃圾,因为标记过了,只能下次GC才能清理

  三、采用"标记-清除"算法就会产生大量空间碎片,大对象分配很麻烦,可能提早Full GC

相关参数:

-XX:ConcGCThreads:与应用程序并发执行的GC线程数,不是Stop-the-world的线程数
-XX:+UseCMSCompactAtFullCollection:Full GC以后进行压缩,由于会产生碎片
-XX:CMSFullGCsBeforeCompaction:发生多少次Full GC压缩一次
-XX:CMSInitiatingOccupancyFraction:Old区内存使用达到多少,才会触发Full GC,默认92%
-XX:+UseCMSInitiatingOccupancyOnly:是否能够冬天调整
-XX:CMScavengeBeforeRemark:Full GC以前先作Young GC,通常开启
-XX:+CMSClassUnloadingEnabled:启用回收Perm区

七、G1(Garbage-First):兼顾吞吐量和停顿时间的GC实现,是JDK9之后的默认GC选项,新生代和老生代的收集器

  JDK 7 Update 4后开始进入商用。使用G1收集器时,Java堆的内存布局就与其余收集器有很大差异,它将整个Java堆分为多个大小相等的独立

区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代再也不是物理隔离的了,它们都是一部分Region的集合。G1收集器跟踪各

个Region里面的垃圾堆积的价值大小,在后台维护一个优先列表,每次根据容许的收集时间,优先回收价值最大的Region(这也是Garbage-First

名称的由来)。这种使用Region划份内存空间以及有优先级的区域回收方式,保证了G1收集器在有限的时间内能够获取尽量高的收集效率。

描述:

Region:

  上图为G1收集器的样式,每个小块就是一个Region,一部分Region组成Young区,一部分组成Old区,Young和Old只是逻辑的分布,其中标明

H的Region是专门用来保存大对象的,超过Region大小的一半,就会把对象存放到H Region中。

SATB:

  Snapshot-At-The-Begining,经过GC-Root获得,GC开始时存活的快照。

RSet:

  记录其余Region中对象引用自身Region中对象的关系,属于points-into结构(谁引用了个人对象)。

Young GC:

  一、新对象进入Eden区

  二、存活对象拷贝到Survivor区

  三、存活时间到达年龄阀值,晋升到Old区

MixedGC:

  一、不是Full GC,而是回收全部的Young和部分Old

  二、这个过程会有global concurrent marking

global concurrent marking:

  一、初始标记:会有GC停顿,只是标记GC Root,速度很快,会发生Stop-The-Word

  二、并发标记:标记存活的Region

  三、标记存活的对象

  四、从新标记:会有GC停顿,修正并发标记期间标记产生变更的那部分对象的标记,会发生Stop-The-Word

  五、并发清除:部分发生Stop-The-Word

Mixed GC发生的时机:

InitiatingHeapOccupancyPercent:

  堆占有率达到这个数值则触发global concurrent marking,默认45%

G1HeapWastePercent:

  在global concurrent marking结束以后,能够知道区中有多少空间要被回收,在每次YGC以后和再次发生Mixed GC以前,会检查垃圾占比是

否达到这个参数,若是达到了,下次才会发生Mixed GC。

G1MixedGCLiveThresholdPercent:

  Old区Region被回收的时候,存货对象的占比

G1MixedGCCountPercent:

  一次global concurrent marking以后,最多执行Mixed GC的次数

能够参考:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html#g1_gc_tuning

官方文档把全部的内容写得都很详细,英语很差的话就翻译过来看,尽可能原文看,还能学习英语

G1最佳实践:

一、年轻代大小:

  避免使用-Xmn、-XX:NewRatio等显式设置Young区大小,会覆盖暂停时间目标

二、暂停时间目标:

  暂停时间不要太过严苛,吞吐量目标是90%应用程序时间和10%的垃圾回收时间,太严苛会影响到吞吐量

须要切换到G1垃圾回收器的条件:

  一、堆的50%被存货对象占用

  二、对象分配和晋升的速度变化很大

  三、垃圾回收时间很长,超过1S

PS:-XX:+UseG1GC,jdk8以后,推荐使用G1垃圾回收器,吞吐量比较高,目标是在于大内存6G+,停顿时间小于0.5s

与其余GC收集器相比,G1的特色:

1).并发与并行

  充分利用多CPU、多核环境下的硬件优点,来缩短Stop-The-World停顿时间,G1能够经过并发让java程序继续执行

2).分带收集

  经过不一样的方式处理新建立的对象和存货了一段时间、熬过屡次GC的就对象来获取更好的收集效果

3).空间整合

  G1从总体来看基于“标记-整理”算法实现的收集器,从局部(两个Region之间)看基于“复制”算法实现

并且这两种算法不会产生内存空间碎片,收集后能提供规整的可用内存

4).可预测的停顿

  G1处理追求低停顿,还能创建可预测的停顿时间模型,让使用者明确指定在一个长度为Mms的时间片断内,消耗在GC的时间不超过Nms,几乎已经

是实时Java的垃圾收集器的特征了。

  Region之间对象引用以及其余收集器中的新声代和老年代之间的对象引用,虚拟机都是使用Rememberd Set来避免全堆扫描。

若是不计算维护Rememberd Set的操做,收集步骤以下:

  1).初始标记

  2).并发标记

  3).最终标记

  4).筛选回收

如何选择垃圾回收器?

一、优先调整堆的大小让服务器本身选择

二、若是内存小于100M,使用串行收集器

三、若是是单核,而且没有停顿时间的要求,选择串行或者服务器本身选择

四、若是容许停顿时间超过1S,选择并行或者服务器本身选择

五、若是响应时间很重要,而且不能超过1S,使用并发收集器

可参考官方文档:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/collectors.html

想要对这部份内容很了解,官方文档必定要多读几遍。。。

Full GC触发条件:

(1)调用System.gc时,系统建议执行Full GC,可是不是必然执行

(2)老年代空间不足

(3)方法区空间不足

(4)经过Minor GC后进入老年代的平均大小大于老年代的可用内存

(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对

象大小

内存分配与回收策略:

内存分配的规则取决于使用的是哪种垃圾回收收集器组合,还有虚拟机中与内存相关参数的配置

一、对象优先Eden space分配,若是Eden Space没有足够的空间,会发生一次Minor GC

二、大对象(很长的字符串、数组)直接分配获得老年代,经过参数-XX:PretenureSizeThreshold=3145728(3M),只要大于这个值,对象直接分

配到老年代,防止新生代进行大量内存复制

三、长时间存活的对象,会转到老年代,每一个对象有个年龄计数器,在survivor每次通过一次Minor GC,就会加一,默认15的时候,会分到老年代

参数为:-XX:MaxTenuringThreshold

四、第三条不是绝对的,VM会动态判断

  -XX:TargetSurvivorRatio:若是进行进行一次GC存活的对象超过这个参数,会计算相同年龄全部对象大小的综合大于Survivor空间的一半,

年龄大于或等于该年龄的对象就能够直接进入老年代,无须等到MaxTenuringThreshold要求的年龄

空间担保失败:

  发生Minor GC以前,虚拟机会检查老年代最大可用的连续空间是否大于新生代全部对象总空间,若是成立,能够确保Minor GC是安全的,else

检查HandlerPromotionFailure设置值是否容许担保失败。if许,继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小

,true,查实进行一次Minor GC,尽管有风险,若是小于,或者参数设置不容许,改成进行一次Full GC可是HandlerPromotionFailure在jdk 6

Update 24没有做用了,jdk规则变成只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,不然进行Full GC

相关文章
相关标签/搜索