JVM垃圾回收机制

JVM垃圾回收机制

一、回收堆区

垃圾回收器在堆进行垃圾回收前,首先要判断这些对象那些还存活,那些已经“死去”。判断对象是否已“死”有如下几种算法:

1.引用计数法

给对象增加一个引用计数器,每当有一个地方引用它时,计数器就+1;

当引用失效时,计数器就-1;

任何时刻计数器为0的对象就是不能再被使用的,即对象已“死”。

引用计数法实现简单,判定效率也比较高,在大部分情况下都是一个比较好的算法。

但是,在主流的JVM中没有选用引用计数法来管理内存,最主要的原因是引用计数法无法解决对象的循环引用问题。

2. 可达性分析算法

在上面讲了,Java并不采用引用计数法来判断对象是否已“死”,而采用“可达性分析”来判断对象是否存活。

通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为“引用链”,当一个对象到 GC Roots 没有任何的引用链相连时(从 GC Roots 到这个对象不可达)时,证明此对象不可用。以下图为例:

<span>JVM垃圾回收机制</span>

在Java语言中,可作为GC Roots的对象包含以下几种:

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象。
  2. 方法区中静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法栈中(Native方法)引用的对象

引用

JDK1.2以前,Java中引用的定义很传统: 如果引用类型的数据中存储的数值代表的是另一块内存的起始地址,就称这块内存代表着一个引用。

这种定义有些狭隘,一个对象在这种定义下只有被引用或者没有被引用两种状态。

JDK1.2之后,Java对引用的概念做了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)四种,这四种引用的强度依次递减。

1.强引用

类似于”Object obj = new Object()”这类的引用,只要强引用还存在,垃圾回收器永远不会回收掉被引用的对象实例。

当内存空 间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

2.软引用

如果一个对象只具有软引用,那就类似于可有可无的生活用品。

如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。

软引用可用来实现内存敏感的高速缓存。

举例:查看网页,可能后退,那么刚才的网页要不要一直存储,一直存储就是强引用,不存储就是回收,那么折中一下,于是产生了弱引用,当内存空间足够时,就不回收,不足时,再回收。这个根据内存敏感程度而变化而决定是否缓存,就是内存敏感的高速缓存。

3.弱引用

对象拥有更短暂的生命周期。

在gc线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间是否充足,都会回收它的内存。

由于gc是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

4.虚引用

就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。

如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

作用: 虚引用主要用来跟踪对象被垃圾回收的活动

区别: 虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。

当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。

如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

3.真正判决

要宣告一个对象的真正死亡,至少要经历两次标记过程

如果对象在进行可达性分析之后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选。

筛选的条件是此对象是否有必要执行finalize()方法。

​ 没必要:没有覆盖finalize()方法finalize()方法已经被调用过一次了

审判

如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个叫做F-Queue的队列之中,并在稍后由一个虚拟机自动建立的、低优先级的Finalizer线程去执行它

如果对象在finalize()中成功拯救自己:与引用链上的任何一个对象建立起关联关系

那在第二次标记时它将会被移除出”即将回收”的集合,也就暂时逃脱死亡的命运了。

如果对象这时候还是没有逃脱,那基本上它就是真的被回收了。

二、回收方法区

方法区(永久代)的垃圾回收主要收集两部分内容:废弃常量无用类。

废弃常量:

没有任何一个String对象引用常量池中的”abc”常量,也没有其他地方引用这个字面量,如果此时发生GC并且有必要的话,这个”abc”常量会被系统清理出常量池。

常量池中的其他类(接口)、方法、字段的符号引用也与此类似。

无用类

1.该类的所有实例都已经被回收(即在Java堆中不存在任何该类的实例)
2.加载该类的ClassLoader已被回收
3.该类对应的Class对象没有任何其他地方被引用,无法在任何地方通过反射访问该类的方法

三、垃圾回收算法

1.标记-清除算法

首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象

“标记-清除”算法的不足主要有两个

  1. 效率问题:标记和清除这两个过程的效率都不高
  2. 空间问题:标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行中需要分配较大对象时,无法找到足够连续内存而不得不提前触发另一次垃圾收集。

<span>JVM垃圾回收机制</span>

2.复制算法(新生代回收算法)

它将可用内存按容量划分为大小相等的两块,每次只使用其中一块。

当这块内存需要进行垃圾回收时,会将此区域还存活着的对象复制到另一块上面,然后再把已经使用过的内存区域一次清理掉。

因为复制过去后,另一边的内存肯定是连续的了,此时再把使用过得内存区域清理,从而达到了整理的效果。

也就是伊甸园区移动到survive0和survive1区的算法。

但是,伊甸园区的对象都是朝生夕死的,所以并不需要1:1的空间,所以出现了8:1:1的默认比例

<span>JVM垃圾回收机制</span>

3.标记整理算法(老年代回收算法)

复制收集算法在对象存活率较高时会进行比较多的复制操作,效率会变低。因此在老年代一般不能使用复制算法。

而是采用标记整理算法

标记过程仍与“标记-清除”过程一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象向一端移动,然后直接清理掉除存活对象以外的内存。流程图如下:

<span>JVM垃圾回收机制</span>

4.分代收集算法

就是将堆区分开,不同的位置采用不同的算法

新生代中,每次垃圾回收都有大批对象死去,只有少量存活,因此我们采用复制算法;

老年代中对象存活率高,就必须采用”标记-清理”或者”标记-整理”算法。

四 .Minor GC、Major GC、Full GC的区别?

Minor GC 又称为新生代GC 指的是发生在新生代的垃圾回收操作(包括Eden区和Survivor区)。

当年轻代内存空间被用完时,就会触发垃圾回收。这个垃圾回收叫做Minor GC。

Major GC通常是跟full GC是等价的,收集整个GC堆。

但因为HotSpot VM发展了这么多年,外界对各种名词的解读已经完全混乱了

Full GC定义是相对明确的,就是针对整个新生代、老生代、元空间(metaspace,java8以上版本取代perm gen)的全局范围的GC。

针对HotSpot VM GC来看

它里面的GC其实准确分类只有两大种:

Partial GC:并不收集整个GC堆的模式

  • Young GC:只收集年轻代的GC
  • Old GC:只收集老年代的GC。只有CMS的concurrent collection是这个模式
  • Mixed GC:收集整个年轻代以及老年代的GC。只有G1有这个模式

Full GC:收集整个堆,包括young gen、old gen、perm gen(如果存在的话)等所有部分的模式。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/115159.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • ubuntu下载pycharm并安装_ubuntu卸载pycharm

    ubuntu下载pycharm并安装_ubuntu卸载pycharm1.下载PyCharm安装包1)进入PyCharm官网,选择Professional专业版,直接点击DOWNLOAD,如图1;2)进入跳转页面,出现提示框,点savefile,开始下载;3)下载完成后,安装包在Downloads文件夹下,如图2;2.安装PyCharm1)解压文件。右键安装包,点击“ExtractHere”,如图3,图4;图三:解压图四:解压后2)先整理文件夹,把pycharm-2018.1.4文件夹放在Downloads根目录,方便找到;3)打开终端,进

  • PHP 二维数组根据某个字段排序

    PHP 二维数组根据某个字段排序PHP 二维数组根据某个字段排序

  • 云邦互联免费空间(免备案,无广告)「建议收藏」

    云邦互联免费空间(免备案,无广告)「建议收藏」【1G免费全能空间,免备案,无广告】1G全能空间+100M数据库(Mysql5.5)支持的脚本:ASP、PHP(5.2-7.0)、.NET(2.0/4.0)没有任何限制,详细功能请访问:https://www.yunzz.net/host/free.html(云邦互联)推广员:ftp257684p…

  • arm linux 移植全部过程「建议收藏」

    arm linux 移植全部过程「建议收藏」armlinux移植全部过程总述功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML图表FLowchart流程图导出与导入导出导入总述之前做过linux在powe…

  • AutoSAR系列讲解 – 总目录

    AutoSAR系列讲解 – 总目录博主想在开始前先讲一下我们的具体安排,这里就以目录加说明的形式展示出来,同时这也可以作为大家学习AutoSAR的参考。由于实践篇需要在很久以后才会更新,所以这里我们先罗列一下理论篇的内容,实践篇等开始更新的时候我再补充上

  • double转BigDecimal精度丢失的解决方案。

    double转BigDecimal精度丢失的解决方案。因为double有精度丢失的问题,所以关于小数点的计算通常使用BigDecimal来计算。但直接调用BigDecimal的构造函数,会出现精度丢失问题。publicBigDecimal(doubleval){this(val,MathContext.UNLIMITED);}例子:publicclassDemo1{publicstaticvoidmain(String[]args){//输出结果:4.7999999999999998223

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号