GC 算法
来源:互联网 发布:孤岛危机3 优化 编辑:程序博客网 时间:2024/05/17 18:41
http://www.cnblogs.com/kubixuesheng/p/5208647.html
http://www.cnblogs.com/ityouknow/p/5614961.html
http://blog.csdn.net/linsongbin1/article/details/51686158 CMS回收器
heap分为了eden伊甸园,两个幸存代survivor,前三者也叫年轻代,其次是老年代old。
现代虚拟机的实现当中,垃圾搜集的算法主要有三种,分别是标记-清除算法、复制算法、标记-整理算法
标记-清除算法
现代Java的垃圾回收使用的基本的算法思想是标记-清除算法:
标记-清除算法是现代垃圾回收算法的思想基础。将垃圾回收分为两个阶段:标记阶段和清除阶段。
一种可行的实现是,在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象(从GC ROOT开始标记引用链——又叫可达性算法)。因此,未被标记的对象就是未被引用的垃圾对象。
而这种算法的缺点就是容易出现内存碎片。利用率不高。
标记-压缩算法
现代的Java虚拟机都是使用的分代回收的设计,比如在标记-清除算法的基础上做了一些优化的——标记-压缩算法,适合用于存活对象较多的场合,如老年代。
和标记-清除算法一样,标记-压缩算法也首先需要从根节点开始,对所有可达对象做一次标记。但之后,它并不简单的清理未标记的对象,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间。有效解决内存碎片问题。
还有一个算法,针对新生代的回收,叫复制算法
和标记-清除算法相比,复制算法是一种相对高效的回收方法,但是
不适用于存活对象较多的场合如老年代,使用在新生代,
原理是
将原有的内存空间分为两块,两块空间完全相同,每次只用一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。同样也没有内存
碎片产生。
复制算法的缺点是内存的浪费,因为每次只是使用了一般的空间, 而大多数存活对象都在老年代,故复制算法不用在老年代,老年代是Java堆的空间的担保地区。复制算法主要用在新生代。在垃圾回收的时候,大对象直接从新生代进入了老年代存放,大对象一般不使用复制算法,因为一是太大,复制效率低,二是过多的大对象,会使得小对象复制的时候无地方存放。还有被长期引用的对象也放在了老年代。
Java的垃圾回收机制使用的是分代的思想。
依据对象的存活周期进行分类,短命对象归为新生代,长命对象归为老年代。根据不同代的特点,选取合适的收集算法。少量对象存活(新生代,朝生夕死的特性),适合复制算法,大量对象存活(老年代,生命周期很长,甚至和应用程序存放时间一样),适合标记清理或者标记压缩算法。
以上一定注意:Java没有采用引用计数算法!
经过上述总结,想到所有的算法,需要能够识别一个垃圾对象,那么怎么才能识别呢?
因此需要给出一个可触及性的定义:
- 可触及的–从GC ROOT这个根节点对象,沿着引用的链条,可以触及到这个对象,该对象就叫可触及的,也就是之前说的可达性算法的思想。
- 可复活的–一旦所有引用被释放,就是可复活状态,因为在finalize()中可能复活该对象(finalize方法只会调用一次)。
- 不可触及的–在finalize()后,可能会进入不可触及状态,不可触及的对象不可能复活,就可以回收了。
- GC准备释放内存的时候,会先调用finalize()。而调用了这个方法不代表对象一定会被回收。因为GC和finalize() 都是靠不住的,只要JVM还没有快到耗尽内存的地步,它是不会浪费时间进行垃圾回收的。
最古老的收集器——串行收集器
最古老,最稳定,效率高,但是串行的最大问题就是停顿时间很长!因为串行收集器只使用一个线程去回收,可能会产生较长的停顿现象。我们可以使用参数-XX:+UseSerialGC,设置新生代、老年代使用串行回收,此时新生代使用复制算法,老年代使用标记-压缩算法(标记-压缩算法首先需要从根节点开始,对所有可达对象做一次标记。但之后,它并不简单的清理未标记的对象,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间。有效解决内存碎片问题)。
并行收集器(两种并行收集器)
- 一种是ParNew并行收集器。使用JVM参数设置XX:+UseParNewGC,设置之后,那么新生代就是并行回收,而老年代依然是串行回收,也就是并行回收器不会影响老年代,它是Serial收集器在新生代的并行版本,新生代并行依然使用复制算法,但是是多线程,需要多核支持,我们可以使用JVM参数: XX:ParallelGCThreads 去限制线程的数量。如图:
注意:新生代的多线程回收不一定快!看在多核还是单核,和具体环境。、
- 还有一种是Parallel收集器,它类似ParNew,但是更加关注JVM的吞吐量!同样是在新生代复制算法,老年代使用标记压缩算法,可以使用JVM参数XX:+UseParallelGC设置使用Parallel并行收集器+ 老年代串行,或者使用XX:+UseParallelOldGC,使用Parallel并行收集器+ 并行老年代。也就是说,Parallel收集器可以同时让新生代和老年代都并行收集。如图:
很重要的收集器-CMS(并发标记清除收集器Concurrent Mark Sweep)收集器
顾名思义,它在老年代使用的是标记清除算法,而不是标记压缩算法,也就是说CMS是老年代收集器(新生代使用ParNew),所谓并发标记清除就是CMS与用户线程一起执行。标记-清除算法与标记-压缩相比,并发阶段会降低吞吐量,使用参数-XX:+UseConcMarkSweepGC打开。
CMS运行过程比较复杂,着重实现了标记的过程,可分为:
- 初始标记,标记GC ROOT 根可以直接关联到的对象(会产生全局停顿),但是初始标记速度快。
- 并发标记(和用户线程一起),主要的标记过程,标记了系统的全部的对象(不论垃圾不垃圾)。
- 重新标记,由于并发标记时,用户线程依然运行(可能产生新的对象),因此在正式清理前,再做一次修正,会产生全局停顿。
- 并发清除(和用户线程一起),基于标记结果,直接清理对象。这也是为什么使用标记清除算法的原因,因为清理对象的时候用户线程还能执行!标记压缩算法的压缩过程涉及到内存块移动,这样会有冲突。
- 并发重置,为下一次GC做准备工作。
CMS的特点
尽可能降低了JVM的停顿时间,但是会影响系统整体吞吐量和性能,比如:
- 在用户线程运行过程中,分一半CPU去做GC,系统性能在GC阶段,反应速度就下降一半。
- 清理不彻底。因为在清理阶段,用户线程还在运行,会产生新的垃圾,无法清理。
- 因为和用户线程基本上是一起运行的,故不能在空间快满时再清理。
可以使用-XX:CMSInitiatingOccupancyFraction设置触发CMS GC的阈值,设置空间内存占用到多少时,去触发GC,如果不幸内存预留空间不够,就会引起concurrent mode failure。
可以使用-XX:+ UseCMSCompactAtFullCollection, Full GC后,进行一次整理,而整理过程是独占的,会引起停顿时间变长。
可以使用-XX:+CMSFullGCsBeforeCompaction,设置进行几次Full GC后,进行一次碎片整理。
还可以使用-XX:ParallelCMSThreads,设定CMS的线程数量,一般设置为cpu数量,不用太大。
为减轻GC压力,我们需要注意些什么?
从三个方面考虑:
- 软件如何设计架构
- 代码如何写
- 堆空间如何分配
0 0
- GC算法
- GC算法
- gc算法
- GC 算法
- GC算法
- GC算法
- GC算法
- GC算法
- GC算法
- 常用的GC算法:
- Java GC 算法
- JVM GC算法
- Java GC 算法总结
- Java GC 算法
- Java GC 算法总结
- Java GC 算法总结
- Java GC基本算法
- Java GC 算法总结
- php中的命名空间
- CPU、内存、硬盘、指令
- poj1113(凸包)
- C++11新特性——range for
- Linux c 用栈和队列实现的停车场管理系统
- GC 算法
- Linux~连接windows的ftp,unzip出现的问题
- java bean 规范
- 食物链
- Java基础语法之数组
- 206. Reverse Linked List
- C#窗体程序的退出
- 震惊! 连谷歌开发人员都不知道的androd stdio使用方式 别说我没告诉你
- Dom解析XML