JVM GC 简述

JVM GC(垃圾回收机制)

在学习Java GC之前,我们需要记住一个单词:stop-the-world。它会出现在任何一种GC算法中。stop-the-world意味着JVM因为需要执行GC而停止了应用程序的执行。当stop-the-world发生时,除GC所需的线程外,所有的线程都进入等待的状态,直到GC任务完成。

GC优化很多时候就是减少stop-the-world的发生。

JVM GC回收哪个区域的垃圾?

需要注意的是,JVM GC只回收堆区和方法区内的对象。而栈区的数据,在超出作用域后会被JVM自动释放掉,所以其不在JVM GC的管理范围内。

JVM GC怎么判断对象可以被回收?

-对象没有引用

-作用域发生未捕获异常

-程序在作用域正常执行完毕

-程序执行了System.exit()

-程序发生意外终止(被杀线程等)

在Java程序中不能显示的分配和注销缓存,因为这些事情JVM都帮我们做了,那就是GC。

有时候我们可以将相关的对象设置成null来试图显式的清除缓存,但是并不是设置成null就会一定被标记成可回收,有可能发生逃逸。

将对象设置成null至少没有什么坏处,但是使用System.gc()便不可取了,使用System.gc()时并不是马上执行GC操作,而是会等待一段时间,甚至不执行,而且System.gc()如果被执行,会触发Full GC,这非常影响性能。

JVM GC什么时候执行?

eden区空间不够存放新对象的时候,执行Minro GC。升到老年代的对象大于老年代剩余空间的时候执行Full GC,小于HandlePromotionFailure参数时被强制Full GC。调优主要是减少Full GC的触发次数,可以通过NewRatio控制新生代转老年代的比例,通过Max Tenuring Threshold设置对象进入老年代的年龄阀值。

按代的垃圾回收机制

新生代(Young generation):绝大多数最新被创建的对象都会被分配到这里,由于大部分在创建后很快变得不可达,很多对象被创建在新生代,然后“消失”。对象从这个区域“消失”的过程我们称为:Minor GC。

老年代(Old generation):对象没有变得不可达,并且从新生代周期中存活下来,会被拷贝到这里。其区域分配的空间要比新生代多。也正因为其较大的空间,发生在老年代的GC次数要比新生代少的多。对象从老年代消失的过程,称之为:Major GC或者Full GC。

持久层(Permanent generation)也称为方法区(Method area):用来保存类常量以及字符串常量。注意,这个区域不是用来存储那些从老年代存活下来的对象,这个区域也可能发生GC。发生在这个区域的GC时间也被算为Major GC。只不过这个区域发生GC的条件非常苛刻,必须符合以下三种条件才会被回收:

所有实例被回收

加载该类的ClassLoader被回收

CLass对象无法通过任何途径访问(包括访问)

如果老年代的对象需要引用新生代的对象,会发生什么?

为了解决这个问题,老年代中存在一个card table,他是一个512byte大小的块。所有老年代的对象指向新生代对象的引用都会被记录在这个表中。当针对新生代执行GC的时候,只需要查询card table来执行是否可以被回收,而不用查询整个老年代。这个card table由一个write barrier来管理。write barrier给GC带来了很大的性能提升,虽然由此可能带来一些开销,但完全是值得的。

默认的新生代(Young generation)、老年代(Old generation)所占空间比例为1:2

新生代空间的构成与逻辑

为了更好的理解GC,我们来学习新生代的构成,它用来保存那些第一次被创建的对象,它被分成三个空间

-一个伊甸园(Eden)

-两个幸存者空间(From Survivor、To Survivor)

默认新生代空间分配:Eden:From:To = 8:1:1

每个空间的执行顺序如下:

绝大多数刚刚被创建的对象会存放在伊甸园空间(Eden)

在伊甸园空间执行第一次GC(Minor GC)之后,存活的对象被移动到其中一个幸存者空间(Survivor)

此后,每次伊甸园空间执行GC后,存活的对象会被堆积在同一个幸存者空间。

当一个幸存者空间饱和,还在存活的对象会被移动到另一个幸存者空间。然后会清空已经饱和的那个幸存者空间。

在以上步骤中重复N次(N = MaxTenuringThreshold(年龄阀值设定,默认15))依然存活的对象,就会被移动到老年代。

从上面的步骤可以发现,两个幸存者空间,必须有一个是保持空的。如果两个两个幸存者空间都有数据,或两个空间都是空的,那一定是你的系统出现了某种错误。

我们需要重点记住的是,对象在刚刚被创建之后,是保存在伊甸园空间的(Eden)。那些长期存活的对象会经由幸存者空间(Survivor)转存到老年代空间(Old generation)。

也有例外出现,对于一些比较大的对象(需要分配一块比较大的连续内存空间)则直接进入到老年代。一般在Survivor 空间不足的情况下发生。

并且Survivor并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。

原文 

https://segmentfault.com/a/1190000023017150

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » JVM GC 简述

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址