热门标签 | HotTags
当前位置:  开发笔记 > 人工智能 > 正文

详解Java虚拟机垃圾收集机制

这篇文章主要介绍了Java虚拟机垃圾收集机制的相关资料,帮助大家更好的理解和学习Java虚拟机的相关知识,感兴趣的朋友可以了解下

1 垃圾收集发生的区域

之前我们介绍过 Java 内存运行时区域的各个部分,其中程序计数器、虚拟机栈、本地方法栈三个区域随线程共存亡。栈中的每一个栈帧分配多少内存基本上在类结构确定下来时就已知,因此这几个区域的内存分配和回收都具有确定性,不需要考虑如何回收的问题,当方法结束或线程结束,内存自然也跟着回收了

而 Java 堆和方法区这两个区域则有显著的不确定性,只有在程序运行时我们才能知道程序究竟创建了哪些对象,创建了多少对象,所以这部分内存的分配和回收是动态的,垃圾收集器所关注的正是这部分内存该如何管理

2 如何判定需要被回收的对象?

如果一个对象没有被其他对象引用,则证明这个对象可以被回收,因为它已经没有实际用途了。那我们怎么去判断一个对象是否可回收呢?业界主要有两种判断方式:

1. 引用计数法
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值加一;当引用失效,计数器值减一;任何时刻计数器值都为零的对象就是不可能再被使用了。这种方法虽然会占用额外的内存空间用于计数,但它的原理简单,判定效率也高,大多数情况下它都是一个不错的算法。然而,这个看似简单的算法却需要考虑很多额外情况,否则将无法保证其正确工作,例如单纯的引用计数法就很难解决对象之间相互循环引用的问题

2. 可达性分析算法
该算法的基本思路是通过一系列称为 GC Roots 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程走过的路径称为引用链。如果某个对象到 GC Roots 间没有任何引用链相连,则证明此对象是不可能再被使用,可以回收

在 Java 技术体系中,可以作为 GC Roots 的对象包括:

  • 在虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中 JNI(即通常所说的 Native 方法)引用的对象
  • Java 虚拟机内部的引用,如基本数据类型对应的 Class 对象,一些常驻的异常对象(NullPointException、OutOfMemoryError)
  • 所有被同步锁持有的对象
  • 反映 Java 虚拟机内部情况的 JMXBean、JVMTI 中注册的回调、本地代码缓存等

除了这些固定的 GC Roots 集合外,根据用户所选用的垃圾收集器以及当前回收的内存区域的不同,还可以有其他对象临时加入,共同构成完整的 GC Roots 集合

3 四种引用类型

无论是通过引用计数法还是可达性分析算法,判断对象是否存活都和引用离不开关系。在 JDK1.2 以前,Java 里的引用是很传统的定义:如果 reference 类型的数据中存储的数值代表的是另外一块内存的起始地址,就称该 reference 数据是代表某块内存、某个对象的引用。这种定义当然没有什么不对,但现在看来显得太狭隘了,比如我们希望描述一类对象:当内存空间足够时,能保留在内存中,如果内存空间在进行了垃圾收集后仍然紧张,则可以抛弃这些对象,很多系统的缓存功能都符合这样的应用场景

JDK1.2 对引用的概念作了补充,将引用分为强引用(Strongly Reference)、软引用(SoftReference)、弱引用(Weak Reference)和虚引用(Phantom Reference),强度依次减弱

  • 强引用

形如 Object obj = new Object() 这种引用关系就是我们常说的强引用。无论什么情况,只要强引用关系存在,对象就永远不会被回收

  • 软引用

用来描述一些有用但非必须的对象。此类对象只有在进行一次垃圾收集仍然没有足够内存时,才会在第二次垃圾收集时被回收。JDK1.2 之后提供了 SoftReference 类来实现软引用

  • 弱引用

也是用来描述那些非必须对象,但它的强度比软引用更弱一些。被软引用关联的对象只能生存到下一次垃圾收集发生为止,当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。JDK1.2 之后提供了 WeakReference 类来实现软引用

  • 虚引用

最弱的一种引用关系,一个对象是否存在虚引用,丝毫不会对其生存时间造成任何影响,也无法通过虚引用来取得一个对象实例。设置虚引用关联的唯一目的就是让这个对象被回收时能收到一个系统通知。JDK1.2 之后提供了 PhantomReference 类来实现软引用

4 finalize() 方法

在可达性分析中被判定为不可达的对象,并不是立即赴死,至少要经历两次标记过程:如果对象在进行可达性分析后发现没有与 GC Root 相连接的引用链,那么它将被第一次标记,随后再进行一次筛选,筛选条件是对象是否有必要执行 finalize() 方法,如果对象没有覆盖 finalize() 方法或是 finalize() 方法已经被调用过,则都视为“没有必要执行”

如果对象被判定为有必要执行 finalize() 方法,那么该对象将会被放置在一个名为 F-Queue 的队列之中,并在稍后由一条由虚拟机自动创建的、低调度优先级的 Finalizer 线程去执行它们的 finalize() 方法。注意这里所说的执行是指虚拟机会触发这个方法开始运行,但并不承诺一定会等待它运行结束。这样做的原因是防止某个对象的 finalize() 方法执行缓慢,或者发生死循环,导致 F-Queue 队列中的其他对象永久处于等待状态

finalize() 方法是对象逃脱死亡命运的最后一次机会,稍后收集器将对 F-Queue 中的对象进行第二次小规模标记,如果对象希望在 finalize() 方法中成功拯救自己,只要重新与引用链上的任何一个对象建立关联即可,那么在第二次标记时它将被移出“即将回收”的集合;如果对象这时候还没有逃脱,那基本上就真的要被回收了

任何一个对象的 finalize() 方法都只会被系统自动调用一次,如果对象面临下一次回收,它的 finalize() 方法将不会再执行。finalize() 方法运行代价高,不确定性大,无法保证各个对象的调用顺序,因此已被官方明确声明为不推荐使用的语法

5 回收方法区

方法区的垃圾收集主要回收两部分:废弃的常量和不再使用的类型。判定一个常量是否废弃相对简单,与对象类似,只要某个常量不再被引用,就会被清理。而判定一个类型是否属于“不再被使用的类”的条件就比较苛刻了,需要同时满足下面三个条件:

  • 该类的所有实例都已经被回收,即 Java 堆中不存在该类及其任何派生子类的实例
  • 加载该类的类加载器已经被回收
  • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法再任何地方通过反射访问该类的方法

Java 虚拟机允许对满足上述三个条件的无用类进行回收,但并不是说必然被回收,仅仅是允许而已。关于是否要对类型进行回收,HotSpot 虚拟机提供了 -Xnoclassgc 参数进行控制

6 分代收集理论

当前商业虚拟机的垃圾收集器大多数都遵循了“分代收集”的设计理论,分代收集理论其实是一套符合大多数程序运行实际情况的经验法则,主要建立在两个分代假说之上:

  • 弱分代假说:绝大多数对象都是朝生夕灭的
  • 强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡

这两个分代假说共同奠定了多款常用垃圾收集器的一致设计原则:收集器应该将 Java 堆划分出不同的区域,将回收对象依据年龄(即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储,把存活时间短的对象集中在一起,每次回收只关注如何保留少量存活的对象,即新生代(Young Generation);把难以消亡的对象集中在一起,虚拟机就可以使用较低的频率来回收这个区域,即老年代(Old Generation)

正因为划出了不同的区域,垃圾收集器才可以每次只回收其中一个或多个区域,因此才有了“Minor GC”、“Major GC”、“Full GC”这样的回收类型划分,也才能够针对不同的区域采用不同的垃圾收集算法,因而有了“标记-复制”算法、“标记-清除”算法、“标记-整理”算法

分代收集并非只是简单划分一下内存区域,它至少存在一个明显的困难:对象之间不是孤立的,对象之间会存在跨代引用。假如现在要进行只局限于新生代的垃圾收集,根据前面可达性分析的知识,与 GC Roots 之间不存在引用链即为可回收,但新生代的对象很有可能会被老年代所引用,那么老年代对象将临时加入 GC Roots 集合中,我们不得不再额外遍历整个老年代中的所有对象来确保可达性分析结果的正确性,这无疑为内存回收带来很大的性能负担。为了解决这个问题,就需要对分代收集理论添加第三条经验法则:

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

存在互相引用的两个对象,应该是倾向于同时生存或同时消亡的,举个例子,如果某个新生代对象存在跨代引用,由于老年代对象难以消亡,会使得新生代对象同样在收集时得以存活,进而年龄增长后晋升到老年代,那么跨代引用也随之消除了。既然跨带引用只是少数,那么就没必要去扫描整个老年代,也不必专门记录每一个对象是否存在哪些跨代引用,只需在新生代上建立一个全局的数据结构,称为记忆集(Remembered Set),这个结构把老年代划分为若干个小块,标识出老年代的哪一块内存会存在跨代引用。此后当发生 Minor GC 时,只有包含了跨代引用的小块内存里的对象才会被加入 GC Roots 进行扫描

7 标记 - 清除算法
如其名,算法分为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成之后,统一回收所有被标记的对象,也可以反过来,标记存活的对象,统一回收所有未被标记的对象。标记过程就是对象是否属于垃圾的判定过程。标记 - 清除算法执行过程如图所示:

标记 - 清除算法是最基础的算法,后续的收集算法都是以标记 - 清除算法为基础,对其缺点进行改进,它的主要缺点有两个:

  • 执行效率不稳定

如果 Java 堆中包含大量对象且大部分需要回收,则必须进行大量标记和清除的动作‘

  • 内存空间碎片化问题

标记、清除之后会产生大量不连续的内存碎片,内存碎片太多会导致下次分配较大对象时无法找到足够的连续内存,从而不得不提前触发一次垃圾收集动作

8 标记 - 复制算法

为了解决标记 - 清除算法面对大量可回收对象时执行效率低的问题,复制算法将可用内存按容量划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了,就将还存活着的对象复制到另外一内存上,再把已使用过的内存空间一次清理掉

如果内存中多数对象都是存活的,这种算法无疑会产生大量内存间复制的开销,但对于多数对象都是可回收的情况,算法需要复制的就是占少数的存活对象,而且每次都是针对整个半区进行内存回收,分配内存时也不用考虑空间碎片的问题,只要移动堆顶指针,按顺序分配即可。不过这种算法的缺陷也显而易见,可用内存被缩小为原来的一半

标记 - 复制算法大多用于新生代。实际上,新生代中的对象大多数都熬不过第一轮收集,因此不需要按 1:1 的比例来划分新生代的内存空间。具体做法是将新生代划分为一块较大的 Eden 区和两块较小的 Survivor 区,每次分配只使用 Eden 区和其中一块 Survivor 区。发生垃圾收集时,将 Eden 区和 Survivor 区中仍然存活的对象一次性复制到另一个 Survivor 区,然后直接清理掉 Eden 区和已经用过的 Survivor 区。HotSpot 虚拟机默认 Eden 和 Survivor 的大小比例是 8:1:1

当 Survivor 空间不足以容纳一次 Minor GC 之后存活的对象时,就需要依赖其他内存区域(大多是老年代)进行分配担保,上一次新生代存活下来的对象直接进入老年代

9 标记 - 整理算法

标记 - 复制算法不适合用在对象存活率高的区域,而且会浪费一半的空间,因此老年代一般不采用这种算法,取而代之的是有针对性的标记 - 整理算法。标记 - 整理算法的标记过程与标记 - 清除算法一样,但后续步骤不是直接清理可回收对象,而是让所有存活对象都向内存空间的一侧移动,然后直接清理掉边界以外的内存

是否移动回收后的存活对象是一项优缺点并存的风险决策,尤其是在老年代这种每次回收都有大量对象存活的区域,移动存活对象并更新其引用将会是一个极为繁重的操作,必须暂停用户应用程序线程才能进行,像这样的停顿行为被称为“Stop the World”。但如果不考虑移动存活对象,又会影响内存分配和访问的效率,为此使用者必须小心权衡其中的得失。一种和稀泥式的解决方案就是让虚拟机平时采用标记 - 清除算法,直到内存空间碎片化程度大到影响对象分配时,再采用标记 - 整理算法收集一次,已获得规整的内存空间

以上就是详解Java 虚拟机垃圾收集机制的详细内容,更多关于java 虚拟机垃圾收集机制的资料请关注其它相关文章!


推荐阅读
  • 本文由编程笔记#小编为大家整理,主要介绍了logistic回归(线性和非线性)相关的知识,包括线性logistic回归的代码和数据集的分布情况。希望对你有一定的参考价值。 ... [详细]
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 近年来,大数据成为互联网世界的新宠儿,被列入阿里巴巴、谷歌等公司的战略规划中,也在政府报告中频繁提及。据《大数据人才报告》显示,目前全国大数据人才仅46万,未来3-5年将出现高达150万的人才缺口。根据领英报告,数据剖析人才供应指数最低,且跳槽速度最快。中国商业结合会数据剖析专业委员会统计显示,未来中国基础性数据剖析人才缺口将高达1400万。目前BAT企业中,60%以上的招聘职位都是针对大数据人才的。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • qt学习(六)数据库注册用户的实现方法
    本文介绍了在qt学习中实现数据库注册用户的方法,包括登录按钮按下后出现注册页面、账号可用性判断、密码格式判断、邮箱格式判断等步骤。具体实现过程包括UI设计、数据库的创建和各个模块调用数据内容。 ... [详细]
  • “你永远都不知道明天和‘公司的意外’哪个先来。”疫情期间,这是我们最战战兢兢的心情。但是显然,有些人体会不了。这份行业数据,让笔者“柠檬” ... [详细]
  • 生成对抗式网络GAN及其衍生CGAN、DCGAN、WGAN、LSGAN、BEGAN介绍
    一、GAN原理介绍学习GAN的第一篇论文当然由是IanGoodfellow于2014年发表的GenerativeAdversarialNetworks(论文下载链接arxiv:[h ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 无线认证设置故障排除方法及注意事项
    本文介绍了解决无线认证设置故障的方法和注意事项,包括检查无线路由器工作状态、关闭手机休眠状态下的网络设置、重启路由器、更改认证类型、恢复出厂设置和手机网络设置等。通过这些方法,可以解决无线认证设置可能出现的问题,确保无线网络正常连接和上网。同时,还提供了一些注意事项,以便用户在进行无线认证设置时能够正确操作。 ... [详细]
  • 本文介绍了游戏开发中的人工智能技术,包括定性行为和非定性行为的分类。定性行为是指特定且可预测的行为,而非定性行为则具有一定程度的不确定性。其中,追逐算法是定性行为的具体实例。 ... [详细]
  • JavaScript设计模式之策略模式(Strategy Pattern)的优势及应用
    本文介绍了JavaScript设计模式之策略模式(Strategy Pattern)的定义和优势,策略模式可以避免代码中的多重判断条件,体现了开放-封闭原则。同时,策略模式的应用可以使系统的算法重复利用,避免复制粘贴。然而,策略模式也会增加策略类的数量,违反最少知识原则,需要了解各种策略类才能更好地应用于业务中。本文还以员工年终奖的计算为例,说明了策略模式的应用场景和实现方式。 ... [详细]
author-avatar
手机用户2702932960
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有