Java中的强引用,软引用,弱引用,虚引用有什么用?

java有四种引用
关注者
260
被浏览
124,643

29 个回答

ThreadLocal中,获取到线程私有对象是通过线程持有的一个threadLocalMap,然后传入ThreadLocal当做key获取到对象的,这时候就有个问题,如果你在使用完ThreadLocal之后,将其置为null,这时候这个对象并不能被回收,因为他还有 ThreadLocalMap->entry->key的引用,直到该线程被销毁,但是这个线程很可能会被放到线程池中不会被销毁,这就产生了内存泄露,jdk是通过弱引用来解决的这个问题的,entry中对key的引用是弱引用,当你取消了ThreadLocal的强引用之后,他就只剩下一个弱引用了,所以也会被回收。

-----------------------

理论再多不如应用来的实际;

WeakReference使用实例:

ThreadLocalMap类 该类时ThreadLocal的静态内部类,该Map使用开放地址法处理hash冲突的Map类,key为ThreadLocal对象,value为TheadLocal对象所对应的值value;

其中Entry对象当中的Key值对TheadLocal的引用就是WeakReference

static class Entry extends WeakReference<ThreadLocal>
{
     /* The value assocaiated with this ThreadLocal.*/
    Object value;
    Entry(ThreadLocal k,Object v)
    {
        super(k);
        value = v;
    }
}

这样当ThreadLocal对象除了Entry对象外没有其他引用的时候,在下一次垃圾回收发生时,该对象将被回收;

这也就导致在使用Entry对象获取key值得时候,需要判断是否为空,如果为空,则说明已经被回收了,此时将value值手动清除即可;

其实因为ThreadLocal牵涉到线程本地变量的操作,对于对象何时被清除,程序逻辑一般不太好实现,所以JDK设计者将其设置为了自动清除,

其实大部分TheadLocal对象我们都将其设置为private static 对象,一般不会被弱引用清除掉;


具体更为详细的内容请查询ThreadLocal


------------------------


SoftReference

public Method getMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        // be very careful not to change the stack depth of this
        // checkMemberAccess call for security reasons
        // see java.lang.SecurityManager.checkMemberAccess
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        Method method = getMethod0(name, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + 
            name + argumentTypesToString(parameterTypes));
        }
        return method;
    }


下面看调用的getMethod0

  private Method getMethod0(String name, Class<?>[] parameterTypes) {
        // Note: the intent is that the search algorithm this routine
        // uses be equivalent to the ordering imposed by
        // privateGetPublicMethods(). It fetches only the declared
        // public methods for each class, however, to reduce the
        // number of Method objects which have to be created for the
        // common case where the method being requested is declared in
        // the class which is being queried.
        Method res;
        // Search declared public methods
        if ((res = searchMethods(privateGetDeclaredMethods(true),
                                 name,
                                 parameterTypes)) != null) {
            return res;
        }
        // Search superclass's methods
        if (!isInterface()) {
            Class<? super T> c = getSuperclass();
            if (c != null) {
                if ((res = c.getMethod0(name, parameterTypes)) != null) {
                    return res;
                }
            }
        }
        // Search superinterfaces' methods
        Class<?>[] interfaces = getInterfaces();
        for (int i = 0; i < interfaces.length; i++) {
            Class<?> c = interfaces[i];
            if ((res = c.getMethod0(name, parameterTypes)) != null) {
                return res;
            }
        }
        // Not found
        return null;
    }

下面看调用的privateGetDeclaredMethods

/**
 * 获取类中声明方法的具体实现
 * @param publicOnly 是否只获取public方法
 * @return 方法数组
 */
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
    // 等待系统内部类初始化完成,系统属性(sun.reflect.noCaches)被解析完成
    checkInitted();
    Method[] res = null;
    // 根据用户指定的系统属性sun.reflect.noCaches值来决定是否使用缓存,默认使用缓存
    if (useCaches) {
        // 清空缓存,将缓存的declaredFields、declaredMethods、annotations等设置为null
        clearCachesOnClassRedefinition();
        // 如果只获取public方法
        if (publicOnly) {
            // 如果declaredPublicFields缓存可用,则直接从缓存中获取
            if (declaredPublicMethods != null) {
                res = (Method[]) declaredPublicMethods.get();
            }
        } else {
            // 如果declaredMethods缓存可用,则直接从缓存中获取
            if (declaredMethods != null) {
                res = (Method[]) declaredMethods.get();
            }
        }
        if (res != null) return res;
    }
    // 不使用缓存,则需要调用本地方法进行获取
    res = getDeclaredMethods0(publicOnly);
    // 如果可以使用缓存,则设置缓存,以备下次使用
    if (useCaches) {
        if (publicOnly) {
            declaredPublicMethods = new SoftReference(res);
        } else {
            declaredMethods = new SoftReference(res);
        }
    }
    return res;
}

其中 declaredPublicMethods就是一个SoftReference

我们来看下这个成员变量的定义

private volatile transient SoftReference declaredPublicMethods;

private volatile transient SoftReference declaredMethods;

if(publicOnly){
// 如果declaredPublicFields缓存可用,则直接从缓存中获取
if(declaredPublicMethods != null){
                res =(Method[]) declaredPublicMethods.get();
}
}else{
// 如果declaredMethods缓存可用,则直接从缓存中获取
if(declaredMethods != null){
                res =(Method[]) declaredMethods.get();
}
}

其中res就是要获取的Method[] res 方法数组

如果发现get的结果已经为空,说明没有初始化或者已经被GC掉了,就重新获取一份新的并设置即可;


所以SoftReference非常适合用于做不是很重要的数据缓存,当内存紧张时,可以被GC掉


其实SoftReference和WeakReference都经常用来作为缓存来使用,不过WeakReference更容易被清除而已。

下面的文章也有详细的介绍