JVM GC杂记


垃圾回收算法与垃圾回收器
1.live time决定了新老生代的回收算法。

新生代使用copy collect,老生代使用MarkSeep 或 MarkCompact

新生代的对象活少死多,采用copy策略,copy对象少,需要的目标空间小。copy的成本小。

老生代的对象存活时间长,所以采用mark sweep/mark compatc的策略。如果使用copy的策略,由于活多死少,copy的对象多,需要目标空间大,copy的成本高。

2.1 copy collect:
理论上copy算法新生代的空间按照1:1划分,一半空间用来存放新建对象,另一半干净空间准备接收对象,这样的空间利用率只有50%,即时刻需要保证另一半的空间清空,从而做为目标空间接收copy来的对象。实践表明98%的新生代的对象都是朝生夕死,所以并不需要1:1的划分

2.1.1 Hotspot中的copy算法实现

现代商业JVM并不需要按1:1去划分新生代,而是将新生代空间划分为一块较大的Eden和二块较小的Survior区域(From Space和To Space),在Hotspot中,默认比例分别是8:1:1, 这样新生代每次可用的内存空间占总空间的80%+10%, 剩下的10%用来做为to space,即目标接收空间。注意From Space与To Space在每次Minor GC结束的时候,都会互换角色。To Space的特征:从From Space copy活对象之前,awlays free。


在可用的空间又分出Eden的目的是只用Eden存放刚创建的活对象。当Eden的空间不能容纳新建对象时,有可能触发Minor GC。 From Space用来存放还没有资格进入Old Generation的活对象。

复制时将Eden Space中的活跃对象和一块Survior Space中尚不够资格(又称为From Space,小于-XX:MaxTenuringThreshold(默认为 31 次)次 Minor GC)进入 Old Generation 的 活跃对象复制到另外一块Survior Space(又称为To Space)中。
对于From Space中经历过 -XX:MaxTenuringThreshold 次仍然存活的对象则复制到 Old Generation 中,大对象也直接复制 到 Old Generation,如 To Space 中已满的话,则将对象直接复制到 Old Generation 中(这点 非常值得注意,在实际的产品中要尽量避免对象直接到 Old Generation),可通过 -XX:SurvivorRatio 来调整 Survior Space 所占的大小,然后清除 Eden Space 和 From Space
清空的From Space下次Minor GC时将做为To Space使用。而To Space在GC完成后,将做为From Space使用。

2.1.2过程描述:
new一个实例都是先放到Eden里面,当Eden的空间放不下时,如果触发Minor GC, 会把活着的对象copy到S0, 然后Eden会清空。
GC之后的状态是S0不空,Eden,S1空。在GC结束时,二个S0,S1交换角色。

.....Eden再次放入新建对象,直到再一次Minor GC时

S0中对象的状态是旧死:
”吐旧“会有二个方向,一种方向是满足N轮(从young gen到old gen需要成熟)回收,可以放到Old Gen,一种方向是还不够N轮,不够成熟,需要继续呆在Survivor区域,但此时需要移到S1中。
S0中还有一种可能对象不再引用,变为死对象,这些死对象会在S0的活对象移动到S1中后,一并清空。
S1中的对象此时有三种状态:新旧死
新表示纳入Eden的活对象。
旧表示S0移动过来的”不够成熟“的对象。
死表示不在引用的死对象。

GC之后的状态是Eden与S0清空,S1不为空。在GC结束时,二个S0,S1交换角色。

.....Eden再次放入新建对象,直到再一次Minor GC时

S0:新旧死
S1:旧死

.....Eden再次放入新建对象,直到再一次Minor GC时

........

2.1.3示意图一:

示意图2:

示意图3:

2.1.4 Minor GC的触发:
对象在Eden Space完成内存分配
当Eden Space满了,再创建对象,会因为申请不到空间,触发Minor GC,进行New(Eden + S0 或 Eden S1) Generation进行垃圾回收
Minor GC时,Eden Space不能被回收的对象被放入到空的Survivor(S0或S1,Eden肯定会被清空),另一个Survivor里不能被GC回收的对象也会被放入这个Survivor,始终保证一个Survivor是空的
在Step3时,如果发现Survivor区满了,则这些对象被copy到old区,或者Survivor并没有满,但是有些对象已经足够Old,也被放入Old Space。
当Old Space被放满之后,进行Full GC

3.垃圾回收算法的具体实现就是SUN提供的垃圾回收器。新生代是Serial, ParNew,Parallel Scanage;老生代包括Serial Old, Parallel Old, CMS

5.回收器概述

不要小看Serial,在client的jvm GC中, <=2的cpu core,Serial都是合理的选择。

ParNew收集器就是Serial收集器的多线程版本,其它特点与Serial收集器完全一样。比如STW。Parallel Scanage看起来和ParNew没什么二样,只是它是一个侧重于高吞吐长延迟的收集器。(所以,它可以理解为parllal的一个特殊的收集器,很多图中它将与ParNew并列在一起,会让人困惑)可以确定的是所有的新生代使用的收集算法都是copy collect。

STW好比“妈妈在打扫房间的时候,你不能在地上扔纸屑。” 对于垃圾回收,STW是合乎情理的。" 所以,新生代的垃圾回收器都按STW设计的。

但是对于老生代,CMS的出现打破了垃圾回收这条看似合理的规则STW,CMS允许“妈妈在一边打扫房间,你一可以一边扔纸屑。",CMS的目的是因为线上应用需要GC保持低吞吐而使应用快速响应,即留给应用执行的时间更长。


由于CMS生于JDK1.5,专门为离线任务设计的GC高吞吐,应用响应慢的Parallel Scanage回收器生于JDK1.4中。所以CMS只能与新生代的Serial,ParNew一起使用,而与Parallel Scanage无法搭配。

Parallel Old在jdk1.6才出现,它的目的搭配新生代的Parallel Scanage使用。而在JDK1.5,如果在新生代使用了Parallel Scanage,老生代只有二种收集器Serial Old,CMS,而CMS又因为生不逢时,不能与Parallel Scanage推配,这样Parallel Scanage只能与Serial Old配合。因为老生代垃圾回收算法是serial而影响了新生代使用Parallel Scanage的整体效果。

6.通常公司的配置线上都是是ParNew+CMS,一些跑定时任务的离线服务器的配置是Parallel Scanage+Parallel old

CMS概述

CMS收集器设计的目的是应用服务器为了较低延迟或较快的用户响应而通过分配较短的时间给GC而实现。


注意到CMS也并不是完全不会暂停application的,在上图CMS的步骤中,有两个步骤需要STW,分别是:initial mark和remark(如图所示)。而其它步骤是可以和application“并发”执行。initial mark是由一个GC thread来执行,它的暂停时间相对比较短。而remark过程的暂停时间要比initial mark更长,且通常由多个thread执行。

再上一张Parallel Mark Compact与CMS的对比图:

FULL GC


说完垃圾回收器的历史缘由,接下来是对象分配,对象分配的过程中会引出FULL GC的概念:
几个场景: 这里假设OldGen的并发收集采用的是CMS收集器,并发收集启动的Threshold设置为默认的68%,禁用CMS自己动态计算收集时机的功能,只在达到Threshold设定值时才启动CMS(即:在jvm的启动参数中加入了-XX:+UseCMSInitiatingOccupancyOnly)

1、在CMS进行并发收集时,新创建的对象的大小大于Eden+一个survivor区域的大小(所以需要直接晋级OldGen)并且也大于OldGen预留的32%的空间,造成CMS收集的失败,于是触发一次Full GC。

2、CMS还没有进行并发收集,但是新创建的对象的大小大于Eden+一个survivor区域的大小并且也大于目前OldGen剩余空间的总大小,于是触发Full GC。

3、YoungGen在进行Minor GC时,发现现在YoungGen中存活的并且已经old enough的对象的大小的总和已经大于OldGen剩余空间的总大小,于是触发Full GC。

4.-Xms与Xmx用来设置JVM的最大使用内存(不包括perm generation),如果值不相同时,一旦超过-Xms指定做resizing, 会FULL GC, Perm generation的设置PermSize与MaxPermSize同理。


场景1也说明了以前容易混淆认为CMS就是FULL GC,大错特错,场景1中触发CMS的机制是达到68%的阀值。而FULL GC往往是JVM最后一着用来自救。FULL GC的特点就是整个heap从头扫到尾,如果-Xms与Xmx指定的heap size很大,可想而知FULL GC会有多慢,而FULL GC除了慢的另一个特点就是STW.

吞吐率与延迟
理解了它,GC就理解了一大步
 
上图中第一个箭头连线表示高吞吐,红色箭头较长表明GC吞吐高。GC占用的时间长,通常Serial是这样的模式。
第二个箭头连线表示低吞吐,红色箭头短,表明GC的时间短。绿色箭头多且较长表示应用执行的时间长。CMS属于这样的模式。
 
 
GC涉及的其它知识点:

1.为什么要避免FULL GC?

因为FULL GC会把整个JVM的分配的heap扫描一遍。而扫描一遍的时间会很长。FULL GC也会引起STW,导致应用不可用。

2.CMS的C表示并发,它与Parallel有什么区别?
首先二者针对的客体不同。并发是在应用程序线程执行期间,一直存在一个垃圾回收线程。
并行相对于Serial在STW阶段的单个回收线程而言,Parallel在STW阶段,多核运行多个垃圾回收线程。(所以,也就理解了为什么单核的服务器只能使用Serial的回收策略。)

3.比较
 
 
3.1Serial与Parallel
共同点都会STW,但Parallel的STW的时间变短,回收效率更高。
 
3.2 STW与CMS是一个对立面。CMS大大减少了STW的时间。
3.3 FULL表示满了才做gc,而increment gc表示达到一定容量就gc。

4.为什么会STW,因为GC与应用程序内容分配会起冲突
 
5.java的对象并非只有引用与不可引用二种。
java的引用类型有四种。google colleciton的MapMaker使用了Weak Reference。(待补充)

6.JVM理解难在术语多而交叉,比如Minor GC, Major GC, FullGC.

There is the minor collection which is just the Eden or the Eden and survivor spaces. A full collection which is everything, and a concurrent collection which is only the old generation. There is no major collection as such. The term full collection is more often used and clearer as to its meaning.

所以,Full GC就是Major GC,但Full GC比Major GC更清楚。Major GC是相对于Minor GC的一个称呼。

6.1 Minor GC到FULL GC (http://stackoverflow.com/questions/13683866/garbage-collection-issue-in-young-generation/13764935#13764935)

”If it is not possible to do / complete a minor collection, then a major / full collection is performed. This is typically done using a mark-sweep-compact algorithm rather than copying algorithm ... which is one reason why full collection is expensive.But ultimately (if you keep filling the heap) a full collection will not be able to reclaim enough space to continue and an OOME will be thrown. (Or if you are using -XX:+UseGCOverheadLimit, the OOME will be thrown when the percentage time spent in GC exceeds a designated threshold.)“

“Young generation has 3 segments Eden Space, Survivor1 and Survivor2. These are just logical divisions of Young Generation. So objects get copied from Eden Space to Survivor1 and then to Survivor2.So minor collection in general means that collection happens in Young generation. And if Young generation is full then object gets copied to Old generation.Again, collection is minor or major depends on multiple factors one of them is space availability in young generation. So if there is enough space in Young generation for object allocation then it will be minor collection. But if there is not enough free space in YG then the same collection can turn into major.”

 
 
 参考:
http://blog.ragozin.info/2011/09/hotspot-jvm-garbage-collection-options.html
http://codegotbugs.blogspot.com/2012/06/java-garbage-collection.html
 
 
 
 
 
 
原文地址:https://www.cnblogs.com/highriver/p/3016992.html