Java:这个对象还活着吗

背景

我们知道,程序运行时会不断创建对象,对象的存储是需要消耗内存。C/C++它们每次都需要手动释放内存,但是很多人用了很久的Java可能会好奇,为什么Java不需要手动释放内存呢?
究其原因就是C/C++的手动释放内存的方式一定程度上很考验开发者的水平和细心,如果哪里忘记了释放内存,可能造成灾难影响。火狐浏览器换Rust也是因为这个原因,在之前发现的大部分漏洞都是因为内存问题。
Java设计者在设计之初就避免了这个问题,将内存管理交给JVM管理,开发者不需要再显式释放内存。我们把这个叫垃圾回收,顾名思义只有垃圾对象才会被回收,我们不能把存活的对象也给回收了,不然就出大问题。
今天我们就来探讨一下,哪些对象是垃圾?哪些对象是存活的?他们又是怎么识别的呢?Java里主要是两个方面,一个是可达性分析,一个是引用类型

可达性分析

可达性分析从名字上理解,就是分析是否可以到达,到达的是什么呢?到达的就是那些基础的存活对象,那么哪些是基础存活对象呢?我们可以自己想象一下,这些对象肯定是我们系统当前运行必须的、或者正在使用的?哪些是必须的呢?最容易想到的是静态变量、常量等,哪些是正在使用呢?我们知道虚拟机运行的时候有虚拟机栈和本地方法栈,这些引用的对象就是正在使用的。为了方便,大家都称这些对象为GC Roots。下面详细介绍一下哪些对象属于GC Roots

GC Roots
  • 栈帧中本地变量表引用的对象,被调用方法的参数、局部变量、临时变量等
  • 方法区类静态属性引用的对象,比如Java类中引用类型静态变量
  • 方法区中常量引用的对象,比如字符串常量池里的引用
  • 本地方法栈中的JNI引用的对象
  • 虚拟机内部的引用,比如基本数据类型对象的class对象、常驻的异常对象、系统类加载器
  • 所有被同步锁持有的对象
  • 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存
可达性遍历

不知道大家有没有学过UML建模,我们在画UML时经常存在一个类关联其他类的情况。如果你没有学过,下边给一个例子让大家看看。很简单,就是一个书本管理网站,我之前自己做的,收藏自己的收集的书籍。application层关联了两个仓储层,一个是Book的,一个是Note(这个是我会在阅读书的时候会记录markdown的笔记)的仓储,然后仓储层又关联mapper,如果我们把整个实例化,那么就是一个bookApp对象持有一个bookRepository对象和一个noteRepository对象,bookRepository持有一个bookMapper对象,noteRepository持有一个noteMapper对象。
在这里插入图片描述

如果我们实例化这些类后,他们之间就可能存在这样的一个关系,你可以想象一下,如果成千上万类似这样的对象相互关联,如果我们把他们全部用边把他们串起来,他们是什么呢?是不是一张有向图,我们学过算法的话,其实给定一个节点遍历和它相联的节点,我们是不是可以选择深度优先遍历和广度优先遍历。
现在我们将GC Roots作为根节点,我们对这个图做遍历,那么可以从根节点遍历到的节点对象说明他们还是存活的。如果不能到达那么就说明它已经没用了。这里给一个例子,大家可以直观的看看。绿色是gc roots;蓝色是存活的对象;灰色是不可达的对象,说明它们已经寿终正寝了
在这里插入图片描述

引用类型

上边我们说了标记存活对象是靠可达性分析,那么可达的对象就一定不会被回收吗?答案是不一定,只有强引用才是确定不会被回收的,其他还有三种引用那就不一定了,接下来简单介绍一下这些引用

强引用

这个没啥好说的我们平时见得最多的就是这种,比如我们用new新建一个然后赋值给一个变量,就是建立一个强引用关系。如果我们把book指向null,那么新创建饿Book对象就没有引用指向它,那么下一次GC时就会被回收

Book book = new Book();

book = null;
软引用

软引用主要用来描述一些还有用,但是非必需的,在系统将要发生内存溢出时,会把这些对象列入二次回收,如果还是内存不足,才会抛出内存溢出异常。当JVM准备回收执行finalize方法前加入ReferenceQueue队列

弱引用

弱引用也是用来描述一些还有用但是非必需的,不过它的强度比软引用更弱,被弱引用关联的对象只能存活到下一次垃圾回收,无论内存是否充足都会被回收。当JVM准备回收执行finalize方法前加入ReferenceQueue队列

虚引用

一个对象是否有虚引用存在完全对它的生存时间没有任何影响,也无法通过虚引用获取一个对象。为一个对象设置虚引用关联的唯一目的就是在对象被回收后能有一个系统通知。虚引用关联对象被真正回收时才会加入ReferenceQueue队列

总结

从上边我们看到了,标记一个对象是否需要被回收是通过可达性分析和引用类型决定的。这里还有一个小trick,被标记为被回收的对象也可能复活,如果在finalize方法中重新建立和引用链的关联,那么这个对象就不会被回收了,不过finalize方法早就被标记为不推荐使用了。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值