深入理解jvm(三)之GC

大家好,我是三叔,很高兴这期又和大家见面了,一个奋斗在互联网的打工人。

这一期主要讲一下jvm的垃圾回收机制。

当程序运行时,内存总是被不断地分配和释放。如果内存管理不当,将导致内存泄漏、内存溢出等问题,因此在 Java 中,垃圾回收机制被引入以实现自动内存管理。在本篇博客中,我将介绍 JVM 的垃圾回收机制,主要回收的是堆中的内存对象。

如何判断对象已死?

  1. 引用计数算法:通过计算对象被引用的次数来判断是否需要释放对象,一个地方引用它时,计数器值就加一;当引用失效时,计数器就减一,但这种方式存在一个问题:如果两个对象相互引用,就会形成循环引用,导致内存泄漏。目前主流的jvm基本上都不采用这种技术算法来管理内存。
  2. 可达性分析算法:通过判断对象是否可以被访问来判断是否需要释放对象。如果一个对象不再被任何对象引用,即不可达,那么该对象就可以被释放。该方法是以“GC Roots” 的根对象作为起点,从根节点开始向下搜索,搜索的路径叫做“引用链”,如果某个对象到根节点没任何引用链相连,则根到这个对象不可达,因此会被判定为回收的对象。

如下图所示:右边的就是无法到达GC Roots根的位置,就被认为不可达,作为GC的对象。
在这里插入图片描述

那么哪些可以作为可达性分析算法的Roots?
  1. 栈中正在运行的方法所使用的参数、局部变量、临时变量等
  2. java类中引用类型的静态变量
  3. 字符串常量池(比如String str = “aabbcc”;)
  4. 本地方法引用的对象
  5. 被同步锁(synchronized)持有的对象
  6. Java中的基本数据类型对应的Class对象
  7. new 一个对象的强引用等

回收方法区

一般很少回收方法区的对象,因为出发回收方法区的条件在虚拟机中也是比较苛刻的,必须同时满足这三个条件:

  1. 该类所有的实例已经被回收
  2. 加载该类的类加载器已经被回收
  3. 该类对应的Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法

如果同时满足这三个条件,才会进行方法区的回收。

垃圾收集算法

既然确认了哪些对象已经“死亡”,是可以被回收的,那接下来就是要对这些死亡的对象进行GC,Java虚拟机提供了这些常见的垃圾收集算法:

  1. 分代收集算法:根据堆中划分的区域,譬如新生代、老年代。然后根据不同的区域进行相应的回收;
  2. 标记-复制算法:复制算法是一种将堆内存划分为两个区域的垃圾回收算法。这两个区域分别称为 from 区域和 to 区域。在垃圾回收期间,所有存活的对象都被复制到 to 区域中,from 区域则被清空。当 to 区域被填满后,JVM 将会清空 from 区域并将所有存活的对象复制到 from 区域中。
    复制算法的优点是可以避免内存碎片。但是,它的缺点是只能使用堆内存的一半。
  3. 标记-清除算法:标记-清除算法是一种最基本的垃圾回收算法。这种算法分为两个阶段:标记阶段和清除阶段。在标记阶段,垃圾回收器会遍历堆内存中的所有对象,并标记出所有不再被引用的对象。在清除阶段,垃圾回收器会扫描整个堆内存,将所有被标记的对象清除,并将它们所占用的内存释放。标记-清除算法的优点是简单和高效。但是,它的缺点是在清除阶段会产生大量的内存碎片。
  4. 标记-整理算法:在标记清除方法的基础上进行改进,标记整理是一种移动式的清除算法,让所有存活的对象向内存空间的一端移动,然后直接清理边界以外的内存。
跨代引用

在JVM垃圾回收的过程中,当对象在新生代被垃圾回收器回收时,如果对象在老年代仍被引用,则会形成跨代引用。因为新生代的对象会被频繁地创建和销毁,所以当新生代被回收时,如果老年代仍然存在对新生代对象的引用,那么这些对象就无法被回收。这就导致了跨代引用的产生。

存在相互应用的关系的对象分别在新生代和老年代,但是这种概率是极低的。不应该为了少量的跨代引用去扫描整个老年代,这种跨代引用基本上Java虚拟机都处理好了,它只需要维护一个“记忆集”,把老年代划分若干区域,标识哪一块区域存在跨代引用,这样相对扫描整个老年代来说是划算的。

什么是安全区域

在 Java 虚拟机中,安全域是垃圾回收器执行垃圾回收操作所需的一些先决条件。在垃圾回收器运行时,JVM 需要暂停所有执行 Java 代码的线程,以便进行垃圾回收。这个过程需要所有线程都停止运行并等待,以便垃圾回收器能够扫描整个堆以查找不再使用的对象,并回收它们的内存空间。

但是,如果某个线程正在执行一个阻塞 I/O 操作或其他一些不可中断的任务,那么这个线程就不能在安全域中停留。如果此时执行垃圾回收,这个线程就会被强制中断,从而导致 I/O 操作失败或其他问题。因此,为了保证垃圾回收的安全性和可靠性,JVM 会在安全点处执行垃圾回收操作。

在 JVM 中,安全域通常与代码的执行位置和 Java 方法的返回点相关联。具体来说,安全域包括以下几个方面:

  1. 方法调用:在调用 Java 方法时,线程会进入一个安全点,执行垃圾回收器操作。垃圾回收器将等待所有线程进入安全点后才能开始垃圾回收操作。
  2. 循环跳转:在 Java 代码中使用循环语句时,线程在每次循环迭代结束后都会进入一个安全点。在循环内部执行长时间运行的计算任务时,这个机制可以确保垃圾回收器有机会回收不再使用的对象。
  3. 同步点:在 Java 中,同步操作通常与安全点相关联。当线程等待进入同步块时,JVM 将把线程置于一个安全点,以便垃圾回收器可以执行垃圾回收操作。
  4. 异常处理:在捕获异常时,线程也会进入一个安全点。这可以确保在异常处理期间进行垃圾回收,以防止内存泄漏等问题。

总的来说,安全域是 JVM 垃圾回收器执行垃圾回收操作所需的一些前提条件,也是确保垃圾回收安全性和可靠性的重要机制。在垃圾回收器的实现中,安全域通常与代码的执行位置和 Java 方法的返回点相关联,确保所有线程都进入安全点后才能执行垃圾回收操作。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是三叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值