“阿里架构师”的JVM之GC详解

GC的概念

Grabage Collection:在系统运行过程当中占据空间的无用对象在必定时间范围内被及时清理来保证整个系统有足够的内存空间来运行。java中GC的对象是堆和永久区。java

 

经常使用的GC算法算法

引用计数法(reference counting)性能优化

概念:对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器就减1。只要对象A的引用计数器的值为0,则对象A就不可能再被使用。多线程

出现的问题:①.引用和去引用伴随加法和减法,影响性能。②.很难处理垃圾对象的循环引用。下面一段代码说明循环引用问题:架构

 
package jvm;/** * GC引用计数法Demo * @author Administrator * */public class JvmGcDemo { public static void main(String[] args) { GcObject g1 = new GcObject();//栈中建立变量g1指向(引用)new出对象GcObject1(堆中),此时GcObject1的引用计数为1 GcObject g2 = new GcObject();//栈中建立变量g2指向(引用)new出对象GcObject2(堆中),此时GcObject2的引用计数为1 g1.o = g2;//GcObject1的引用计数再加1,引用计数=2 g2.o = g1;//GcObject2的引用计数再加1,引用计数=2 g1 = null;//GcObject1的引用计数减1,引用计数=1 g2 = null;//GcObject2的引用计数减1,引用计数=1 } } class GcObject{ public Object o; }1234567891011121314151617181920

看上面代码流程发现GcObject1和GcObject2循环引用,引用计数都为1,最后引用计数法没法回收这两个无用的对象。并发

在eclipse中初始化配置jvm参数,最大使用内存20M,最小使用内存5M:-Xmx20m -Xms5m;修改上段代码,用System.gc()进行GC,即下面代码:eclipse

 
package jvm;/** * GC引用计数法Demo * @author Administrator * */public class JvmGcDemo { public static void main(String[] args) { printMemory(1);//打印内存 free memory:4.719520568847656M,约5M GcObject g1 = new GcObject();//栈中建立变量g1指向(引用)new出对象GcObject1(占用1M堆内存),此时GcObject1的引用计数为1 GcObject g2 = new GcObject();//栈中建立变量g2指向(引用)new出对象GcObject2(占用1M堆内存),此时GcObject2的引用计数为1 printMemory(2);//打印内存发现free memory:2.7194900512695312M = 4.719520568847656M - 2M g1.o = g2;//GcObject1的引用计数再加1,引用计数=2 g2.o = g1;//GcObject2的引用计数再加1,引用计数=2 g1 = null;//GcObject1的引用计数减1,引用计数=1 g2 = null;//GcObject2的引用计数减1,引用计数=1 System.gc();//进行GC printMemory(3);//free memory3:4.922454833984375M 说明GcObject1和GcObject2被回收 } /** * 打印内存 */ public static void printMemory(int i){ //打印空闲堆内存 System.out.println("free memory"+i+":"+Runtime.getRuntime().freeMemory()/1024.0/1024+"M"); System.out.println("---------------------------->"); } } class GcObject{ public Object o; byte[] by = new byte[1024*1024];//占用1M内存}1234567891011121314151617181920212223242526272829303132

控制台输出:jvm

free memory1:4.75958251953125M分布式

—————————->微服务

free memory2:2.7194137573242188M

—————————->

free memory3:4.922454833984375M

—————————->

发现GC后free memory3比free memory2增长了2M多一点(多出的部分是回收的其余垃圾内存),free memory3与free memory1无明显差异,说明对象GcObject1和GcObject2被回收,这是由于JVM没有采用引用计数法进行回收垃圾。

标记-清除算法(mark-and-sweep)

概念:标记-清除算法是现代垃圾回收算法的思想基础。是指在可使用的内存被耗尽的时候,GC线程就会被触发并将程序暂停,进行标记-清除算法将垃圾回收,过程分为两个阶段:标记阶段和清除阶段。在标记阶段,首先经过根节点,标记全部从根节点开始的可达对象。所以,未被标记的对象就是未被引用的垃圾对象。而后,在清除阶段,清除全部未被标记的对象。

出现的问题:①.标记和清除过程效率不高 ,并且在进行GC的时候,须要中止应用程序,这会致使用户体验很是差劲。②.标记清除以后会产生大量不连续的内存碎片。失效对象都是随即的出如今内存的各个角落的,如今把它们清除以后,内存的布局天然会乱七八糟。

标记-压缩算法(mark-compact)

概念:标记-压缩算法适合用于存活对象较多的场合,如老年代。它在标记-清除算法的基础上作了一些优化。和标记-清除算法同样,标记-压缩算法也首先须要从根节点开始,对全部可达对象作一次标记。但以后,它并不简单的清理未标记的对象,而是将全部的存活对象压缩到内存的一端,以后,清理边界外全部的空间。

出现的问题:标记-压缩算法的缺点就是效率也不高,不只要标记全部存活对象,还要整理全部存活对象的引用地址。

复制算法(copying)

概念:将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,以后,清除正在使用的内存块中的全部对象,交换两个内存的角色,完成垃圾回收.

出现的问题:用空间换取时间,空间浪费,不适用于存活对象较多的场合,如老年代等。

分代收集算法(generational collecting)

概念:把对象分为年轻代、年老代、持久代,对不一样的生命周期使用不一样的算法进行回收,通常新生代对象和小对象适合复制算法,老年代对象和大对象存活适合标记-清理或者标记-压缩算法。

对象的可触及性

可触及的

从根节点能够触及到这个对象。根通常为栈中引用的对象、方法区中静态成员或者常量引用的对象(全局对象)、JNI方法栈中引用对象。

可复活的

一旦全部引用被释放,就是可复活状态,由于在finalize()中可能复活该对象。

不可触及的

在finalize()以后,可能会进入不可触及状态,不可触及的对象不可能复活,能够回收。

 

GC参数

串行收集器

最古老、最稳定、效率高的收集器,缺点是可能会产生较长时间的停顿,她只使用一个线程去回收,没有办法发挥多核计算机的性能。

-XX:+UseSerialGC

新声代和老年代使用串行回收,新生代使用复制算法、老年代使用标记-压缩算法。

并行收集器

ParNew

-XX:UserParNewGC

新生代使用并行回收,老年代使用串行回收。

新生代的的并行回收采用复制算法,多线程回收(多核支持),可使用XX:ParallelGCThreads限制线程的数量。固然,多线程并不必定快。

Parallel

相似于ParNew,新声代采用复制算法、老年代使用标记-压缩算法,更加关注吞吐量。

-XX:+UseParallelGC 使用Parallel收集器和老年代串行。

-XX:+UserParalleOldGC 使用Paralle收集器老年代并行。

-XX:MaxGCPauseMills 最大停顿时间,单位毫秒,GC尽力保证收回时间不超过设定的值,但不是确定。

-XX:GCTimeRatio 在0-100中取值,表明垃圾收集时间占总时间的比,默认99,表明最大容许1%的时间作GC。

CMS收集器

Concurrent Mark Sweep 并发(与用户线程一块儿执行)标记清除,这是一个老年代的收集器(新声代使用的是ParNew),GC中应用线程停顿时间比较短,因此并发阶段会下降吞吐量。

-XX:+UseConcMarkSweepGC

CMS收集器GC过程

  1. 初始标记

    根能够直接关联到的对象,速度快

  2. 并发标记(和用户线程一块儿)

    主要标记过程,标记所有对象

  3. 从新标记

    因为并发标记时,用户线程依然运行,所以在正式清理前,再作修正

  4. 并发清除(和用户线程一块儿)

    基于标记结果,直接清理对象

  1.  

CMS收集器特色

  1. 尽量下降停顿

  2. 会影响系统总体吞吐量和性能。好比,在用户线程运行过程当中,分一半CPU去作GC,系统性能在GC阶段,反应速度就降低一半。

  3. 清理不完全。由于在清理阶段,用户线程还在运行,会产生新的垃圾,没法清理。

  4. 不能在空间快满时再清理,由于和用户线程一块儿运行。-XX:CMSInitiatingOccupancyFraction设置触发GC的阈值,若是不幸内存预留空间不够,就会引发concurrent mode failure。

CMS收集器经常使用参数

-XX:+UseCMSCompactAtFullCollection Full GC,进行一次整理,整理的过程是独占的,停顿时间较长。

-XX:+CMSFullGCsBeforeCompaction 设置几回 Full GC后进行一次一次碎片整理。

-XX:ParallelCMSThreads 设定CMS的线程数量,通常约等于可用CPU的数量,不宜设置太大。

GC的参数整理

-XX:+UseSerialGC:在新生代和老年代使用串行收集器

-XX:SurvivorRatio:设置eden区大小和survivior区大小的比例

-XX:NewRatio:新生代和老年代的比

-XX:+UseParNewGC:在新生代使用并行收集器

-XX:+UseParallelGC :在新生代使用并行回收收集器

-XX:+UseParallelOldGC:老年代使用并行回收收集器

-XX:ParallelGCThreads:设置用于垃圾回收的线程数

-XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS+串行收集器

-XX:ParallelCMSThreads:设定CMS的线程数量

-XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发

-XX:+UseCMSCompactAtFullCollection:设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片的整理

-XX:CMSFullGCsBeforeCompaction:设定进行多少次CMS垃圾回收后,进行一次内存压缩

-XX:+CMSClassUnloadingEnabled:容许对类元数据进行回收

-XX:CMSInitiatingPermOccupancyFraction:当永久区占用率达到这一百分比时,启动CMS回收

-XX:UseCMSInitiatingOccupancyOnly:表示只在到达阀值的时候,才进行CMS回收

在此我向你们推荐一个架构学习交流群。交流学习群号: 744642380, 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良

相关文章
相关标签/搜索