GC学习笔记
这是我公司同事的GC学习笔记,写得蛮详细的,由浅入深,循序渐进,让人一看就懂,特转到这里。
一、
1、垃圾回收器的特性
2、对垃圾回收器的选择
2.1
2.2并发VS.stop-the-world
2.3
二、GC性能指标
三、分代回收
四、
五、
六、
七、
八、
九、启动参数学习示例
1.1垃圾回收器的特性
该回收的对象一定要回收,不该回收的对象一定不能回收
一定要有效,并且要快!尽可能少的暂停应用的运行
需要在时间,空间,回收频率这三个要素中平衡
内存碎片的问题(一种解决内存碎片的方法,就是压缩)
可扩展性和可伸缩性(内存的分配和回收,不应该成为跑在多核多线程应用上的瓶颈)
对垃圾回收器的选择
1.2
连续垃圾回收器,即使在多核的应用中,在回收时,也只有一个核被利用。
但并行
并行
1.3并发VS.stop-the-world
当使用
而并发是指
一般的说,并发
stoptheworld
相应的,并发
所以,并发
1.4
在GC确定内存中哪些是有用的对象,哪些是可回收的对象之后,他就可以压缩内存,把拥有的对象放到一起,并把剩下的内存进行清理。
在压缩之后,分配对象就会快很多,并且内存指针可以很快的指向下一个要分配的内存地址。
一个不压缩的GC,就原地把不被引用的对象回收,他并没有对内存进行压缩。好处就是回收的速度变快了;缺点呢,就是产生了碎片。
一般来说,在有碎片的内存上分配一个对象的代价要远远大于在没有碎片的内存上分配。
另外的选择是使用一个复制算法的GC,他是把所有被引用的对象复制到另外一个内存区域中。
使用复制GC的好处就是,原来的内存区域,就可以被毫无顾忌的清空了。但缺点也很明显,需要更多的内存,以及额外的时间来复制。
2.GC性能指标
几个评估GC性能的指标
吞吐量应用花在非GC上的时间百分比
GC
暂停时间应用花在GCstop-the-world的时间
GC频率顾名思义
Footprint一些资源大小的测量,比如堆的大小
反应速度从一个对象变成垃圾道这个对象被回收的时间
一个交互式的应用要求暂停时间越少越好,然而,一个非交互性的应用,当然是希望GC负荷越低越好。
一个实时系统对暂停时间和GC负荷的要求,都是越低越好。
一个嵌入式系统当然希望Footprint越小越好。
3.分代回收
3.1什么是分代
当使用分代回收技术,内存会被分为几个代(generation)。也就是说,按照对象存活的年龄,把对象放到不同的代中。
使用最广泛的代,,应属年轻代和年老代了。
根据各种GC算法的特征,可以相应的被应用到不同的代中。
研究发现:
大部分的对象在分配后不久,就不被引用了。也就是,他们在很早就挂了。
只有很少的对象熬过来了。
年轻代的GC相当的频繁,高效率并且快。因为年轻代通常比较小,并且很多对象都是不被引用的。
如果年轻代的对象熬过来了,那么就晋级到年老代中了。如图:
通常年老代要比年轻代大,而且增长也比较慢。所以GC在年老代发生的频率非常低,不过一旦发生,就会占据较长的时间。
3.2总结
年轻代通常使用时间占优的
年老代通常使用善于处理大空间的
J2SE5.0update6
4.1HotSpot上的分代
分成三部分:年轻代、年老代、永久代
很多的对象一开始是分配在年轻代的,这些对象在熬过了一定次数的younggc之后,就进入了年老代。同时,一些比较大的对象,一开始就可能被直接分配到年老代中(因为年轻代比较小嘛)。
4.2年轻代
年轻代也进行划分,划分成:一个
大部分的对象被直接分配到年轻代的eden区(之前已经提到了是,很大的对象会被直接分配到年老代中),
survivor
同一时刻,两个
4.3GC类型
年轻代的
也就是说,所有的代都会进行GC。
一般的,首先是进行年轻代的
有时候,如果年老区本身就已经很满了,满到无法放下从
4.4快速分配内存
多线程进行对象建立的时候,在为对象分配内存的时候,就应该保证线程安全,为此,就应该进入全局锁。但全局锁是非常消耗性能的。
为此,
每个线程只使用自己的
为了减少
5.1串行GC
串行
5.1.1young的串行GC
如下图:
当发生
如果有些对象在Tosurvivor放不下,则直接升级到年老区。
当YGC完成后,内存情况如下图:
5.1.2old区的串行GC
年老区和永久区使用的是Mark-Sweep-Compact的算法。
mark阶段是对有引用的对象进行标识
sweep是对垃圾进行清理
compact对把活着的对象进行迁移,解决内存碎片的问题
如下图:
5.2何时使用串行收集器
串行GC适用于对暂停时间要求不严,在客户端下使用。
5.3串行收集器的选择
在
也可以显示进行选择,在
-ParallelGC
6.1并行GC
现在已经有很多java应用跑在多核的机器上了。
并行的
6.2YGC的并行GC
YGC
只不过是不再串行,而是充分利用多个
如下图,串行
6.3年老区的并行GC
也是和串行
6.4何时使用并行GC
对跑在多核的机器上,并且对暂停时间要求不严格的应用。因为频率较低,但是暂停时间较长的FullGC还是会发生的。
6.5选择并行GC
在
或者可以显式选择并行
7.1ParallelCompactingGC
parallelCompactingGC
parallelcompactingGC
7.2YGC的并行压缩GC
与并行
7.3年老区的并行压缩GC
他将把年老区和永久区从逻辑上划分成等大的区域。
分为三个阶段:
标记阶段,使用多线程对存在引用的对象进行并行标记。
分析阶段,
压缩阶段,多各个需要压缩的区域进行并行压缩。
7.4什么时候使用并行压缩GC
同样的,适合在多核的机器上;并且此
可以使用参数-XX:ParallelGCThreads=n来指定并行的线程数。
7.5开启并行压缩GC
使用参数-XX:+UseParallelOldGC
-CMSGC
8.1ConcurrentmarksweepGC
很多应用对响应时间的要求要大于吞吐量。
YGC
因此,
8.2CMS的YGC
与并行
8.3CMS的FGC
CMS
CMS
在并发标记时候,会和应用程序一起运行。
因为并发标记是和程序一起运行的,所以在并发标记结束的时候,不能保证所有被引用的对象都被标记,
为了解决这个问题,
在这个过程中,GC会重新对在并发标阶段时候有修改的对象做标记。
因为remark的暂停要大于最初标记,所以在这时候,需要使用多线程来并行标记。
在上述动作完成之后,就可以保证所有被引用的对象都被标记了。
因此,并发清理阶段就可以并发的收集垃圾了。
下图是
因为要增加很多额外的动作,比如对被引用的对象重新标记,增加了
CMS
没有压缩,对于GC的过程,是节约了时间。但因此产生了内存碎片,所以对于新对象在年老区的分配,就产生了速度上的影响,
当然,也就包括了对YGC时间的影响了。
CMS的另一个缺点,就是他需要的堆比较大,因为在并发标记的时候和并发清除的时候,应用程序很有可能在不断产生新的对象,而垃圾又还没有被删除。
另外,在最初标记之后的并发标记时,原先被引用的对象,有可能变成垃圾。但在这一次的GC中,这是没有被删除的。这种垃圾叫做:漂流垃圾。
最后,由于没有进行压缩,由此而带来了内存碎片。
为了解决这个问题,
与其他的
如果在
为了防止这种情况,会根据上一次GC的统计来确定启动时间。
或者是当年老区超过初始容量的话,CMSGC就会启动。
初始容量的设置可以在
n
总之,
8.4额外模式
为了防止在并发标记的时候,
收集器会想并发标记分解成很小的时间串任务,在YGC之间来执行。
这个功能对于机器的CPU个数少,但又想降低暂停时间的应用来说,非常有用。
8.5何时使用CMS
当
8.6选择CMS
选择
开启额外模式:增加参数-XX:+CMSIncreamentalMode
9.结合线上启动参数学习
线上的启动参数
即使爬到最高的山上,一次也只能脚踏实地地迈一步。