首先是API文档中,java.lang.ref的解释:
PhantomReference<T> | 虚引用对象,在回收器确定其指示对象可另外回收之后,被加入队列。 |
Reference<T> | 引用对象的抽象基类。 |
ReferenceQueue<T> | 引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到该队列中。 |
SoftReference<T> | 软引用对象,在响应内存需要时,由垃圾回收器决定是否清除此对象。 |
弱引用对象,它们并不禁止其指示对象变得可终结,并被终结,然后被回收。 |
可到达性
从最强到最弱,不同的可到达性级别反映了对象的生命周期。在操作上,可将它们定义如下:
- 如果某一线程可以不必遍历所有引用对象而直接到达一个对象,则该对象是强可到达 对象。新创建的对象对于创建它的线程而言是强可到达对象。
- 如果一个对象不是强可到达对象,但通过遍历某一软引用可以到达它,则该对象是软可到达 对象。
- 如果一个对象既不是强可到达对象,也不是软可到达对象,但通过遍历弱引用可以到达它,则该对象是弱可到达 对象。当清除对某一弱可到达对象的弱引用时,便可以终止此对象了。
- 如果一个对象既不是强可到达对象,也不是软可到达对象或弱可到达对象,它已经终止,并且某个虚引用在引用它,则该对象是虚可到达 对象。
- 最后,当不能以上述任何方法到达某一对象时,该对象是不可到达 对象,因此可以回收此对象。
几种引用:
我们都知道 JVM 中对象是被分配在堆(heap)上的,当程序行动中不再有引用指向这个对象时,这个对象就可以被垃圾回收器所回收。这里所说的引用也就是我们一般意义上申明的对象类型的变量(如 String, Object, ArrayList 等),区别于原始数据类型的变量(如 int, short, long 等)也称为强引用。
在了解虚引用之前,我们一般都是使用强引用来对对象进行引用。如:
String tag = new String("T"); |
此处的 tag 引用就称之为强引用。而强引用有以下特征:
- 强引用可以直接访问目标对象。
- 强引用所指向的对象在任何时候都不会被系统回收。
- 强引用可能导致内存泄漏。
我们要讨论的这三种 Reference 较之于强引用而言都属于“弱引用”,也就是他们所引用的对象只要没有强引用,就会根据条件被 JVM 的垃圾回收器所回收,它们被回收的时机以及用法各不相同。下面分别来进行讨论。
SoftReference 在“弱引用”中属于最强的引用。SoftReference 所指向的对象,当没有强引用指向它时,会在内存中停留一段的时间,垃圾回收器会根据 JVM 内存的使用情况(内存的紧缺程度)以及 SoftReference 的 get() 方法的调用情况来决定是否对其进行回收。(后面章节会用几个实验进行阐述)
具体使用一般是通过 SoftReference 的构造方法,将需要用弱引用来指向的对象包装起来。当需要使用的时候,调用 SoftReference 的 get() 方法来获取。当对象未被回收时 SoftReference 的 get() 方法会返回该对象的强引用。如下:
SoftReference<Bean> bean = new SoftReference<Bean>(new Bean("name", 10)); System.out.println(bean.get());// “name:10” |
- 软引用使用 get() 方法取得对象的强引用从而访问目标对象。
- 软引用所指向的对象按照 JVM 的使用情况(Heap 内存是否临近阈值)来决定是否回收。
- 软引用可以避免 Heap 内存不足所导致的异常。
当垃圾回收器决定对其回收时,会先清空它的 SoftReference,也就是说 SoftReference 的 get() 方法将会返回 null,然后再调用对象的 finalize() 方法,并在下一轮 GC 中对其真正进行回收。
WeakReference 是弱于 SoftReference 的引用类型。弱引用的特性和基本与软引用相似,区别就在于弱引用所指向的对象只要进行系统垃圾回收,不管内存使用情况如何,永远对其进行回收(get() 方法返回 null)。
完全可以通过和 SoftReference 一样的方式来操作 WeakReference,这里就不再复述。
弱引用有以下特征:
- 弱引用使用 get() 方法取得对象的强引用从而访问目标对象。
- 一旦系统内存回收,无论内存是否紧张,弱引用指向的对象都会被回收。
- 弱引用也可以避免 Heap 内存不足所导致的异常。
PhantomReference 是所有“弱引用”中最弱的引用类型。不同于软引用和弱引用,虚引用无法通过 get() 方法来取得目标对象的强引用从而使用目标对象,观察源码可以发现 get() 被重写为永远返回 null。
那虚引用到底有什么作用?其实虚引用主要被用来 跟踪对象被垃圾回收的状态,通过查看引用队列中是否包含对象所对应的虚引用来判断它是否 即将被垃圾回收,从而采取行动。它并不被期待用来取得目标对象的引用,而目标对象被回收前,它的引用会被放入一个 ReferenceQueue 对象中,从而达到跟踪对象垃圾回收的作用。
所以具体用法和之前两个有所不同,它必须传入一个 ReferenceQueue 对象。当虚引用所引用对象被垃圾回收后,虚引用会被添加到这个队列中。如:
public static void main(String[] args) { ReferenceQueue<String> refQueue = new ReferenceQueue<String>(); PhantomReference<String> referent = new PhantomReference<String>( new String("T"), refQueue); System.out.println(referent.get());// null System.gc(); System.runFinalization(); System.out.println(refQueue.poll() == referent); //true } |
值得注意的是,对于引用回收方面,虚引用类似强引用不会自动根据内存情况自动对目标对象回收,Client 需要自己对其进行处理以防 Heap 内存不足异常。
虚引用有以下特征:
- 虚引用永远无法使用 get() 方法取得对象的强引用从而访问目标对象。
- 虚引用所指向的对象在被系统内存回收前,虚引用自身会被放入 ReferenceQueue 对象中从而跟踪对象垃圾回收。
- 虚引用不会根据内存情况自动回收目标对象。
另外值得注意的是,其实 SoftReference, WeakReference 以及 PhantomReference 的构造函数都可以接收一个 ReferenceQueue 对象。当 SoftReference 以及 WeakReference 被清空的同时,也就是 Java 垃圾回收器准备对它们所指向的对象进行回收时,调用对象的 finalize() 方法之前,它们自身会被加入到这个 ReferenceQueue 对象
中,此时可以通过 ReferenceQueue 的 poll() 方法取到它们。而 PhantomReference 只有当 Java 垃圾回收器对其所指向的对象真正进行回收时,会将其加入到这个 ReferenceQueue 对象
中,这样就可以追综对象的销毁情况。
下表对于各种引用类型的特征进行了小结:
强引用 | 直接调用 | 不回收 | 可能 |
软引用 | 通过 get() 方法 | 视内存情况回收 | 不可能 |
弱引用 | 通过 get() 方法 | 永远回收 | 不可能 |
虚引用 | 无法取得 | 不回收 | 可能 |
注意:
如果想使用这些相对强引用来说较弱的引用来进行对象操作的时候,就必须保证没有强引用指向被操作对象。否则将会被视为强引用指向,不会具有任何的弱引用的特性。
下一章我们将做 2 个实验来佐证上面这些总结的内容。
StrongReference, SoftReference, WeakReference 以及 PhantomReference 的各种特性实验分析
为了更好的描述它们的特性,先以表格进行归纳,再以示例程序加以说明。
- JVM 使用 Oracle 的 Java SE6
- 首先将 JVM 运行环境的初始以及最大 Heap 数设到最低以便更明显的看出结果:
软引用测试代码:
package test.reference; public class Bean { private String name; private int age; public Bean(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Bean: name=" + this.name + ", age=" + age; } }
package test.reference; import java.lang.ref.SoftReference; /** * 软引用使用 get() 方法取得对象的强引用从而访问目标对象。 * 软引用所指向的对象按照 JVM 的使用情况(Heap 内存是否临近阈值)来决定是否回收。 * 软引用可以避免 Heap 内存不足所导致的异常。 * */ public class TestSoftReference { public static void main(String[] args) { SoftReference<Bean> bean = new SoftReference<Bean>(new Bean("name", 10)); System.out.println(bean.get());// "Bean: name=name, age=10" System.gc(); System.out.println(bean.get());// "Bean: name=name, age=10" 在heap内存没有用完之前,GC不回收SoftReference } }
测试 虚引用
package test.reference; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; /** * 虚引用有以下特征: * 虚引用永远无法使用 get() 方法取得对象的强引用从而访问目标对象。 * 虚引用所指向的对象在被系统内存回收前,虚引用自身会被放入 ReferenceQueue 对象中从而跟踪对象垃圾回收。 * 虚引用不会根据内存情况自动回收目标对象。 * 另外值得注意的是,其实 SoftReference, WeakReference 以及 PhantomReference 的构造函数都可以接收一个 ReferenceQueue 对象。 * 当 SoftReference 以及 WeakReference 被清空的同时,也就是 Java 垃圾回收器准备对它们所指向的对象进行回收时, * 调用对象的 finalize() 方法之前,它们自身会被加入到这个 ReferenceQueue 对象中, * 此时可以通过 ReferenceQueue 的 poll() 方法取到它们。 * 而 PhantomReference 只有当 Java 垃圾回收器对其所指向的对象真正进行回收时, * 会将其加入到这个 ReferenceQueue 对象中,这样就可以追综对象的销毁情况。 * */ public class TestPhantomReference { public static void main(String[] args) { ReferenceQueue<Bean> refQueue = new ReferenceQueue<Bean> (); PhantomReference<Bean> referent = new PhantomReference<Bean>(new Bean("Jack",29),refQueue); System.out.println(referent.get()); //虚引用PhantomReference的get()永远返回null System.gc(); System.runFinalization(); System.out.println(refQueue.poll()==referent);//true //当调用了gc()和runFinalization()后才返回true,否则返回false } }
相关推荐
NULL 博文链接:https://jackyin5918.iteye.com/blog/1882071
maven-resources-production java.lang.NegativeArraySizeException java.lang.NegativeArraySizeException 问题解决
java.lang.NoSuchFieldError: Companion 问题的解决方案
其中,“java.lang.OutOfMemoryError: Java heap space”是一种常见的异常情况,它表明Java虚拟机(JVM)的堆内存空间已耗尽。 #### 标题和描述中的知识点详解 **标题:“java错误处理:java.lang.OutOfMemoryError:...
Java程序在运行过程中可能会遇到各种异常,其中"nested exception is java.lang.OutOfMemoryError: Java heap space"是一个常见的问题,通常发生在程序试图分配超过堆内存限制的空间时。这个错误表明Java虚拟机(JVM...
"Java.lang.OutOfMemoryError: Java heap space 解决...Java.lang.OutOfMemoryError: Java heap space 是一种常见的 Java 错误,解决该问题需要从多方面入手,包括调整 JVM 的堆大小、调整 Tomcat 的配置、优化程序等。
java.lang.OutOfMemoryError是Java虚拟机(JVM)中的一种常见错误,发生这种错误时,JVM将无法继续运行,程序将崩溃。这种错误的出现通常是由于Jvm的内存不足或内存泄露导致的。 出现java.lang.OutOfMemoryError的...
idea启动项目报错 java.lang.NegativeArraySizeException解决方法
标题 "java.lang.Exception: java.lang.IllegalArgumentException: firstMovedIndex, lastMove" 描述了一个Java编程中的异常情况。这个异常通常发生在尝试执行一个不合法的操作时,例如数组或集合操作超出了其边界。...
标题中的问题“scrcpy投屏 AssertionError: java.lang.reflect.InvocationTargetException”是用户在尝试使用Scrcpy时遇到的一个常见错误。这个错误通常意味着在执行某个方法时,Java运行时环境遇到了未预期的情况。...
在Java编程中,`java.lang.StackOverflowError` 是一个常见的运行时异常,它通常发生在程序执行过程中,当Java虚拟机(JVM)的调用栈溢出时。调用栈是每个线程用来存储方法调用信息的数据结构,当递归调用过深或者...
Java中的`java.lang.UnsatisfiedLinkError`是一个常见的运行时异常,通常出现在Java试图加载本地(C或C++)库但找不到相应的库文件时。这个错误可能是由于多种原因引起的,如库路径设置不正确、库文件不存在或者版本...
解决 java.lang.NoSuchFieldError: STRING at org.jbpm.identity.hibernate.PermissionUserType. 不用jbpm的jbpm-identity.jar 用这个就好
《java_lang包详解》 Java语言的核心库之一就是java.lang包,它包含了Java程序中最基础、最核心的类和接口。由于这个包是自动导入到每个Java程序中的,所以理解和掌握其中的类和接口对于编写Java代码至关重要。下面...
Java中的`java.lang.OutOfMemoryError`是一种常见的运行时错误,通常表示应用程序在尝试分配内存时遇到了问题。根据提供的信息,这个错误主要涉及到两个方面:`PermGen space`和`Java heap`,并且与Tomcat服务器相关...
Java.lang.NullPointerException 是一种常见的错误,它可以通过配置 JAVA 环境、检查源码中的包、导入数据库文件、添加数据源、检查数据库源文件的系统类型、检查 JDBC 驱动文件和升级 SQL Server 来解决。
Java编程语言的基础构建块之一是`java.lang`包,它被自动导入到每个Java程序中,无需显式导入。这个包包含了许多核心类和接口,是编写任何Java应用程序不可或缺的部分。`java.lang`包中最基本的类是`Object`,它是...