jvm的垃圾回收_垃圾回收机制原理

jvm的垃圾回收_垃圾回收机制原理通常,我们在写java程序的时候,似乎很少关注内存分配和垃圾回收的问题。因为,这部分工作,JVM已经帮我们自动实现了。这样看起来,好像很美好,但是任何事情都有两面性。虽然JVM会自动的进行垃圾回收,但是,如果遇到有些问题,JVM自己也处理不了呢?因此,我们需要了解一下JVM垃圾回收是怎样运作的,这样才能在遇到问题的时候,有的放矢。所以,今天就来聊一聊JVM的垃圾回收吧。首先,思考一下,为什么…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

通常,我们在写java程序的时候,似乎很少关注内存分配和垃圾回收的问题。因为,这部分工作,JVM已经帮我们自动实现了。

这样看起来,好像很美好,但是任何事情都有两面性。虽然JVM会自动的进行垃圾回收,但是,如果遇到有些问题,JVM自己也处理不了呢?

因此,我们需要了解一下JVM垃圾回收是怎样运作的,这样才能在遇到问题的时候,有的放矢。所以,今天就来聊一聊JVM的垃圾回收吧。

首先,思考一下,为什么需要进行垃圾回收?

我们知道,在创建对象的时候,Java会把对象的内容放到堆中。随着时间的推移,堆中的对象肯定会越来越多,但是,堆的大小是有限制的。如果,我们不进行垃圾回收,也就是把无用的对象进行清除和回收,那么JVM将不堪重负,最终导致内存泄漏。

既然我们需要进行垃圾回收,那么,首先得知道什么是垃圾。

在垃圾收集器对堆内存进行回收前,会先判断哪些对象还在“存活”,哪些对象已经“死去”(即不可能再被任何途径使用的对象),这些“死去”的对象,就是我们需要进行回收的垃圾。

那么,通过什么方式去判定是否为垃圾呢?(即判定对象是否存活)

引用计数算法(已淘汰)

引用计数算法,是指给对象中添加一个引用计数器,每当有一个地方引用它时,计数器的值就加1,当引用失效时,计数器的值就减1。当计数器值为0时,该对象就会被回收。

可以说,引用计数算法的实现非常简单,判定效率也很高。但是,我们忽略了一个问题,在Java中,对象之间是可以互相循环引用的。如果,两个对象之间互相循环引用,那么就会导致,它们之间的引用计数都不为0(都在等待对方释放资源),因此,就无法通知垃圾收集器回收它们。

可达性分析算法

这个算法的思想就是,通过一系列被称为“GC Roots”的对象作为起点,然后向下搜索,所走过的路径被称为引用链。当一个对象到 GC Roots之间没有任何引用链时(即从GC Roots到该对象不可达),则证明该对象是不可用的。

jvm的垃圾回收_垃圾回收机制原理

这个算法解决了循环引用的问题,只要对象无法与GC Root之间建立直接或间接的连接,就会判定为可回收对象。

那么,什么对象可以作为GC Root呢?一般分为以下四种:

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

既然已经确定了哪些垃圾可以被回收,那么就需要垃圾收集器进行垃圾回收了,我们来了解一下几种比较常见的的垃圾收集算法。

标记清除算法

file

是最基础的一种收集算法,分为标记和清除两个阶段。首先,把需要回收的对象标记出来,然后再把他们清除掉。如上图所示,所有可回收的对象会变成未使用的一片区域。

标记清除算法逻辑清晰,易于操作。但是,我们可以看到,未使用的内存区块都不是连续的,因此,此算法会产生很多的内存碎片。这样,当一些较大的对象需要分配空间的时候,就找不到足够的连续内存来存储,因此会提前触发GC,同时也浪费了很多的内存空间(内存空间太小,导致不可用)。

复制算法

file

复制算法,是指把内存区域划分为大小相等的两块区域。每次只使用其中的一块,当这一块内存用完了,就把所有存活的对象复制到另一块上面,最后再把已使用过的内存空间一次清理掉。

这样,就可以保证内存区域的连续性,不会产生内存碎片,实现简单,运行高效。但是,这样的话只有使用原来一半的内存,代价也太高了。

标记整理算法

file

标记整理算法,标记过程和标记清除算法一样,但是后续不是进行清除,而是先整理,让所有存活的对象都向一端移动,然后再清除另一端的内存区域。

标记整理算法解决了标记清除算法产生内存碎片的问题,同时也解决了复制算法只能利用一半内存的问题,看似是非常的完美。但是,它却产生了另外一个问题。可以看到图中,内存的变动非常频繁,每次整理都有很多存活的对象内存地址发生改变。因此,它的效率会慢很多。

所以,现在一般用分代收集算法。在Java堆中,分为新生代和老年代,可以根据各个代的特点,选择最合适的收集算法。新生代中,每次垃圾收集都有大批对象死去,只有少量对象存活,就可以选择复制算法,只需要付出少量存活对象的复制成本即可。而老年代中,对象存活率高,没有额外空间对它进行分配担保,因此使用标记清除或者标记整理算法。

堆内存模型

Java堆是内存管理中最大的一块区域,也是垃圾回收的重点区域。堆分为新生代、老年代和永久代,新生代又分为Eden区和Survivor区,Survivor又分为S0和S1区。在JDK1.8之后把永久代移除了,而用元空间代替。(永久代使用的是堆内存,而元空间直接使用本机物理内存)

file

新生代中的对象98%都是朝生夕死的,因此把新生代分为较大的一块Eden空间和两块较小的Survivor空间,每次使用Eden和其中的一块Survivor(此处Survivor区也叫From区,另一块空的未使用的空间叫To区,From和To区是会交换的,保证空的总是To区)。

当Eden区没有足够的空间分配时,会进行一次Minor GC,Eden区大部分对象都被回收,而Eden区和From区存活的对象会放入到To区,然后From和To区进行交换。(如果To区空间不够,直接进入老年代)

以下几种情况会进入老年代。

1) 大对象

大对象就是指需要大量连续内存空间的对象,最典型的就是那种很长的字符串和数组。大对象会直接进入到老年代,这样做的目的主要是为了避免新生代发生大量的内存复制(大对象的复制成本较高)。

2)长期存活的对象

虚拟机给每个对象都定义了一个对象年龄计数器。每当进行一次Minor GC,年龄就增加1岁,当年龄超过一定值时(默认是15,可以通过参数配置),就进入到老年代。

3)动态对象年龄判断

虚拟机并不要求对象年龄一定要到达15岁才进入到老年代。如果Survivor空间中有某年龄相同的所有对象大小总和大于Survivor空间的一半,则年龄大于等于该年龄的对象就会直接进入老年代。

空间分配担保

在发生Minor GC之前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果大于,那么Minor GC可以确保是安全的(因为,极端情况下,就算新生代所有对象都存活,也可以保证安全晋升到老年代)。否则,虚拟机会查看HandlePromotionFailure的值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小。如果大于,将尝试进行一次Minor GC(尽管有风险);如果小于或者HandlePromotionFailure设置不允许冒险,那么就先进行一次Full GC。

以上说的有风险,是因为取历次晋升到老年代对象的平均值这种方式只是经验值,并不能保证每次都能担保成功,如果担保成功还好,如果担保失败的话,依然需要进行Full GC。

尽管如此,我们最好还是打开HandlePromotionFailure开关,避免过多频繁的Full GC(因为Full GC的执行速度比Minor GC慢的多)。

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

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/194745.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 百度究竟是哪国的公司

    百度究竟是哪国的公司

    2021年11月14日
    167
  • 网络通信基础知识总结报告_数据通信与计算机网络知识点总结

    网络通信基础知识总结报告_数据通信与计算机网络知识点总结1.常见术语说明 数据载荷 在具有层次化结构的网络通信过程中,上层协议传递给下层协议的数据单元(报文)都可以称之为下一层协议的载荷数据。 报文 报文是网络交换与传输的数据单元,它具有一定的内在格式,并通常都具有头部+数据载荷+尾部的基本结构。在传输过程中,报文的格式和内容可能会发生改变。 头部 …

    2022年9月21日
    2
  • java从入门到精通_学习Java最好的10本书,从入门到精通

    java从入门到精通_学习Java最好的10本书,从入门到精通在当代,学习Java等编程课程的主要方式是视频资源。如果你想学,在网上五分钟之内就可以找到一堆学习视频,瞬间将你的硬盘填满。但是这些课程质量良莠不齐,对于小白来说,的确让人头痛不已。但是,书籍不同。对于书籍而言,它们都是出自业内大牛和资深的大学教授的精心编写,内容好坏与否,有很多同领域的网友都能帮你把关。所以说,如果你选对了学习的书籍,就可以不用担心自己在编程中,埋下错误的种子,同时还可以更深入的…

    2022年7月8日
    16
  • Pytest(5)美化插件进度条pytest-sugar

    Pytest(5)美化插件进度条pytest-sugar前言在我们进行自动化测试的时候,用例往往是成百上千,执行的时间是几十分钟或者是小时级别。有时,我们在调试那么多用例的时候,不知道执行到什么程度了,而pytest-sugar插件能很好解决我们的痛点。

    2022年7月29日
    9
  • YouTube为什么打不开?以及简便的訪问的方法/解决方式!

    YouTube为什么打不开?以及简便的訪问的方法/解决方式!

    2021年12月6日
    387
  • db2 日志管理

    db2 日志管理DB2日志是以文件的形式存放在文件系统中,分为两种模式:循环日志和归档日志。当创建新数据库时,日志的缺省模式是循环日志。在这种模式下,只能实现数据库的脱机备份和恢复。如果要实现联机备份和恢复,必须设为归档日志模式。

    2022年6月13日
    29

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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