1.垃圾收集算法
现在的垃圾回收分带理论其实已经始于几十年前了,java的回收算法和垃圾回收器一直都在改进从未出现一款能适合所有场景的回收机制。所以我们进行垃圾回收的核心思想是 结合具体业务框架技术等客观条件合理选择垃圾回收器并分配资源。
现在这个机制:
一般将java 堆分为新生代和老年代 在新生代中的一般是我们java中朝生夕死的对象每次垃圾回收都会有大约99%的对象死去。现在一般采用复制算法。
老年代的对象存活比例较高。这里推荐大家先了解什么对象存入新生代,什么对象存入老年代。什么对象存入栈中。如何你把上面的整个理解了80%以上 那么就可以继续读下去了。对于老年代我们应该采用标记清除或者标记整理。应该根据自己数据特点选择是否需要整理。以及老年代做几次GC整理一次。
1.标记清除算法
主要分为两个阶段标记 和清除阶段 可以正着标记也可以反着标记 在标记完成后统一回收。但是存在两个问题:
1.效率问题。
2.空间内会产生大量内存碎片。(数组和某些大对象需要连续的空间存放。)
2.标记整理:
就是清除后统一进行整理,虽然这里的地址会被改变,但是利用计算机底层的一些屏障机制等
可以改变我们的引用地址。
针对老年代对象的存亡特征,1974年Edward Lueders提出了另外一种有针对性的“标记-整 理”(Mark-Compact)算法,其中的标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存, 标记-复制算法在对象存活率较高时就要进行较多的复制操作,效率将会降低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。 标记-清除算法与标记-整理算法的本质差异在于前者是一种非移动式的回收算法,而后者是移动式的。是否移动回收后的存活对象是一项优缺点并存的风险决策: 如果移动存活对象,尤其是在老年代这种每次回收都有大量对象存活区域,移动存活对象并更新 所有引用这些对象的地方将会是一种极为负重的操作,而且这种对象移动操作必须全程暂停用户应用程序才能进行[1],这就更加让使用者不得不小心翼翼地权衡其弊端了,像这样的停顿被最初的虚拟机设计者形象地描述为“Stop The World” |
但如果跟标记-清除算法那样完全不考虑移动和整理存活对象的话,弥散于堆中的存活对象导致的 空间碎片化问题就只能依赖更为复杂的内存分配器和内存访问器来解决。譬如通过“分区空闲分配链 表”来解决内存分配问题(计算机硬盘存储大文件就不要求物理连续的磁盘空间,能够在碎片化的硬盘
|
如果你看完周说的并理解了恭喜你,如果没看懂 上面的大致意思就是 标记整理 会浪费更多的资源。我们根据自己本身业务的硬件与程序数据的结构自己选择合适的,也可以用混用来和稀泥。CMS就是这样的。就JDK1.8吧来说如果你应用程序在3--16G内存空间的 那么CMS无疑是优异的。如果小于3G 那么采用更简单的垃圾收集器或许是不错的选择。
常用的垃圾收集器:
作者:公众号_Zack说码
链接:https://juejin.im/post/5bade237e51d450ea401fd71
来源:掘金
CSDN:https://blog.csdn.net/maoyeqiu/article/details/97316982
Serial,是单线程执行垃圾回收的。当需要执行垃圾回收时,程序会暂停一切手上的工作,然后单线程执行垃圾回收。
因为新生代的特点是对象存活率低,所以收集算法用的是复制算法,把新生代存活对象复制到老年代,复制的内容不多,性能较好。
单线程地好处就是减少上下文切换,减少系统资源的开销。但这种方式的缺点也很明显,在GC的过程中,会暂停程序的执行。若GC不是频繁发生,这或许是一个不错的选择,否则将会影响程序的执行性能。 对于新生代来说,区域比较小,停顿时间短,所以比较使用。
Serial Old收集器
老年代的收集器,与Serial一样是单线程,不同的是算法用的是标记-整理(Mark-Compact)。
因为老年代里面对象的存活率高,如果依旧是用复制算法,需要复制的内容较多,性能较差。并且在极端情况下,当存活为100%时,没有办法用复制算法。所以需要用Mark-Compact,以有效地避免这些问题。
新生代的收集器,同样用的是复制算法,也是并行多线程收集。与ParNew最大的不同,它关注的是垃圾回收的吞吐量。
这里的吞吐量指的是 总时间与垃圾回收时间的比例。这个比例越高,证明垃圾回收占整个程序运行的比例越小。
Parallel Scavenge收集器提供两个参数控制垃圾回收的执行:
因为Parallel Scavenge收集器关注的是吞吐量,所以当设置好以上参数的时候,同时不想设置各个区域大小(新生代,老年代等)。可以开启**-XX:UseAdaptiveSizePolicy**参数,让JVM监控收集的性能,动态调整这些区域大小参数。
老年代的收集器,是Parallel Scavenge老年代的版本。其中的算法替换成Mark-Compact。
适用场合:在注重CPU资源和吞吐量的情况下Parallel Scavenge 和 Parallel Old会是不错的选择。但是parNEw和CMS的组合STW感觉更轻微用户体现会更好。
ParNew同样用于新生代,是Serial的多线程版本,并且在参数、算法(同样是复制算法)上也完全和Serial相同。
Par是Parallel的缩写,但它的并行仅仅指的是收集多线程并行,并不是收集和原程序可以并行进行。ParNew也是需要暂停程序一切的工作,然后多线程执行垃圾回收。
因为是多线程执行,所以在多CPU下,ParNew效果通常会比Serial好。但如果是单CPU则会因为线程的切换,性能反而更差。
是一款以获取最短回收时间为目的的收集器,非常注重用户体验
整个过程分为四步:
主要优点:并发收集低停顿。
缺点:
对CPU资源敏感(会和服务抢资源)
会产生浮动垃圾 需要在下次GC时清理
Mark Sweep算法会导致内存碎片比较多(可以通过:-XXUseCMSCompactAtFullCollection参数 让JVM在清理垃圾后进行整理)
执行中的不确定行 如何并发收集垃圾时内存不够了 会采用Serial Old收集器 并STW 专心收集垃圾 这个过程很漫长 一定要避免
CMS的相关核心参数
1. -XXx:+UseConcMarkSweepGC:启用cms
2. -XX:ConcGCThreads:并发的GC线程数
3. -XX:+UseCMSCompactAtFullCollection: FullGC之 后做压缩整理(减少碎片)
4. -XX:CMSFullGCsBeforeCompaction:多少次FullGC之后压缩- -次。默认是0,代表每次FullGC后都会压缩一
5. -XX:CMSInitiatingOccupancyFraction:当老年代使用达到该比例时会触发FulIGC (默认是92,这是百分比)6. -XXx:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设 定的值),如果不指定,JVM仅在第- -次使用设定值, 后续则会自动调整
7. -XX:+ CMSScavengeBeforeRemark:在CMS GC前启动- -次minor gc,目的在于减少老年代对年轻代的引 用,降低CMS GC的标记阶段时的开销,一般CMS的GC耗时 80%都在标记阶段
8. -XX:+ CMSParallellnitialMarkEnabled:表示在初始标记的时候多线程执行,缩短STW
9. -XX:+ CMSParallelRemarkEnabled:在重新标记的时候多线程执行,缩短STW;
10.ParNew&CMS -XX:UseParNewGC -XXx:+UseConcMarkSweepGC
三色算法:
在并发标记的过程中,因为标记期间应用线程还在继续跑,对象间的引用可能发生变化,多标和漏标的情况就有可能发生。 这里我们引入“三色标记"来给大家解释下,把Gcroots可达性分析遍历对象过程中遇到的对象,按照“是否访问过”这个条件标记成以 下三种颜色:
●黑色:表示对象已经被垃圾收集器访问过,且这个对象的所有引用都已经扫描过。 黑色的对象代表已经扫描 过,它是安全存活的,如果有其他对象引用指向了黑色对象,无须重新扫描一 遍。黑色对象不可能直接 (不经过
灰色对象)指向某个白色对象。
灰色:表示对象已经被垃圾收集器访问过,但这个对象上至少存在一个引用还没有被扫描过。
白色:表示对象尚未被垃圾收集器访问过。显然在可达性分析刚刚开始的阶段,所有的对象都是白色的, 若 在分析结束的阶段,仍然是白色的对象,即代表不可达。
看懂了上图 我们来看周的:
读写屏障:
解决并发扫描时的对象消失问题,只需破坏这两个条件的任意一个即可。
两种解决方案:增量更新(Incremental Update)和原始快照(Snapshot At The BeginningSAT B)
增量更新就是当黑色对象插入新的指向白色对象的引用关系时,就讲这个新插入的引用记录下来等并发扫描结束后再讲这些记录的的引用关系中黑色的根重新扫描一次,即黑色对象一旦新插入了指向白色对象的引用之后,他就变成灰色对象了。
原始快照急速当黑色对象要删除指向白色对象的引用时,就将这个要删除的引用记录下来,在并发扫描结束之后再将这些记录的引用中的灰色对象为根重新扫描一次,这样就能扫描到白色对象并将白色对象直接标记为黑色对象。
无论是对引用关系的插入还是删除,虚拟机的记录操作都是通过写屏障实现的。
写屏障 推荐参考https://blog.csdn.net/qq_21383435/article/details/106311542