JVM GC

1 篇文章 0 订阅

引用博客链接:

https://www.cnblogs.com/ASPNET2008/p/6496481.html

http://baijiahao.baidu.com/s?id=1604308216748480477&wfr=spider&for=pc

堆是GC管理的主要区域,对堆进行回收前,首先要确定对象是否已死(即该对象不可能再被使用)

  • 判断对象存活的方法

1.引用计数法:为每一个对象添加一个引用计数器,初始化为0,每当有一个引用指向它时,计数器加一。进行GC时根据该对象计数器是否为0来决定是否要进行回收。

2.可达性分析法:Java语言使用的方法。通过创建以“GC Root”的对象为起点的路径,由该节点往下搜索,搜索过的路径被称为引用链,当一个对象不再任何一个引用链上时,则说明该对象不再被使用。

GC Root包含的对象:1、虚拟机栈中引用的对象 2、本地方法栈中JNI(Java Native Interface)引用的对象 3、方法区中类静态成员变量引用的对象 4、方法区中常量引用的对象

  • 垃圾回收基本算法:

1.标记-清除算法

基于可达性分析法,在内存中构建对象树。标记从树根可达的所有对象,将未被标记的对象全部清除掉。此过程需要暂停程序运行,因不暂停程序运行可能会产生新的对象,此时该对象为树根可达,但标记工作已经完成,就会造成误清理。

缺点:递归效率低;容易造成空间碎片;需要暂停程序运行

2.复制算法

将内存划分为两个相等的块(S0活动区、S1空闲区),每次只用活动区(S0),当S0内存不够用时,将S0中存活的对象复制到S1中,清除S0所有数据。此时将S0变为空闲区、S1变为活动区

缺点:速度快但耗费空间(每次只能用内存的一半)

3.标记-整理算法

标记-清除的升级版。在标记阶段完成后,将所有存活对向一端移动,以保证内存的连续,再进行清除工作。

缺点:需暂停程序运行,递归效率低

  • JVM垃圾回收分代算法

对于JVM来说,内存分为三个区域:年轻代、年老代和持久代。年轻代和年老代用来存放Java进程中的变量,持久代用于放Java类信息。一般对我们主要关注年轻代和年老代。

JVM一般采用分代收集算法。基于该算法,将Java堆分为新生代和年老代,新生代分为Eden区(连续内存)和Survivor区,Survivor又分为Survivor0和Survivor1。新生代大小可以由-Xmn来控制,也可以用-XX:SurvivorRatio来控制Eden和Survivor的比例,默认是8。内存首次分配区域一般都在Eden(如果对象太大可能直接进入老年代-XX:PretenureSizeThreshold来控制直接升入老年代的对象大小,大于这个值的对象会直接分配在老年代上),Eden空间不足,会执行Minor GC,将存活对象转移至Survivor区中,同时将存活时间较久的数据移至老年代(虚拟机在进行MinorGC的时候,会判断要进入OldGeneration区域对象的大小,是否大于Old Generation剩余空间大小,如果大于就会发生Full GC;如果Full GC后,空间仍不足将发生OOM(即OutOfMemoryError)),年老代存放新生代中经过多次Minor GC仍存活的对象,年老代空间不足,会执行Full GC。

造成Full GC的原因:1.年老代(Tenured)被写满  2.持久代(Perm)被写满  3.System.gc()被显示调用  4.上一次GC之后Heap的各域分配策略动态变化

新生代Minor GC过程:

  1. 第一次,当Eden区满的时候,执行Minor GC,将消亡的对象清理掉,并将剩余的对象复制到一个存活区Survivor0(此时,Survivor1是空白的,两个Survivor总有一个是空白的);
  2. 下次Eden区满了,再执行一次Minor GC,将消亡的对象清理掉,将存活的对象复制到Survivor1中,然后清空Eden区;将Survivor0中消亡的对象清理掉,将其中可以晋级的对象晋级到老年代区,将存活的对象也复制到Survivor1区,然后清空Survivor0区;
  3. 当两个存活区切换了几次(HotSpot虚拟机默认15次,用-XX:MaxTenuringThreshold控制,大于该值进入老年代,并不代表一定大于这个值才能进入)之后,将仍然存活的对象复制到老年代。

注:在Eden区,HotSpot虚拟机使用了两种技术来加快内存分配。分别是bump-the-pointer和TLAB(Thread-Local Allocation Buffers),这两种技术的做法分别是:由于Eden区是连续的,因此bump-the-pointer技术的核心就是跟踪最后创建的一个对象,在对象创建时,只需要检查最后一个对象后面是否有足够的内存即可,从而大大加快内存分配速度;而对于TLAB技术是对于多线程而言的,将Eden区分为若干段,每个线程使用独立的一段,避免相互影响。TLAB结合bump-the-pointer技术,将保证每个线程都使用Eden区的一段,并快速的分配内存。

可能存在年老代对象引用新生代对象的情况,如果需要执行Young GC,则可能需要查询整个老年代以确定是否可以清理回收,这显然是低效的。解决的方法是,年老代中维护一个512 byte的块——”card table“,所有老年代对象引用新生代对象的记录都记录在这里。Young GC时,只要查这里即可,不用再去查全部老年代,因此性能大大提高。

  • 垃圾收集器(新生代采用停止复制算法,年老代采用标记整理算法)

第一阶段,Serial(串行)收集器

STW,在jdk1.3.1之前,java虚拟机仅仅能使用Serial收集器。 Serial收集器是一个单线程的收集器,但它的“单线程”的意义并不仅仅是说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。

PS:开启Serial收集器的方式

-XX:+UseSerialGC

第二阶段,Parallel(并行)收集器

Parallel收集器也称吞吐量收集器,相比Serial收集器,Parallel最主要的优势在于使用多线程去完成垃圾清理工作,这样可以充分利用多核的特性,大幅降低gc时间。

PS:开启Parallel收集器的方式

-XX:+UseParallelGC -XX:+UseParallelOldGC

第三阶段,CMS(并发)收集器

ParNew收集器:新生代收集器,使用停止复制算法,Serial收集器的多线程版,用多个线程进行GC,并行,其它工作线程暂停,关注缩短垃圾收集时间。

PS:开启ParNew收集器的方式

-XX:+UseParNewGC 

致力于获取最短回收停顿时长。CMS收集器在Minor GC时会暂停所有的应用线程,并以多线程的方式进行垃圾回收。在Full GC时不再暂停应用线程,而是使用若干个后台线程定期的对老年代空间进行扫描,及时回收其中不再使用的对象。

PS:开启CMS收集器的方式

-XX:+UseConcMarkSweepGC

第四阶段,G1(并发)收集器

分代收集器,将新生代,老年代的物理空间划分取消了;将堆内存划分为若干区域,新生代的垃圾收集依然采用暂停所有应用线程的方式,将存活对象拷贝到老年代或者Survivor空间。老年代也分成很多区域,G1收集器通过将对象从一个区域复制到另外一个区域,完成了清理工作。G1收集器(或者垃圾优先收集器)的设计初衷是为了尽量缩短处理超大堆(大于4GB)时产生的停顿。相对于CMS的优势而言是减少了堆的压缩工作、内存碎片的产生率大大降低。

PS:开启G1收集器的方式

-XX:+UseG1GC  

G1的设计原则就是简单可行的性能调优

开发人员仅仅需要声明以下参数即可:-XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=200

-XX:+UseG1GC为开启G1垃圾收集器,-Xmx32g 设计堆内存的最大内存为32G,-XX:MaxGCPauseMillis=200设置GC的最大暂停时间为200ms。如果我们需要调优,在内存大小一定的情况下,我们只需要修改最大暂停时间即可

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值