强引用 vs 非强引用
首先明晰一下是否强引用的区别,请看如下代码:
Object obj = new Object();
如上代码执行之后,obj保留了新创建对象的“强引用”,作用是使得垃圾回收器侦测对象是否可回收的时候,能够知道此对象正在被使用,而不能够被释放。
有了强引用,自然会引出非强引用,在往下说之前,马背想和大家先思考一下为什么需要非强引用:既然我们知道强引用是让垃圾回收器不回收对象,而有限的内存在大量对象不被释放时,容易引发OutOfMemory错误,导致服务崩溃,到此,我们大约呈现出一个需求:在我们的程序里,可能设计出一些对象的生存周期是memory sensitive的,即如果内存足够,则对象不被释放,否则释放这些对象,但需要一种机制/架构让程序能够判断其状态,从而在程序中做出下一步的动作,比如重新创建/加载对象等。
对于上面这种需求,要求我们首先了解jvm中的垃圾回收器是如何判断对象是否可以回收的。
通过翻阅文档,我们可以知道垃圾回收器是根据对象的可到达性来判断其是否可以回收。这种可到达性,代表了jvm中对象的被引用的状态,如本文开头的强引用,即对应着强可到达对象。具体请详见:
http://gceclub.sun.com.cn/Java_Docs/jdk6/html/zh_CN/api/java/lang/ref/package-summary.html
每一种可达性状态,在程序中都有相应的方式来实现,马背在下面制作了一个表格,从上到下代表
从强到弱的过程:
可到达性 | 含义 | 在lang.ref包中对应的对象 | 程序创建方式 |
强可到达对象 | 有强引用的对象 | 无 | 像文章开头那样:Object obj = new Object() |
软可到达对象 | 没有强引用,只有软引用的对象 | SoftReference | sr = new SoftReference(obj) 且 obj = null; |
弱可到达对象 | 没有强/软引用,只有弱引用的对象 | WeakReference | wr = new WeakReference(obj) 且 obj = null; |
虚可到达对象 | 没有强/软/弱引用,只有虚引用的对象 | PhantomReference | pr = new PhantomReference(obj) 且 obj = null; |
不可到达对象 | 无任何引用的对象 | 无 | 强引用可调用obj = null;软/弱/虚引用可调用.clear()方法释放掉对象 |
下图是java.lang.ref包的类图
从上图中看到整个包结构非常清晰,Reference类做为一个抽象父类,定义了非强引用的几个典型行为:get,clear,isEnqueue,enqueue。而软/弱/虚引用分别用三个子类来实现,这里还有2个类是不对ref包外开放的:FinalReference和Finalizer。
下面我们对比下软/弱/虚三种引用的区别:
非强引用类型 | 作用 |
SoftReference | 软引用类型,当对象的可到达性是软可到达时候,对象是否被释放取决于垃圾回收器当时的判断,也就是说并不是遇到垃圾回收,软引用对象就一定会被释放。 |
WeakReference | 弱引用类型,当对象是弱可到达时,垃圾回收器将释放此对象。但是注意垃圾回收器可能要运行多次才能找到并释放弱可及对象。 |
PhantomReference | 虚引用类型,一种"假"的非强引用。当垃圾回收器判断其引用的对象为"虚可达到对象"时候,只是将虚引用对象放入ReferenceQueue中,但并不回收对象(但对象的finalize方法会被执行),直到对象进一步弱化到达“不可及对象”才会被回收。这样就给开发者提供了一种在对象被回收释放之前进行某种操作的机会,且不同于利用finalize方法这种方式,此外虚引用对象还有两个特点: (A) 构建函数必须传入一个ReferenceQueue对象。(B) get方法永远只返回null。 |
ok,我们来做个例子:
public class Entry {
private String name;
public Entry(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void finalize(){
System.out.println("==finalize:"+name);
}
}
public class TestRef {
public static void main(String[] argc) {
TestRef.testSoftReference();
TestRef.testWeakReference();
TestRef.testPhantomRefence();
}
private static void testPhantomRefence() {
Entry e = new Entry("phantom test ojbect");
ReferenceQueue rq = new ReferenceQueue();
PhantomReference pr = new PhantomReference(e, rq);
//phantomReference.get永远返回null
System.out.println("=PhantomReference.get()=" + pr.get());
//首先释放强引用
e = null;
//占用一些内存:)
Entry[] tempMemory = new Entry[78500]; //115555
for (int i = 0; i < 78500; i++) {
tempMemory[i] = new Entry(String.valueOf(i));
}
System.gc();
System.out.println("=PhantomReference.get()=" + pr.get());
System.out.println("=PhantomReference.isEnqueued()=" + pr.isEnqueued());
//如果之前已经显示执行了Entry的finalize(),则此处一定返回值
// 因为PhantomReference的enqueue是发生在finalize方法执行之前
System.out.println("=ReferenceQueue.poll()=" + rq.poll());
}
private static void testWeakReference() {
Entry e = new Entry("weak ojbect");
ReferenceQueue rq = new ReferenceQueue();
WeakReference wr = new WeakReference(e, rq);
//首先释放强引用
e = null;
System.gc();
System.out.println("=WeakReference.get()=" + wr.get());
System.out.println("=ReferenceQueue.poll()=" + rq.poll());
}
private static void testSoftReference() {
Entry e = new Entry("soft ojbect");
ReferenceQueue rq = new ReferenceQueue();
SoftReference sr = new SoftReference(e, rq);
//首先释放强引用
e = null;
//占用一些内存:)
Entry[] tempMemory = new Entry[78500]; //115555
for (int i = 0; i < 78500; i++) {
tempMemory[i] = new Entry(String.valueOf(i));
}
System.gc();
System.out.println("=SoftReference.get()=" + sr.get());
System.out.println("=ReferenceQueue.poll()=" + rq.poll());
}
}
好了,马背这里的运行环境为:Ubuntu 9.10
Sun JDK1.6.0_18
程序运行时候设置:java -Xms4097k -Xmx4097k
可以发现运行特性基本与上面的描述相同,只有一点需要注意:软/弱引用的被放入到ReferenceQueue中是发生在对象的finalize方法调用之后,而虚引用是在finalize调用之前。
在本文的参考资料中,例举了一种不正确的恢复weakReference对象的方式,摘录如下:
obj = wr.get();
if (obj == null)
{
wr = new WeakReference(recreateIt()); //1
obj = wr.get(); //2
}
//code that works with obj
错误的原因在如果在1,2步骤之间发生垃圾回收,则不能保证obj对象一定会被赋值正确。
参考资料:
- https://www.ibm.com/developerworks/cn/java/j-refs/
- http://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html
- 大小: 9.4 KB
分享到:
相关推荐
Java 创建类和类的引用文档 本文档主要介绍了 Java 中的类和类的引用,通过一个学生信息系统的示例,展示了 Java 中类的定义、实例化和引用。 一、Java 中的类 在 Java 中,类是对象的模板,定义了对象的属性和...
在Java中,有两类数据类型:基本类型(如int、char)和引用类型(如类实例、数组)。对于基本类型,Java采用值传递的方式,而对于引用类型,则是传递引用的副本。 1. 值传递与引用传递的区别: - **值传递**:当...
### Java中对象与引用 在Java编程中,深入理解对象与引用的概念对于掌握面向对象编程至关重要。本文将从以下几个方面详细解析对象与引用的基本概念及其相互间的关系。 #### 一、对象与引用的基础概念 **对象**: ...
引用类型是一种对象类型,包括数组、类和接口。引用类型的变量不直接存储值,而是存储对内存空间的引用,也就是地址。例如 MyDate today;这里的 today 变量存储的是对 MyDate 对象的引用,而不是实际的日期值。 在...
3. **Class::方法**:从Java 8开始,你可以直接引用类的构造器,这被称为构造器引用。例如,`String::new`表示创建新的String对象的构造器引用。 4. **实例::类方法**:如果你有一个对象实例,但想调用该类的非静态...
在Java中,类可以包含不同的数据类型,包括基本数据类型(如int、char等)和引用数据类型(比如String、Date等)。 当我们提到字符串(String)和日期(Date)这两个基础类时,我们指的是Java API中的String类和...
JAVA类与对象及数组习题 JAVA类和对象及数组是JAVA编程...在JAVA中,变量的类型可以是基本类型或引用类型,例如`int`、`String`等。 14. 编程实践: 通过实际编程实践,可以加深对JAVA类和对象及数组的理解和应用。
本示例“java引用的demo”将深入探讨四种不同类型的引用:强引用、软引用、弱引用和虚引用。这些引用类型对于理解和优化内存管理至关重要,特别是在Android开发中,因为良好的内存管理能够提升应用性能并防止内存...
4. **解析**:将符号引用转换为直接引用,即将类、接口、字段和方法的符号引用转换为可以直接使用的内存地址。 5. **初始化**:执行类的初始化方法 `<clinit>` ,这包括执行静态初始化块和对静态变量的赋值。 在...
这是因为 Java 编译器在编译时已经将常量值替换为了具体的值,而不是使用对常量类的引用。 了解了这个机制后,我们可以使用以下三种方法来解决这个问题: ### 方法一:删除 CLASS 文件并重新编译 这是最简单的...
java 包装类练习 Java 包装类是 Java 语言中的基本类型的对象表示形式,用于提供包装基本类型的对象。Java 包装类包括 Boolean、Byte、Character、Short、Integer、Long、Float 和 Double 八种。 1. Long 包装类型...
Java类思维导图是理解Java编程语言中类与类之间关系的重要工具,它通过图形化的方式,清晰地展示了Java各类的结构、继承关系以及方法的使用。这份"Java思维导图"很可能包含了从基础的面向对象概念到高级特性的全面...
本文将深入探讨"springboot+java类热加载"这一主题,包括其核心概念、实现方式以及相关技术。 **热加载(Hot Loading)**是开发过程中的一个重要功能,它允许开发者在不重启应用的情况下更新代码,提高开发效率。在...
### 浅析Java引用类型和方法参数传递 #### 一、引言 在Java编程语言中,理解数据类型的处理方式对于编写高效、可维护的代码至关重要。本文将深入探讨Java中的引用类型及其如何影响方法参数的传递机制。通过具体实例...
- **生成的Java类**:生成的Java类通常包括根元素类、简单类型类和复杂类型类。根元素类对应XSD中的根元素,复杂类型类对应XSD中的复杂类型,简单类型类对应基本的XML数据类型。 3. **Java对象到XML的序列化**: ...
### Java中对象与对象引用的区别 #### 基础概念 在Java编程语言中,对象与对象引用是非常基础且重要的概念。理解它们之间的区别对于掌握Java面向对象编程至关重要。 #### 对象与引用定义 - **对象**:在Java中,...
Java元数据——Class类 Java中的元数据Class类是一个基础的概念,它代理了这个类的类型信息、方法签名、属性等信息。每个类都有一个Class对象,它用来创建这个类的所有对象。每个对象的创建都依赖于Class对象的创建...
标题中的“Groovy和Java相互调用1”指的是在编程时如何在Groovy语言环境中调用Java类,以及反之,如何在Java程序中调用Groovy类。这是一种跨语言交互的方式,特别是在混合使用Groovy和Java的项目中非常常见。 ...
弱引用是 Java.lang.ref.WeakReference 类的实例,它们提供了一种方式来引用对象,而不阻止垃圾回收器回收这些对象。 在 Java 中,引用可以分为四类:强引用、软引用、弱引用和虚引用(也称为精灵引用)。弱引用是...
Java对象通常由JNI的局部引用或全局引用管理,避免内存泄漏。C++的对象则需要手动管理内存,确保正确释放。 9. **异常处理**: JNI提供了异常检查机制,C++代码需要在适当的地方检查并处理可能出现的Java异常。 ...