对象内存回收

垃圾回收

垃圾收集主要是针对堆和方法区进行。程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后就会消失,因此不需要对这三个区域进行垃圾回收。

判断一个对象是否可被回收

要对垃圾进行回收,首先得判断这个对象是否可以被回收,有两种方法来判断:

引用计数法

给对象添加一个引用计数器,当对象增加一个引用时计数器加 1,引用失效时计数器减 1。引用计数为 0 的对象可被回收。
在两个对象出现循环引用的情况下,此时引用计数器永远不为 0,导致无法对它们进行回收。正是因为循环引用的存在,因此 Java 虚拟机不使用引用计数算法。

可达性分析算法

以 GC Roots 为起始点向下搜索,搜索所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连,证明此对象是不可的。

GC Roots根节点:线程栈的本地变量、静态变量、本地方法栈的变量等等

常见引用类型

java的引用类型一般分为四种:强引用软引用、弱引用、虚引用

  • 强引用:普通的变量引用
1
public static User user = new User(); 
  • 软引用:将对象用SoftReference软引用类型的对象包裹,正常情况不会被回收,但是GC做完后发现释放不出空间存放新的对象,则会把这些软引用的对象回收掉。软引用可用来实现内存敏感的高速缓存。
1
public static SoftReference<User> user = new SoftReference<User>(new User()); 
  • 软引用在实际中有重要的应用,例如浏览器的后退按钮。按后退时,这个后退时显示的网页内容就可以使用软引用存储。

​ (1)如果一个网页在浏览结束时就进行内容的回收,则按后退查看前面浏览过的页面时,需要重新构建

​ (2)如果将浏览过的网页存储到内存中会造成内存的大量浪费,甚至会造成内存溢出

  • 弱引用:将对象用WeakReference软引用类型的对象包裹,弱引用跟没引用差不多,GC会直接回收掉,很少用

    1
    public static WeakReference<User> user = new WeakReference<User>(new User()); 
  • 虚引用:虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系,几乎不用

finalize()方法

从Java9开始,finalize方法已被标注为@Deprecated,也就是过期了

即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历再次标记过程。

标记的前提是对象在进行可达性分析后发现没有与GC Roots相连接的引用链。

1. 第一次标记并进行一次筛选。

筛选的条件是此对象是否有必要执行finalize()方法。

当对象没有覆盖finalize方法,对象将直接被回收。

2. 第二次标记

如果这个对象覆盖了finalize方法,finalize方法是对象脱逃死亡命运的最后一次机会,如果对象要在finalize()中成功拯救自己,只要重新与引用链上的任何的一个对象建立关联即可,譬如把自己赋值给某个类变量或对象的成员变量,那在第二次标记时它将移除出“即将回收”的集合。如果对象这时候还没逃脱,那基本上它就真的被回收了。

注意:一个对象的finalize()方法只会被执行一次,也就是说通过调用finalize方法自我救命的机会就一次。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class User {
public int id;

public User(int id) {
this.id=id;
}

@Override
protected void finalize() throws Throwable {
System.out.println("id"+id);
super.finalize();
}
}
1
2
3
4
5
6
7
8
9
10
11
public class OOMTest {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
int i = 0;
int j = 0;
while (true) {
list.add(new User(i++));
new User(j--);
}
}
}

image-20230228234402806

可以看出,id为负数的user实例,也就是需要回收的对象,是会调用finalize()方法的!

如何判断一个类是无用的类

方法区主要回收的是无用的类,那么如何判断一个类是无用的类的呢? 类需要同时满足下面3个条件才能算是 “无用的类”

  • 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。

  • 加载该类的 ClassLoader 已经被回收。 这点就使得大部分的类都没办法回收,因为类加载器很难被回收,自定义类加载器会有可能。

  • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。


对象内存回收
http://example.com/对象内存回收/
作者
Panyurou
发布于
2022年6月24日
许可协议