精华内容
下载资源
问答
  • 主要介绍了Java引用队列和虚引用,结合实例形式分析了java引用队列和虚引用相关概念、原理与使用方法,需要的朋友可以参考下
  • 本文实例讲述了Java引用队列和虚引用。分享给大家供大家参考,具体如下:一 点睛引用队列由ReferenceQueue类表示,它用于保存被回收后对象的引用。当联合使用软引用、弱引用和引用队列时,系统在回收被引用的对象...

    本文实例讲述了Java引用队列和虚引用。分享给大家供大家参考,具体如下:

    一 点睛

    引用队列由ReferenceQueue类表示,它用于保存被回收后对象的引用。当联合使用软引用、弱引用和引用队列时,系统在回收被引用的对象之后,将把它所回收对象对应的引用添加到关联的引用队列中。而虚引用在对象被释放之前,将把它对应的虚引用添加到它关联的引用队列中,这使得可以在对象被回收之前采取行动。

    虚引用无法获取它所对应的对象。

    二 实战

    1 代码

    import java.lang.ref.*;

    public class PhantomReferenceTest

    {

    public static void main(String[] args)

    throws Exception

    {

    // 创建一个字符串对象

    String str = new String("疯狂Java讲义");

    // 创建一个引用队列

    ReferenceQueue rq = new ReferenceQueue();

    // 创建一个虚引用,让此虚引用引用到"疯狂Java讲义"字符串

    PhantomReference pr = new PhantomReference (str , rq);

    // 切断str引用和"疯狂Java讲义"字符串之间的引用

    str = null;

    // 取出虚引用所引用的对象,并不能通过虚引用获取被引用的对象,所以此处输出null

    System.out.println(pr.get()); //①

    // 强制垃圾回收

    System.gc();

    System.runFinalization();

    // 垃圾回收之后,虚引用将被放入引用队列中

    // 取出引用队列中最先进入队列中的引用与pr进行比较

    System.out.println(rq.poll() == pr); //②

    }

    }

    2 运行

    null

    true

    3 说明

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

    希望本文所述对大家java程序设计有所帮助。

    展开全文
  • 这篇文章主要介绍了Java中管理资源的引用队列相关原理解析,涉及到Java的垃圾回收机制方面的知识,需要的朋友可以参考下当对象改变其可达性状态时,对该对象的引用就可能会被置于引用队列(reference queue)中。...

    这篇文章主要介绍了Java中管理资源的引用队列相关原理解析,涉及到Java的垃圾回收机制方面的知识,需要的朋友可以参考下

    当对象改变其可达性状态时,对该对象的引用就可能会被置于引用队列(reference queue)中。这些队列被垃圾回收器用来与我们的代码沟通有关对象可达性变化的情况。这些队列是探测可达性变化的最佳方式,尽管我们也可以通过检查get方法的返回值是不是null来探测对象的可达性变化。

    引用对象在构造时可以与特定队列建立关联。Reference的每一个子类都提供了如下形式的构造器:

    .public Strength Reference (T referent, ReferenceQueueq):该方法用给定的指称对象创建新的引用对象,并且把该引用对象注册到给定的队列中。弱引用和软引用在垃圾回收器确定它们的指称对象进人它们所表示的特定可达性状态之后,就会被插人到队列中,并且这两种引用在插人队列前都会被清除。虚引用也会在垃圾回收器确定它的指称对象进入虚可达状态之后,被插入到队列中,但是它们不会被清除。一旦引用对象被垃圾回收器插人到队列中,其get方法的返回值就肯定会是null,因此该对象就再也不能复活了。

    将引用对象注册到引用队列中并不会创建队列和引用对象之间的引用。如果我们的引用对象本身变成了不可达的,那么它就不能插人队列了。因此我们的应用需要保持对所有引用对象的强引用。

    ReferenceQueue类提供了三个用来移除队列中引用的方法:

    .public Reference < ? extends下>poll ():用于移除并返回该队列中的下一个引用对象,如果队列为空,则返回null.

    .public Referenceremove ()throws InterruptedException:用于移除并返回该队列中的下一个引用对象,该方法会在队列返回可用引用对象之前一直阻塞。

    .public Referenceremove (long timeout) throws interrupte-dException:用于移除并返回队列中的下一个引用对象。该方法会在队列返回可用引用对象之前一直阻塞,或者在超出指定超时后结束。如果超出指定超时,则返回null.如果指定超时为0,意味着将无限期地等待。

    poll方法使得线程可以查询某个引用是否在队列中,并且在该引用存在于队列中时执行特定的动作。remove方法可以处理更复杂(更少见)的情况,在该方法中有一个专门的线程负责从队列中移除引用,并执行恰当的动作。这些方法的阻塞行为和object.wait中定义的阻塞行为相同。对于特定的引用,我们可以通过其isEnqueued方法来查询它是否在队列中,也可以通过调用其enqueue方法将其强制插入到队列中,但是通常这种插人是由垃圾回收器完成的。

    引用队列中的虚引用可以用来确定对象何时可以被回收。我们不可能通过虚引用来访问任何对象,即使该对象通过其他方式是可达的也是如此,因为虚引用的get方法总是返回null,事实上,用虚引用来查找要回收的对象是最安全的方法,因为弱引用和软引用在对象可终结之后会被插人到队列中,而虚引用是在指称对象被终结之后插人到队列中的,即在该对象可以执行某些操作的最后时刻之后插人队列的,所以它是绝对安全的。如果可以的话,应该总是使用虚引用,因为其他引用会存在finalize方法使用可终结对象的可能性。

    考虑一个资源管理器的例子,它可以控制外部资源集合的访问。对象可以请求访问某项外部资源,并且直至操作完成才结束访问,在此之后,它们应该将所用资源返回给资源管理器。如果这项资源是共享的,它的使用权就会在多个对象之间传递,甚至可能会在多个线程之间传递,因此我们很难确定哪个对象是这项资源最后的使用者,从而也就很难确定哪段代码将负责返回这项资源。为了处理这种情况,资源管理器可以通过将资源关联到被称为键( key)的特殊对象上,实现这项资源的自动回收。只要键对象是可达的,我们就认为这项资源还在使用中;只要键对象可以被当作垃圾回收,这项资源就会被自动释放。下面的代码是对上述资源的抽象表示:

    interface Resource{

    void use(Object key, Object…args);

    void release();

    }

    当获得某项资源时,其键对象必须提供给资源管理器。对于交还的Resource实例,只有在它得到了其对应的键时,才可以使用这项资源。这样就可以确保在键被回收之后,它所对应的资源就不能再被使用了,即便表示这项资源的Resource对象本身可能仍然是可达的。请注意,Resource对象并未存储对键对象的强引用,这一点很重要,因为这可以防止键对象变为不可达的,从而造成资源不能收回。Resource的实现可以嵌套在资源管理器中:

    private static class ResourceImpl implements Resource{

    int keyHash;

    boolean needsRelease=false

    ResourceImpl(Object key){

    keyHash=System.identityHashCode(key);

    //=set up the external resource

    needsRelease=true; }

    public void use(Object key,Object... args){

    if (System.identityHashCode(key)!=keyHash)

    throw new IlleqalArgumentException("wrong key"

    //...use the resource

    }

    public synchronized void release(){

    if (needsRelease){

    needsRelease=false:

    //=release the resource

    }

    }

    }

    在资源被创建时就存储了键对象的散列码,并且无论何时调用use方法,它都会检查是否提供了相同的键。对资源的实际使用可能还需要进行同步,但是为了简单起见,我们在这里把它们省略了。release方法负责释放资源,它可以由资源的使用者在使用结束之后直接调用,也可 以由资源管理器在键对象不再被引用时调用。因为我们将使用独立的线程来监视引用队列,所以release方法必须是synchronized的,并且必须允许多次调用。

    实际的资源管理器具有如下形式:

    public final class ResourceManager{

    final ReferenceQueue

    键对象可以是任意对象,与让资源管理器分配键对象相比,这赋予了资源使用者极大的灵活性。在调用getResource方法时,会创建一个新的Resource工mpl对象,同时会把提供给该方法的键传递给这个新的ResourceImpl对象。然后会创建一个虚引用,其指称对象就是传递给该方法的键,之后这个虚引用会被插人到资源管理器的引用队列中。最后所创建的虚引用和引用对象会被存储到映射表中,这个映射表有两个用途:一是保持所有的虚引用对象都是可达的,二是可以提供便捷的方法来查询每个虚引用所关联的实际的Resource对象。(另一种方式是子类化PhantomReference并将Resource对象存在一个字段中。)

    如果键对象变成了不可达的,那么资源管理器会使用独立的“收割机”(reaper)线程来处理资源。shutdown方法通过终止收割机线程(以响应中断)从而导致getResource方法抛出Ille-llleStateException异常来“关闭”资源管理器。在这个简单的设计中,任何在资源管理器关闭之后插人队列的引用都不会得到处理。实际的收割机线程如下:

    class ReaperThread extends Thread{

    public void run(){

    //run until interrupted

    while (true){

    try{

    Reference ref=queue.remove();

    Resource res=null;

    synchronized(ResourceManager.this){

    res=refs.get(ref);

    refs . remove(ref);

    }

    res .release();

    ref.clear();

    }

    catch (InterruptedException ex){

    break;//all done

    }

    }

    }

    }

    ReaperThread是内部类,并且给定的收割机线程会一直运行,直至与其相关联的资源管理器关闭。该线程会在remove方法上阻塞,直至与特定键相关联的虚引用被插人到引用队列中为止。这个虚引用可以从映射表中获取对Resource对象的引用,然后这一项“键一引用”对将会从映射表中移除。紧接着,在Resource对象上调用其release方法来释放这项资源。最后,

    虚引用被清除,使得键可以被回收。

    作为一种替代使用独立线程的方案,凡是在引用队列上调用poll方法并释放其键已经变为不可达的所有资源的操作都可以用getResourc“方法来替代,shutdow”方法也可以用来执行最后的poll操作。而资源管理器的语义将依赖于实际的资源类型和资源使用的模式。

    使用引用队列的设计与直接使用终结(特别是使用虚引用)的设计相比,要可靠得多。但是我们要记住,对于引用对象插入到引用队列中的确切时间和位置都是不能确定的,我们也不能确定在应用程序终止的时候,所有可插人的引用是否都匕经插人到了引用队列中。如果我们需要确保所有资源在应用程序终止之前都能够被释放掉,就必须要安装必要的关闭挂钩或者使用由应用程序定义的其他协议来确保实现这一点。

    展开全文
  • 当对象改变其可达性状态时,对该对象的引用就可能会被置于引用队列(reference queue)中。这些队列被垃圾回收器用来与我们的代码沟通有关对象可达性变化的情况。这些队列是探测可达性变化的最佳方式,尽管我们也可以...

    当对象改变其可达性状态时,对该对象的引用就可能会被置于引用队列(reference queue)中。这些队列被垃圾回收器用来与我们的代码沟通有关对象可达性变化的情况。这些队列是探测可达性变化的最佳方式,尽管我们也可以通过检查get方法的返回值是不是null来探测对象的可达性变化。

    引用对象在构造时可以与特定队列建立关联。Reference的每一个子类都提供了如下形式的构造器:

    .public Strength Reference (T referent, ReferenceQueueq):该方法用给定的指称对象创建新的引用对象,并且把该引用对象注册到给定的队列中。弱引用和软引用在垃圾回收器确定它们的指称对象进人它们所表示的特定可达性状态之后,就会被插人到队列中,并且这两种引用在插人队列前都会被清除。虚引用也会在垃圾回收器确定它的指称对象进入虚可达状态之后,被插入到队列中,但是它们不会被清除。一旦引用对象被垃圾回收器插人到队列中,其get方法的返回值就肯定会是null,因此该对象就再也不能复活了。

    将引用对象注册到引用队列中并不会创建队列和引用对象之间的引用。如果我们的引用对象本身变成了不可达的,那么它就不能插人队列了。因此我们的应用需要保持对所有引用对象的强引用。

    ReferenceQueue类提供了三个用来移除队列中引用的方法:

    .public Reference < ? extends下>poll ():用于移除并返回该队列中的下一个引用对象,如果队列为空,则返回null.

    .public Referenceremove ()throws InterruptedException:用于移除并返回该队列中的下一个引用对象,该方法会在队列返回可用引用对象之前一直阻塞。

    .public Referenceremove (long timeout) throws interrupte-dException:用于移除并返回队列中的下一个引用对象。该方法会在队列返回可用引用对象之前一直阻塞,或者在超出指定超时后结束。如果超出指定超时,则返回null.如果指定超时为0,意味着将无限期地等待。

    poll方法使得线程可以查询某个引用是否在队列中,并且在该引用存在于队列中时执行特定的动作。remove方法可以处理更复杂(更少见)的情况,在该方法中有一个专门的线程负责从队列中移除引用,并执行恰当的动作。这些方法的阻塞行为和object.wait中定义的阻塞行为相同。对于特定的引用,我们可以通过其isEnqueued方法来查询它是否在队列中,也可以通过调用其enqueue方法将其强制插入到队列中,但是通常这种插人是由垃圾回收器完成的。

    引用队列中的虚引用可以用来确定对象何时可以被回收。我们不可能通过虚引用来访问任何对象,即使该对象通过其他方式是可达的也是如此,因为虚引用的get方法总是返回null,事实上,用虚引用来查找要回收的对象是最安全的方法,因为弱引用和软引用在对象可终结之后会被插人到队列中,而虚引用是在指称对象被终结之后插人到队列中的,即在该对象可以执行某些操作的最后时刻之后插人队列的,所以它是绝对安全的。如果可以的话,应该总是使用虚引用,因为其他引用会存在finalize方法使用可终结对象的可能性。

    考虑一个资源管理器的例子,它可以控制外部资源集合的访问。对象可以请求访问某项外部资源,并且直至操作完成才结束访问,在此之后,它们应该将所用资源返回给资源管理器。如果这项资源是共享的,它的使用权就会在多个对象之间传递,甚至可能会在多个线程之间传递,因此我们很难确定哪个对象是这项资源最后的使用者,从而也就很难确定哪段代码将负责返回这项资源。为了处理这种情况,资源管理器可以通过将资源关联到被称为键( key)的特殊对象上,实现这项资源的自动回收。只要键对象是可达的,我们就认为这项资源还在使用中;只要键对象可以被当作垃圾回收,这项资源就会被自动释放。下面的代码是对上述资源的抽象表示:

    interface Resource{

    void use(Object key, Object…args);

    void release();

    }

    当获得某项资源时,其键对象必须提供给资源管理器。对于交还的Resource实例,只有在它得到了其对应的键时,才可以使用这项资源。这样就可以确保在键被回收之后,它所对应的资源就不能再被使用了,即便表示这项资源的Resource对象本身可能仍然是可达的。请注意,Resource对象并未存储对键对象的强引用,这一点很重要,因为这可以防止键对象变为不可达的,从而造成资源不能收回。Resource的实现可以嵌套在资源管理器中:

    private static class ResourceImpl implements Resource{

    int keyHash;

    boolean needsRelease=false

    ResourceImpl(Object key){

    keyHash=System.identityHashCode(key);

    //=set up the external resource

    needsRelease=true; }

    public void use(Object key,Object... args){

    if (System.identityHashCode(key)!=keyHash)

    throw new IlleqalArgumentException("wrong key"

    //...use the resource

    }

    public synchronized void release(){

    if (needsRelease){

    needsRelease=false:

    //=release the resource

    }

    }

    }

    在资源被创建时就存储了键对象的散列码,并且无论何时调用use方法,它都会检查是否提供了相同的键。对资源的实际使用可能还需要进行同步,但是为了简单起见,我们在这里把它们省略了。release方法负责释放资源,它可以由资源的使用者在使用结束之后直接调用,也可 以由资源管理器在键对象不再被引用时调用。因为我们将使用独立的线程来监视引用队列,所以release方法必须是synchronized的,并且必须允许多次调用。

    实际的资源管理器具有如下形式:

    public final class ResourceManager{

    final ReferenceQueue

    键对象可以是任意对象,与让资源管理器分配键对象相比,这赋予了资源使用者极大的灵活性。在调用getResource方法时,会创建一个新的Resource工mpl对象,同时会把提供给该方法的键传递给这个新的ResourceImpl对象。然后会创建一个虚引用,其指称对象就是传递给该方法的键,之后这个虚引用会被插人到资源管理器的引用队列中。最后所创建的虚引用和引用对象会被存储到映射表中,这个映射表有两个用途:一是保持所有的虚引用对象都是可达的,二是可以提供便捷的方法来查询每个虚引用所关联的实际的Resource对象。(另一种方式是子类化PhantomReference并将Resource对象存在一个字段中。)

    如果键对象变成了不可达的,那么资源管理器会使用独立的“收割机”(reaper)线程来处理资源。shutdown方法通过终止收割机线程(以响应中断)从而导致getResource方法抛出Ille-llleStateException异常来“关闭”资源管理器。在这个简单的设计中,任何在资源管理器关闭之后插人队列的引用都不会得到处理。实际的收割机线程如下:

    class ReaperThread extends Thread{

    public void run(){

    //run until interrupted

    while (true){

    try{

    Reference ref=queue.remove();

    Resource res=null;

    synchronized(ResourceManager.this){

    res=refs.get(ref);

    refs . remove(ref);

    }

    res .release();

    ref.clear();

    }

    catch (InterruptedException ex){

    break;//all done

    }

    }

    }

    }

    ReaperThread是内部类,并且给定的收割机线程会一直运行,直至与其相关联的资源管理器关闭。该线程会在remove方法上阻塞,直至与特定键相关联的虚引用被插人到引用队列中为止。这个虚引用可以从映射表中获取对Resource对象的引用,然后这一项“键一引用”对将会从映射表中移除。紧接着,在Resource对象上调用其release方法来释放这项资源。最后,

    虚引用被清除,使得键可以被回收。

    作为一种替代使用独立线程的方案,凡是在引用队列上调用poll方法并释放其键已经变为不可达的所有资源的操作都可以用getResourc“方法来替代,shutdow”方法也可以用来执行最后的poll操作。而资源管理器的语义将依赖于实际的资源类型和资源使用的模式。

    使用引用队列的设计与直接使用终结(特别是使用虚引用)的设计相比,要可靠得多。但是我们要记住,对于引用对象插入到引用队列中的确切时间和位置都是不能确定的,我们也不能确定在应用程序终止的时候,所有可插人的引用是否都匕经插人到了引用队列中。如果我们需要确保所有资源在应用程序终止之前都能够被释放掉,就必须要安装必要的关闭挂钩或者使用由应用程序定义的其他协议来确保实现这一点。

    展开全文
  • 引用主要应用在不阻止它的key或者value 被回收的mapping。弱引用的出现就是为了垃圾回收服务的。它引用一个对象,但是并不阻止该对象被回收。如果使用一个强引用的话,只要该引用存在,那么被引用的对象是不能被...

    弱引用主要应用在不阻止它的key或者value 被回收的mapping。弱引用的出现就是为了垃圾回收服务的。它引用一个对象,但是并不阻止该对象被回收。如果使用一个强引用的话,只要该引用存在,那么被引用的对象是不能被回收的。弱引用则没有这个问题。在垃圾回收器运行的时候,如果一个对象的所有引用都是弱引用的话,该对象会被回收。

    弱引用案例深度解析

    理想的情况下,我们希望当我们不再使用一个对象的时候,能够在gc 发生的时候就把它回收掉。但是有些时候,由于我们的粗忽,在坏的情况下会导致内存溢出。这种案例尤其发生在一个生命使用周期很长的map 存放了很多实际使用生命周期短的对象。请看下面这个例子:

    public class StrongRefenceDemo {

    static Map map;

    public static void main(String[] args) throws Exception {

    StrongRefenceDemo demo = new StrongRefenceDemo();

    demo.strongTest();

    System.out.println("gc 发生前:" + map.size());

    System.out.println("开始通知GC");

    //注意,这里只是通过垃圾回收器进行垃圾回收,并不一定马上执行

    System.gc();

    Thread.sleep(1000 * 5);

    System.out.println("gc 发生后:" + map.size());

    }

    /**

    * 强引用测试

    */

    public void strongTest() {

    map = new HashMap<>();

    String key = new String("key");

    String value = new String("value");

    map.put(key, value);

    key = null;

    value = null;

    }

    }

    运行后输出结果:

    gc 发生前:1 开始通知GC gc 发生后:1

    从输出的结果可以看到,即使我们通过把key和value 设置为null 来告诉jvm,我们不再使用这个对象了,map 里面对象依然没有被GC 回收,因为key和value 被一个强引用map 指向,根据可达性判断,垃圾回收器是不能回收掉key和value 这个对象的。map 被定义为statis 的静态变量,是一个使用生命周期很长的对象。在strongTest()方法中存在了一个key和value 的局部变量,它随着方法的执行完,这个变量的生命使用周期就结束了,但是粗糙的程序员忘记remove 了,这个时候垃圾回收器是不能回收它的。如果这种生命周期相对短的对象很多,最终就有可能消耗掉JVM中全部的内存。

    但是这里我有一个好奇,假如这里的key和value 指向的对象在执行完strongTest()方法 以后用不着了,但是我可能又不是很好的判断去主动调用remove 来移除它。想要垃圾回收器自己判断回收掉可不可以呢?答案其实是可以的,这个时候就是弱引用上场了,请看下面程序

    public class WeakRefenceDemo {

    static Map, String> map;

    public static void main(String[] args) throws Exception {

    WeakRefenceDemo demo = new WeakRefenceDemo();

    demo.weakTest();

    System.out.println("gc 发生前:" + map.size());

    System.out.println("开始通知GC");

    //注意,这里只是通过垃圾回收器进行垃圾回收,并不一定马上执行

    System.gc();

    Thread.sleep(1000 * 5);

    System.out.println("gc 发生后:" + map.size());

    }

    /**

    * 若引用测试

    */

    public void weakTest() {

    map = new WeakHashMap<>();

    String key = new String("key");

    String value = new String("value");

    map.put(new WeakReference<>(key), value);

    key = null;

    value = null;

    }

    }

    运行上面代码输出结果

    gc 发生前:1 开始通知GC gc 发生后:0

    从输出结果0,我们可以判断已经成功被垃圾回收了。what?整个过程我们只是把HashMap 换成了WeakHashMap,并且key 由String 换成了WeakReference。其实就是由于字符串只有弱引用指向,所以可以被垃圾回收掉。是不是很简单,如果到这里你就停止研究弱引用了,那就太暴殄天物了

    WeakHashMap 深度解析

    上面的程序片段中,其实只有key 设置了为弱引用new WeakReference<>(key),那正常也就只有这个key 对应的内存被回收而已,由于没有调用remove ,里面的value 和entry 也是不会回收掉的,那为什么最后输出的size 是0 呢? 很好的问题,我们深入去看WeakHashMap 的源码,我们发现了一个神奇的方法expungeStaleEntries()。在看源码之前先解析下引用队列的概念: 在弱引用被回收的时候会把该对象放到引用队列中,也就意味着从引用队列中获取的对象都是被回收的对象,先解释到这里,足以满足我们下面的源码分析了,接下来会做详细的解析

    /**

    * Expunges stale entries from the table.

    */

    private void expungeStaleEntries() {

    //这里从引用队列里面取出一个已经被回收的对象

    for (Object x; (x = queue.poll()) != null; ) {

    synchronized (queue) {

    @SuppressWarnings("unchecked")

    Entry e = (Entry) x;

    int i = indexFor(e.hash, table.length);

    Entry prev = table[i];

    Entry p = prev;

    //下面就是通过遍历链表来设置值为null 来告诉垃圾回收器回收掉

    //注意WeakHashMap 和HashMap 的数据结构都是通过数组+链表组成的,只有理解了这点才知道下面的代码做了什么

    while (p != null) {

    Entry next = p.next;

    //相等的时候,就意味着这个就是要回收的对象

    if (p == e) {

    //下面就是让回收对象不再被引用

    if (prev == e)

    table[i] = next;

    else

    prev.next = next;

    // Must not null out e.next;

    // stale entries may be in use by a HashIterator

    //这里通过设置value 为null 来告诉垃圾回收

    e.value = null; // Help GC

    size--;

    break;

    }

    prev = p;

    p = next;

    }

    }

    }

    }

    从上面的代码片段,大概的意思就是从引用队列里面取出被回收的对象,然后和WeakHashMap 中的对象查找,找到之后就把对应的value 也设置为null,并且把对应的entry 设置为null,来告诉GC 去回收它。从源码可以看到expungeStaleEntries() 这个方法在执行WeakHashMap中的任何方法的时候都会被调用到的

    /**

    * Expunges stale entries from the table.

    */

    private void expungeStaleEntries() {

    //这里从引用队列里面取出一个已经被回收的对象

    for (Object x; (x = queue.poll()) != null; ) {

    synchronized (queue) {

    @SuppressWarnings("unchecked")

    Entry e = (Entry) x;

    int i = indexFor(e.hash, table.length);

    Entry prev = table[i];

    Entry p = prev;

    //下面就是通过遍历链表来设置值为null 来告诉垃圾回收器回收掉

    //注意WeakHashMap 和HashMap 的数据结构都是通过数组+链表组成的,只有理解了这点才知道下面的代码做了什么

    while (p != null) {

    Entry next = p.next;

    //相等的时候,就意味着这个就是要回收的对象

    if (p == e) {

    //下面就是让回收对象不再被引用

    if (prev == e)

    table[i] = next;

    else

    prev.next = next;

    // Must not null out e.next;

    // stale entries may be in use by a HashIterator

    //这里通过设置value 为null 来告诉垃圾回收

    e.value = null; // Help GC

    size--;

    break;

    }

    prev = p;

    p = next;

    }

    }

    }

    }

    到这里也就完全明白为什么value 不设置为弱引用和没有显性的调用remove 方法也可以回收掉了。

    引用队列

    从上面的的源码中,我们大概知道了引用队列的使用,那为什么要使用引用队列呢?假如没有引用队列,上面的例子我们就需要遍历全部的元素一个一个的去找,如果数量少那还好,如果数量多的时候,肯定就会出现一些性能问题。有了引用队列那就轻松可以解决上面的问题了。从WeakReference 源码中我们可以看到有两个构造函数,第二个是需要传入引用队列的

    public WeakReference(T referent) {

    super(referent);

    }

    public WeakReference(T referent, ReferenceQueue q) {

    super(referent, q);

    }

    总结

    弱引用的出现是为了垃圾回收的

    一个对象只有弱引用指向它的时候,它是可以被回收的

    弱引用是在GC 发生的时候就进行回收,不管当时内存是否充足

    如果你在创建弱引用指定一个引用队列的话,弱引用对象被回收的时候,会把该对象放入引用队列中

    为了安全使用,每次都要判断下是否为空来判断该对象是否已经被回收,来避免空指针异常

    展开全文
  • Java引用队列和虚引用

    千次阅读 2019-06-23 15:50:40
    当联合使用软引用、弱引用和引用队列时,系统在回收被引用的对象之后,将把它所回收对象对应的引用添加到关联的引用队列中。而虚引用在对象被释放之前,将把它对应的虚引用添加到它关联的引用队列中,这使得可以在...
  • JAVA 的五种引用类型及引用队列 ​ 在介绍 JAVA 的五种引用之前,先介绍一下什么是引用,以及引用和对象之间的关系 什么是引用 ​ 众所周知,JAVA 是一种面向对象的语言,在 JAVA 程序运行时,对象是存储在堆内存...
  • java的强引用、软引用、弱引用、幻象引用,引用队列总结 java除了原始数据类型的变量,其他所有都是引用类型。 引用分为强引用、软引用、弱引用、幻象引用,这几种引用影响着对象的回收 强引用 强引用:形如...
  • 对象的引用被至少一个变量所把持,此时该对象引用就是强引用,jvm无论怎么样都不会回收强引用,除非再也没有任何变量继续把持该引用。 二、软引用、弱引用、虚引用的使用都需要配合SoftReference、WeekReference、...
  • 引用队列5.虚引用(对象回收和跟踪)6.总结 一.Java中的四种引用 Java中有四种引用类型依次为: 强引用(Strong Reference) 软引用(Soft Reference) 弱引用(Weak Reference) 虚引用(Phantom Reference) 这...
  • 主要介绍了Java中管理资源的引用队列相关原理解析,涉及到Java的垃圾回收机制方面的知识,需要的朋友可以参考下
  • Java语言中,除了基本数据类型外,其他的都是指向各类对象的对象引用Java中根据其生命周期的长短,将引用分为4类。 强引用 特点:我们平常典型编码Object obj = new Object()中的obj就是强引用。通过关键字new...
  • 目录 强引用引用引用 幻象引用 Reachability Fence 参考 强引用 正常的引用,生命周期最长,例如 Object obj = new Object(); 当JVM内存不足时,宁可抛出OutOfMemor...
  • JAVA引用实现队列

    2015-02-04 21:56:04
    JAVA内没有指针,取而代之的是引用,他比指针更加安全,c语言里队列里的每个元素,有两个区域,一个是指针域,指向下一个节点,还有一个是值的区域。 而JAVA内,把那个指针换成了节点,每个节点又包含有值区域,和下...
  • jvm的四种引用都有什么 1.强引用 一般像new Object()...软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。后续
  • 前几天讨论到WeakHashMap(这个是个弱引用的Map,用于缓存,弱引用的特点是随时被GC回收),WHM在每次put()会间接地调用expungeStaleEntries()来从去除实体。今天看到Hudon的WeakLogHandler的应用就怀着学习的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 874
精华内容 349
关键字:

java引用队列

java 订阅