专栏首页健程之道JVM垃圾回收(上)

JVM垃圾回收(上)

Java 中的垃圾回收,常常是由 JVM 帮我们做好的。虽然这节省了大家很多的学习的成本,提高了项目的执行效率,但是当项目变得越来越复杂,用户量越来越大时,还是需要我们懂得垃圾回收机制,这样也能进行更深一步的优化。

辨别对象存亡

垃圾回收( Garbage Collection,以下简称 GC ),从字面上理解,就是将已经分配出去的,但却不再使用的内存回收回来,以便能够再次分配。

在 JVM 中,垃圾就是指的死亡对象所占据的堆空间( GC 是发生在堆空间中),那么我们如果辨别一个对象是否死亡呢?JVM 使用的是引用计数法可达性分析

引用计数法

引用计数法( Reference Counting),是为每个对象添加一个引用计数器,用来统计引用该对象的个数。一旦某个对象的引用计数器为0,则说明该对象已经死亡,便可以被回收了。

其具体实现为:

如果有一个引用,被赋值为某一对象,那么将该对象的引用计数器 +1。 如果一个指向某一对象的引用,被赋值为其他值,那么将该对象的引用计数器 -1。 也就是说,我们需要截获所有的引用更新操作,并且相应地增减目标对象的引用计数器。

看似很简单的实现,其实里面有不少缺陷:

  1. 需要额外的空间来存储计数器。
  2. 计数器的更新操作十分繁琐。
  3. 最重要的:无法处理循环引用对象。

针对第3点,举个例子特别说明一下:

假设对象 a 与 b 相互引用,除此之外没有其他引用指向他们。在这种情况下,a 和 b 实际上已经死了。

但由于它们的引用计数器皆不为0(因为相互引用,两者均为1),在引用计数法的计算中,这两个对象还活着。因此,这些循环引用对象所占据的空间将不可回收,从而造成了内存泄露

可达性分析

可达性分析( Reachability Analysis ),是目前 JVM 主要采取的判定对象死亡的方法。实质在于将一系列GC Roots作为初始的存活对象合集(live set),然后从该合集出发,探索所有能够被该集合引用到的对象,并将其加入到该集合中,这个过程我们也称之为标记(mark)。最终,未被探索到的对象便是死亡的,是可以回收的。

那么什么是GC Roots呢?我们可以暂时理解为由堆外指向堆内的引用,一般而言,GC Roots 包括(但不限于)如下几种:

  1. Java 方法栈桢中的局部变量
  2. 已加载类的静态变量
  3. JNI handles
  4. 已启动且未停止的 Java 线程

之前我们说引用计数法会有循环引用的问题,可达性分析就不会了。举例来说,即便对象 a 和 b 相互引用,只要从 GC Roots 出发无法到达 a 或者 b,那么可达性分析便会认为它们已经死亡。

可达性分析有没有什么缺点呢?有的,在多线程环境下,其他线程可能会更新已经分析过的对象中的引用,从而造成误报(将引用设置为 null)或者漏报(将引用设置为未被访问过的对象)。

误报并没有什么伤害,JVM 至多损失了部分垃圾回收的机会。漏报则比较麻烦,因为垃圾回收器可能回收事实上仍被引用的对象内存。一旦从原引用访问已经被回收了的对象,则很有可能会直接导致 JVM 崩溃。

STW

既然可达性分析在多线程下有缺点,那 JVM 是如何解决的呢?答案便是 Stop-the-world(以下简称JWT),停止了其他非垃圾回收线程的工作直到完成垃圾回收。这也就造成了垃圾回收所谓的暂停时间(GC pause)。

那 SWT 是如何实现的呢?当 JVM 收到 SWT 请求后,它会等待所有的线程都到达安全点(Safe Point),才允许请求 SWT 的线程进行独占的工作。

那什么又叫安全点呢?安全点是 JVM 能找到一个稳定的执行状态,在这个执行状态下,JVM 的堆栈不会发生变化。

这么一来,垃圾回收器便能够“安全”地执行可达性分析,所有存活的对象也都可以成功被标记,那么之后就可以将死亡的对象进行垃圾回收了。

总结

以上便是发现死亡对象的过程,这也为之后的垃圾回收进行铺垫,具体的垃圾回收过程,我会在下一篇文章中讲述,敬请期待。

本文分享自微信公众号 - 健程之道(JianJianCoder),作者:健健壮

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-10-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java 中的四种引用

    之前我们提到过 GC,但当 Java 中引用的对象越来越多,会导致内存空间不足,最终会产生错误 OutOfMemoryError,并让应用程序终止。那为什么 G...

    健程之道
  • JVM垃圾回收(下)

    接着上一篇,介绍完了 JVM 中识别需要回收的垃圾对象之后,这一篇我们来说说 JVM 是如何进行垃圾回收。

    健程之道
  • 设计模式——原型模式

    设计模式中,单例模式应该是大家最为熟悉的了,那如果我们需要对一个对象进行多次复制的话,大家会用什么呢?这就要用到今天要讲的原型模式了。

    健程之道
  • Android内存管理(垃圾回收算法相关)

    给对象添加一个引用计数器,每当有一个地方引用它的时候,计数器的值就加1;当引用失效的时候,计数器的值就减1;任何时刻计数器为0的对象是不可能再被引用的。

    Anymarvel
  • 垃圾收集策略静态内存分配和回收动态内存分配和回收1 Java堆内存的回收2 回收无效对象的过程3 方法区的内存回收4 垃圾收集算法5 Java中引用的种类

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

    之前我们介绍过 Java 内存运行时区域的各个部分,其中程序计数器、虚拟机栈、本地方法栈三个区域随线程共存亡。栈中的每一个栈帧分配多少内存基本上在类结构确定下来...

    java乐园
  • Android内存管理(七)垃圾回收算法相关

    给对象添加一个引用计数器,每当有一个地方引用它的时候,计数器的值就加1;当引用失效的时候,计数器的值就减1;任何时刻计数器为0的对象是不可能再被引用的。

    Anymarvel
  • Python的垃圾回收机制(引用计数+标

    我们都知道Python一种面向对象的脚本语言,对象是Python中非常重要的一个概念。在Python中数字是对象,字符串是对象,任何事物都是对象,而它们的核心就...

    py3study
  • Java 垃圾回收机制(早期版本)

    Java 垃圾回收机制在我们普通理解来看,应该视为一种低优先级的后台进程来实现的,其实早期版本的Java虚拟机并非以这种方式实现的。

    Rekent
  • 阿里字节跳动90%被问到的JVM面试题

    最近老是收到小伙伴的私信问我能不能帮忙整理出一份JVM相关的面试题出来,说自己在大厂去面试的时候这一块问的是特别多的,每次自己学的时候每次都学不到重点去。这不他...

    烂猪皮

扫码关注云+社区

领取腾讯云代金券