广告
返回顶部
首页 > 资讯 > 精选 >JVM中垃圾收集算法的示例分析
  • 444
分享到

JVM中垃圾收集算法的示例分析

2023-06-15 09:06:15 444人浏览 泡泡鱼
摘要

这篇文章给大家分享的是有关JVM中垃圾收集算法的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、什么是垃圾本文要讲的是垃圾收集算法,那么首先要确定的问题就是什么是垃圾,也就是哪些对象是要被回收的,对此有

这篇文章给大家分享的是有关JVM中垃圾收集算法的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

一、什么是垃圾

本文要讲的是垃圾收集算法,那么首先要确定的问题就是什么是垃圾,也就是哪些对象是要被回收的,对此有两种判断方式:

1.1 引用计数算法

什么样的对象是要被回收的,很明显,没有被引用的对象才要被回收。因此在对象中加一个引用计数器,当有一个对象引用该对象的时候,计数器就加一,当引用结束后,计数器就减一,当计数器为0的时候,对象就可以被回收了。

1.1.1 优点
  • 原理简单

  • 判断效率高

1.1.2 缺点
  • 需要花费额外的内存空间(引用计数器)

  • 无法回收相互循环引用的对象:比如有对象A和对象B,A引用B,B引用A,两对象的引用计数器都为1,理论上来说,没有其他对象能够引用到A和B了,因此这两个对象应该被回收,然后按照引用计数算法的判断,这两个对象无法被回收。(要克服这个缺点,需要在代码中做很多特殊处理)

1.2 可达性分析算法

因为引用计数算法的缺陷,各大主流的商用程序语言都采用可达性分析算法来判断对象是否需要被回收。可达性顾名思义,是指对象跟对象之间有引用关系,此处有两种引用关系:

  • 对象A引用对象B,则称对象A到对象B可达

  • 对象A引用对象B,对象B引用对象C,对象A可以通过若干个对象(此处为对象B)引用到对象C,则称对象A到对象C可达。

要判断一个对象是否可达,首先要有一个根对象,在Java中有一系列被称为“GC Roots”的根对象作为起始节点集,任何从“GC Roots”不可达的对象都是需要被垃圾收集器回收的垃圾。

1.2.1 优点

可以有效解决引用计数算法的相互循环引用问题

 二、什么是引用

在讨论什么是垃圾的时候,多次提到引用一词,那么什么是引用呢?

2.1 jdk1.2以前

按照书中的说法,在JDK1.2以前,引用的意思是:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称该reference数据是代表某块内存、某个对象的引用。
在这种定义下可以发现,对于一个对象来说,就只有未被引用和被引用两种状态了,但其实可以发现,在实际应用中,并不是一定要把对象回收掉的,书中有个词就很贴切,“食之无味,弃之可惜”,我们想要的是当内存空间足够的时候,把这部分本该回收的对象留着不回收,当内存不够的时候,就将其回收。

2.2 JDK1.2之后

因此在JDK1.2之后,引用的概念就扩张到了以下四种:

  • 强引用:指传统意义上的引用,有强引用的对象是肯定不被回收的;

  • 软引用:用于描述一些还有用,但非必须的对象,当要发生内存溢出的时候,就会回收软引用对象;

  • 弱引用:用于描述非必须对象,强度比软引用弱一点,当垃圾收集器开始工作,无论内存够不够都会回收弱引用对象;

  • 虚引用:虚引用意思就是这个引用跟没有一样,对对象完全没有印象,其存在的唯一作用就是在对象被垃圾收集器回收时能收到一个系统通知。

三、垃圾判断全流程

按照书中所述,我画了个流程图,如下:

JVM中垃圾收集算法的示例分析

一个对象在被回收前,需要进行两次标记,第一次进行可达性分析后,对象被垃圾收集器认为是垃圾,则对对象进行第一次标记,然后垃圾收集器会给予对象一次自救的机会,不然就没必要两次标记了,一次标记直接回收就好了。
我们都知道对象有个finalize()方法,自救的机会就在这个方法中,当第一次标记后,垃圾收集器会对对象做一次筛选,筛选条件是要不要执行对象的finalize()方法,如果开发者未对finalize()方法进行覆写或者虚拟机已经执行过该对象的finalize()方法了,那么自然就不用再执行了,反之则需要执行。
将筛选出来的需要执行finalize()方法的对象放入一个特定的队列中,由虚拟机统一执行,如果finalize()方法中使得对象被别的对象引用了,导致可达性分析认为对象是可用的,那么自救就成功了。
根据筛选的条件可以知道,对象的自救机会在整个程序中只有一次,因为finalize()方法只会被执行一次。
需要注意的是,官方明确申明不推荐使用finalize()方法,因为使用它的不确定性太大。对于资源清理等操作,try…catch语法可以做的更好。

四、垃圾收集算法

大多数虚拟机的垃圾收集都采用了分代收集的形式,这是因为三条经验法则:

  • 弱分代假说:绝大多数对象都是朝生夕死的;

  • 强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡

  • 跨代引用假说:跨代引用相对于同代引用来说仅占极少数

因为对象的生存周期是不一样的,所以我们不能对所有对象采用同一种垃圾收集算法,采用分代收集,将有共性的对象放在一个集合里,会大大地提高垃圾收集效率。
按照上述经验法则,可以将堆内存分为两代:

  • 新生代:对应弱分代假说

  • 老年代:对应强分代假说

下面分别介绍三种垃圾收集算法:

4.1 标记-清除算法

标记-清除算法是最基础的垃圾收集算法,顾名思义,标记就是判断对象是否是垃圾,也就是前面第四节讲到的内容,清除就是统一回收垃圾,该算法有两种执行过程:

  • 标记所有需要被回收的对象,统一回收所有标记的对象;

  • 标记所有存活对象,统一回收所有未被标记的对象。

标记-清除算法示意图:

JVM中垃圾收集算法的示例分析

标记-清除算法有两大缺点:

  • 执行效率不稳定:标记和清除两个过程的执行效率随对象数量的增加而降低

  • 内存空间碎片化:执行完标记和清除后,会产生大量的不连续的内存碎片,当分配大对象的时候,如果找不到足够的连续内存,那么会提前触发下一次垃圾收集。

4.2 标记-复制算法

基于标记-清除算法的缺点,标记-复制算法将内存空间一分为二,两块内存空间等大,每次只使用其中一块内存空间,当这一块内存空间用完了,就把存活的对象复制到另一块内存空间中,然后一次性清理所有已使用的内存空间。

标记-复制算法示意图:

JVM中垃圾收集算法的示例分析

标记-复制算法解决了标记-清除算法面对大量可回收对象场景下的不足之处,面对这种情况,标记-复制算法只需要将内存空间中的存活对象复制到另一半内存空间中,可以有效解决内存碎片的问题,在给对象分配内存的时候,只需要移动堆顶指针按顺序分配即可,不过这个算法也有缺点:

  • 面对大量不可回收对象的时候,会产生大量内存间对象复制的开销;

  • 原先的内存空间缩小了一半,会造成严重的空间浪费

4.3 标记-整理算法

标记-复制算法不足以应对有大量存活对象的场景,因此就有了标记-整理算法,该算法的执行流程如下:

  • 与其他算法一样,首先对对象进行标记;

  • 将所有存活对象往内存的一个方向移动;

  • 直接清理掉边界以外的内存

标记-整理算法示意图:

JVM中垃圾收集算法的示例分析

标记-整理算法同样可以解决内存碎片化问题,并且不会造成空间浪费,不过它也有缺点:

在大量对象存活的情况下,移动对象并更新引用也会花费大量时间

4.4 应用

不同的场景适用不同的垃圾收集算法,像标记-复制算法就适用于存活对象少的情况下,也就是新生代区域,像标记-整理算法就适用于存活对象多的情况下,也就是老年代。
这里有点需要注意的是,标记-整理算法对于老年代来说也不是完美的,在5.3节我们说过,在大量对象存活的情况下,移动对象和更新引用也是要花费大量时间的,不过算法这个东西吧,它比的是谁更适合,对于标记-复制算法来说,我把区域一分为二,如果大量对象存活,我要把对象全部复制到另一块内存区域,这个开销不见得比标记-整理算法少,并且它还有个缺点就是可用内存一下子少了一半,这个问题在标记-整理算法中是没有的。也有的虚拟机采用标记-清除算法标记-整理算法协作的垃圾收集方案,没有最适合,只有更适合

4.5 优化

前面讲标记-复制算法的时候说到要把内存区域等半分,这是在没有规定场景的情况下,在新生代中采用该垃圾收集算法可以做更好的优化。

众所周知,新生代中的对象都是朝生夕死的,因此当标记完成后的存活对象肯定是少量的,根据这个现象,可以将内存区域非等半分,比如说9:1的分法,这里我们将90%的内存区域称为Eden空间,将10%的内存区域称为Survivor空间,一开始使用Eden空间的内存,当垃圾收集时,将Eden空间的存活对象复制到Survivor空间中。
这里肯定有人要问了,那下一次使用Survivor空间不是就只有10%的内存了吗?

对的,所以这里有两种解决方案:

  • 将存活对象从Eden空间复制到Survivor空间后,再从Survivor空间复制回Eden空间

  • Eden空间再分离出一个Survivor空间,每次可使用的内存为一个Eden空间一个Survivor空间,当垃圾收集时,将使用的内存区域中的存活对象复制到另一个Survivor空间中,下一次的可用内存则为Eden空间和这个Survivor空间,如此循环往复。

第二种方法就是大名鼎鼎的半区复制分代策略,现在叫Appel式回收,因为提出这个策略的人叫Apple,目前很多虚拟机在新生代的垃圾收集算法中采用这个策略。

4.5.1 缺点

半区复制分代策略也是有缺点的,从上面的叙述中我们可以知道,Eden空间Survivor空间的内存占比为8:1:1,如果当垃圾收集后的存活对象所需要的内存空间大于一个Survivor空间时,那就难办了。

4.5.2 补丁

既然Survivor空间的内存不够放存活对象了,那就去借内存区域,这个借当然不能跟Eden空间Survivor空间借,不然会影响到整个算法,增加算法的复杂度。新生代不能借,那就跟老年代借,这里就有一个所谓的内存分配担保,放不下的存活对象将直接通过分配担保机制进入到老年代中。有了这个“逃生门”一样的设计,这个策略才算是没有漏洞。

五、写在后面

几个垃圾收集算法的图是我直接截了书里面的图,因为我觉得它讲的很详细了,第四节垃圾判断过程在书中实际上是一长串的代码,看懂不难,不过我想画个流程图可能更清楚点,这个流程图是用plantUML画出来的,这个工具可以用代码画出各种图,功能强大,有兴趣的可以百度搜搜,下面是这个流程图的代码:

@startumlstart:对对象进行可达性分析;if (对象是否为垃圾?) then (是)    :进行第一次标记;    if (对象没有覆盖finalize()方法 或 finalize()方法已经被虚拟机调用) then(是)        :没必要执行对象的finalize()方法;    else (否)        :将对象放入队列F-Queue中;        :等待虚拟机的Finalizer线程执行对象的finalize()方法;        :执行对象的finalize()方法;    endif    if (对象是否为垃圾?) then (是)        :进行第二次标记;        :垃圾回收;    else (否)        stop    endifelse (否)    stopendifstop@enduml

感谢各位的阅读!关于“JVM中垃圾收集算法的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

--结束END--

本文标题: JVM中垃圾收集算法的示例分析

本文链接: https://www.lsjlt.com/news/279092.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • JVM中垃圾收集算法的示例分析
    这篇文章给大家分享的是有关JVM中垃圾收集算法的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、什么是垃圾本文要讲的是垃圾收集算法,那么首先要确定的问题就是什么是垃圾,也就是哪些对象是要被回收的,对此有...
    99+
    2023-06-15
  • JVM垃圾回收算法的示例分析
    这篇文章主要介绍了JVM垃圾回收算法的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。新一代JVM垃圾回收算法JVM垃圾回收的瓶颈传统分代JVM垃圾回收方式,已经在一定...
    99+
    2023-06-17
  • JVM中垃圾回收机制的示例分析
    这篇文章主要介绍了JVM中垃圾回收机制的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。堆内存的划分分为三个部分(以下名词表示同一个区):新生区、新生代、年轻代养老区、...
    99+
    2023-06-29
  • PHP中垃圾收集机制的示例分析
    这篇文章主要为大家展示了“PHP中垃圾收集机制的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“PHP中垃圾收集机制的示例分析”这篇文章吧。PHP的垃圾收...
    99+
    2022-10-19
  • jvm垃圾回收算法详细解析
    目录前言几种常用的垃圾回收算法1、引用计数法2、根搜索算法3、标记清除法(Mark-Sweep)4、复制交换算法(Mark-Sweep)5、标记压缩算法(Mark-Compact)J...
    99+
    2022-11-13
  • JVM知识总结之垃圾收集算法
    目录一、什么是垃圾1.1 引用计数算法1.1.1 优点1.1.2 缺点1.2 可达性分析算法1.2.1 优点 二、什么是引用2.1 JDK1.2以前2.2 JDK1.2之后...
    99+
    2022-11-12
  • Java垃圾回收的示例分析
    这篇文章将为大家详细讲解有关Java垃圾回收的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Java垃圾回收是一项自动化的过程,用来管理程序所使用的运行时内存。通过这一自动化过程,JVM解除了程序...
    99+
    2023-05-30
    java
  • JavaGC垃圾回收算法分析
    目录对象探活强-软-弱-虚引用标记清除标记复制标记整理回收算法的在堆内存上的应用对象探活 在讨论回收算法前,更为重要的问题是如何判断一个对象是否可以被回收? 引用计数算法 每个对象会...
    99+
    2022-12-20
    Java GC垃圾回收 Java GC回收算法
  • js中垃圾回收机制的示例分析
    这篇文章主要介绍了js中垃圾回收机制的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。原理找到不再被使用的变量,然后释放其占用的内存,...
    99+
    2022-10-19
  • PHP中垃圾回收机制的示例分析
    小编给大家分享一下PHP中垃圾回收机制的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!如果用过C语言,那么申请内存的方式是malloc或者是calloc,...
    99+
    2023-06-15
  • kubernetes中垃圾回收机制的示例分析
    这篇文章主要介绍了kubernetes中垃圾回收机制的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一:前言Kubernetes系统在长时间运行后,Kubernete...
    99+
    2023-06-04
  • JVM内存管理深入垃圾收集器与内存分配策略的示例分析
    这篇文章给大家介绍JVM内存管理深入垃圾收集器与内存分配策略的示例分析,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出...
    99+
    2023-06-17
  • JVM中怎么解析JVM分代垃圾回收策略
    这篇文章将为大家详细讲解有关JVM中怎么解析JVM分代垃圾回收策略,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。由于不同对象的生命周期不一样,因此在JVM的垃圾回收策略中有分代这一策略。JV...
    99+
    2023-06-17
  • JVM的垃圾回收算法一起来看看
    目录垃圾回收算法概念1.标记算法1.1 引用计数法(Reference Counting)1.2 可达性分析算法(Reachable Analysis)2.回收算法2.1 标记清除算...
    99+
    2022-11-13
  • 浏览器中垃圾回收机制的示例分析
    这篇文章将为大家详细讲解有关浏览器中垃圾回收机制的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。浏览器的垃圾回收机制垃圾回收是一种自动的内存管理机制。当计算机上的...
    99+
    2022-10-19
  • JVM中常见的垃圾收集器有哪些
    这篇文章主要介绍了JVM中常见的垃圾收集器有哪些,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。Java...
    99+
    2023-06-02
  • JVM调优之垃圾定位、垃圾回收算法、垃圾处理器的区别有哪些
    本篇内容主要讲解“JVM调优之垃圾定位、垃圾回收算法、垃圾处理器的区别有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JVM调优之垃圾定位、垃圾回收算法、垃...
    99+
    2022-10-19
  • go:垃圾回收GC触发条件的示例分析
    这篇文章将为大家详细讲解有关go:垃圾回收GC触发条件的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。什么是gogo是golang的简称,golang 是Google开发的一种静态强类型、编译型、...
    99+
    2023-06-14
  • js中新生代垃圾回收知识点的示例分析
    这篇文章将为大家详细讲解有关js中新生代垃圾回收知识点的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。在进行老生代的标记清除法回收以前,还会有一个新生代的垃圾回收...
    99+
    2022-10-19
  • Linux下垃圾文件的示例分析
    这篇文章主要介绍Linux下垃圾文件的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Linux 计算机安装后,在我们不断的使用过程中,因为添加、删除软件和上网冲浪、调试程序等行为,硬盘中会产生各种各...
    99+
    2023-06-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作