详解Java中对象的软、弱和虚引用的区别

对于大部分的对象而言,程序里会有一个引用变量来引用该对象,这是最常见的引用方法。除此之外,java.lang.ref包下还提供了3个类:SoftReference、WeakReference和PhantomReference。它们分别代表了系统对对象的另外3中引用方式:软引用、弱引用和虚引用。

Java中四种引用的区别和关联:

  1. 强引用。这是Java中最常见的引用方式。程序创建一个对象,并把这个对象赋给一个引用变量,程序通过该引用变量来操作实际的对象。当一个对象被一个或者多个引用变量引用时,它处于可达状态,不能被系统垃圾回收机制回收。
  2. 软引用。当一个对象只有软引用时,它有可能被垃圾回收机制回收。对于只有软引用的对象而言,当系统内存空间足够时,它不会被系统回收,程序也可以使用该对象。当系统内存空间不足时,系统可能会回收它。软引用通常用于对内存敏感的程序中。
  3. 弱引用。弱引用和软引用很像,但它的级别比软引用更低。对于只有弱引用的对象而言,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存。当然并不是说当一个对象只有软引用时,它会立即被回收,正如那些失去引用的对象一样,必须等到系统垃圾回收机制运行时才会被回收。
  4. 虚引用。虚引用完全类似于没有引用。虚引用对对象本身没有什么太大的影响。虚引用主要用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,虚引用必须和引用队列(ReferenceQueue)搭配使用。

软、弱和虚引用都包含了一个get()方法,用于获取被它们所引用的对象。区别是虚引用的get()方法只会返回null。

引用队列由java.lang.ReferenceQueue类表示,它用于保存被回收后对象的引用。与软引用和弱引用不同的是,虚引用在对象被释放之前,将把它对应的虚引用添加到它关联的引用队列中,这使得可以在对象被回收之前采取行动。

下面的代码示范了弱引用所引用的对象被系统垃圾回收的过程:

import java.lang.ref.WeakReference;

public class Test{
    public static void main(String[] args){
        String str = new String("AmosH");
        WeakReference wr = new WeakReference(str);
        //建立弱引用,此弱引用指向"AmosH"字符串
        //注意这里一定要使用new来创建一个字符串对象
        //否则会该字符串会被保留在常量值而非堆内存中
        str = null;
        //切断"AmosH"字符串的强引用
        System.out.println(wr.get());
        //output "AmosH"
        //此时弱引用依然有效
        System.gc();
        System.runFinalization();
        //调用垃圾回收机制
        System.out.println(wr.get());
        //output null
        //弱引用已经被回收
    }
}

弱引用和软引用可以单独使用,但是虚引用不能单独使用。虚引用的主要作用是搭配引用队列来跟踪对象被垃圾回收的状态,程序可以通过检查与虚引用关联的引用队列中是否已经包含了该虚引用,从而了解虚引用所引用的对象是否即将被释放。

下面代码示范了虚引用对象被系统垃圾回收的过程:

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class Sample {
    public static void main(String[] args){
        String str = new String("AmosH");
        ReferenceQueue rq = new ReferenceQueue();
        //创建一个引用队列
        PhantomReference pr = new PhantomReference(str,rq);
        //创建一个虚引用,并且将该引用和rq引用队列关联
        str = null;
        //切断"AmosH"字符串的引用
        System.out.println(pr.get());
        //output null,因为系统无法通过虚引用的get()方法获取被引用对象
        System.gc();
        System.runFinalization();
        //强制垃圾回收
        System.out.println(rq.poll() == pr);
        //output true
        //虚引用被回收
    }
}

使用这些引用类可以避免在程序执行期间将对象留在内存中。如果以软引用、弱引用和虚引用的方式引用对象,垃圾回收器就可以随意的释放对象。如果希望尽可能减小程序在其生命周期中所占用的内存大小时,这些引用类就会很有用处。

但是要注意的是,使用了这些特殊的引用类,就不能保留对对象的强引用,这会浪费这些引用类所提供的好处。

由于垃圾回收的不确定性,当程序希望从软、弱引用中获取被引用对象时,可能这个被引用对象已经被释放了。如果需要使用那个被引用的对象,则必须重新创建该对象:

obj = wr.get();
//获取引用所引用的对象
//如果被取出的对象为null
if(obj==null){
    obj = recreateIt();
    //重建该对象并使用一个强引用来引用它
    //这里使用的伪代码,需要进行自定义
    wr = new WeakReference(obj);
    //重建这个弱引用
}
...//操作obj对象
obj = null;
//再次切断obj和对象之间的关系

Java 填坑手册,欢迎fork我的GitHub仓库

原文地址:https://www.cnblogs.com/AmosH/p/10219173.html

时间: 2024-11-09 00:12:19

详解Java中对象的软、弱和虚引用的区别的相关文章

详解Java中代码块和继承

本文发表于个人GitHub主页,原文请移步详解Java中代码块和继承 阅读. 概念 1.代码块 局部代码块 用于限定变量生命周期,及早释放,提高内存利用率 静态代码块 对类的数据进行初始化,仅仅只执行一次. 构造代码块 把多个构造方法中相同的代码可以放到这里,每个构造方法执行前,首先执行构造代码块. 2.继承 继承是已有的类中派生出新的类,新的类能够吸收已有类的数据属性和行为,并能扩展新的功能. 代码块的执行顺序 public class Test {    public String name

详解Java中的clone方法

转载自:http://blog.csdn.net/zhangjg_blog/article/details/18369201 Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那么在java语言中,有几种方式可以创建对象呢? 1 使用new操作符创建一个对象 2 使用clone方法复制一个对象 那么这两种方式有什么相同和不同呢? new操作符的本意是

详解Java中的clone方法:原型模式

Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那么在java语言中,有几种方式可以创建对象呢? 1 使用new操作符创建一个对象2 使用clone方法复制一个对象 那么这两种方式有什么相同和不同呢? new操作符的本意是分配内存.程序执行到new操作符时, 首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间.分配完内存之

【Java学习笔记之三十三】详解Java中try,catch,finally的用法及分析

这一篇我们将会介绍java中try,catch,finally的用法 以下先给出try,catch用法: try { //需要被检测的异常代码 } catch(Exception e) { //异常处理,即处理异常代码 } 代码区如果有错误,就会返回所写异常的处理. 首先要清楚,如果没有try的话,出现异常会导致程序崩溃.而try则可以保证程序的正常运行下去,比如说: try { int i = 1/0; } catch(Exception e) { ........ } 一个计算的话,如果除数

详解Java中Map用法

Map以按键/数值对的形式存储数据,这里要特别说明( Map.Entry,是Map的内部类,它用来描述Map中的键/值对). Map是一个接口,我们平时多用它的实现类HashMap. 用例如下: public static void main(String args[]) { HashMap hashmap = new HashMap(); hashmap.put("Item0", "Value0"); hashmap.put("Item1",

详解Java中的访问控制修饰符(public, protected, default, private)

Java中的访问控制修饰符已经困惑笔者多时,其中较复杂的情况一直不能理解透彻.今天下定决心,系统.全面地研究Java中的访问控制修饰符的所有方面,并整理成这篇文章,希望有同样疑惑的读者读完后能有所收获.如果文章中出现错误,欢迎评论指出,共同交流~ 说在前面:这篇文章只研究Java中访问控制修饰符声明类的变量/方法的情况. 先抛出结论: * 成员变量/方法的访问权限 *                                        private        default  

详解Java中格式化日期的DateFormat与SimpleDateFormat类

DateFormat其本身是一个抽象类,SimpleDateFormat 类是DateFormat类的子类,一般情况下来讲DateFormat类很少会直接使用,而都使用SimpleDateFormat类完成,下面我们具体来看一下两个类的用法: DateFormat1. DateFormat 介绍DateFormat 的作用是 格式化并解析“日期/时间”.实际上,它是Date的格式化工具,它能帮助我们格式化Date,进而将Date转换成我们想要的String字符串供我们使用不过DateFormat

详解Java中super的几种用法并与this的区别

---恢复内容开始--- 1.子类的构造函数如果要引用super的话,必须把super放在函数的首位 代码如下: class Base { Base() { System.out.println("Base"); } } public class Checket extends Base { Checket() { super();//调用父类的构造方法,一定要放在方法的首个语句 System.out.println("Checket"); } public sta

详解java中clone方法

原文地址:http://leihuang.net/2014/11/14/java-clone/ In java, it essentially means the ability to create an object with similar state as the original object. 什么是clone 字典中的意思就是复制(强调跟原来的一模一样). *By default, java cloning is 'field by field copy' *.因为Object类不知