Java的ThreadLocal类有一个严重的瑕疵那就是即便在ThreadLocal 实例本身不在被引用的情况下,其中存储的东西也需要相当长的时间才能被解除引用。原因是所有的ThreadLocal 实例共享一个map,而该map只会在指定的时间段内清除过期的条目。
CloseableThreadLocal通过接收WeakReference类型的值并且保持对每个存储值一个硬应用,当调用close()方法的时候会清楚所有的引用,这样GC就可以回收所占用的内存空间。
Java 中一共有 4 种类型的引用 : StrongReference、 SoftReference、 WeakReference 以及 PhantomReference。
一:Strong Reference
StrongReference 是 Java 的默认引用实现, 它会尽可能长时间的存活于 JVM 内, 当没有任何对象指向它时 GC 执行后将会被回收
public static void main(String[] args) {
Object bject = new Object();
// 通过赋值创建 StrongReference
Object referent = object;
System.out.println(referent == object);
/************** true **********************/
bject = null;
System.gc();
// StrongReference 在 GC 后不会被回收
System.out.println(null == referent);
/************** false **********************/
}
二:WeakReference & WeakHashMap
WeakReference, 顾名思义, 是一个弱引用, 当所引用的对象在 JVM 内不再有强引用时, GC 后 weak reference 将会被自动回收
public static void main(String[] args) {
Object bject = new Object();
// 创建 WeakReference
WeakReference referent = new WeakReference(object);
bject = null;
System.gc();
//一旦没有指向 referent 的强引用, WeakReference 在 GC 后会被自动回收
System.out.println(null == referent.get());
/************** false **********************/
}
WeakHashMap 使用 WeakReference 作为 key, 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry
public static void main(String[] args) throws Exception {
Map weakHashMap = new WeakHashMap();
Object key = new Object();
Object value = new Object();
weakHashMap.put(key, value);
System.out.println(weakHashMap.containsValue(value));
key = null;
System.gc();
/**
* 等待无效 entries 进入 ReferenceQueue 以便下一次调用 getTable 时被清理
*/
Thread.sleep(1000);
/**
* 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry
*/
System.out.println(weakHashMap.containsValue(value));
}
三:SoftReference
SoftReference 于 WeakReference 的特性基本一致, 最大的区别在于 SoftReference 会尽可能长的保留引用直到 JVM 内存不足时才会被回收(虚拟机保证), 这一特性使得 SoftReference 非常适合缓存应用
四:PhantomReference
Phantom Reference(幽灵引用) 与 WeakReference 和 SoftReference 有很大的不同, 因为它的 get() 方法永远返回 null, 这也正是它名字的由来。PhantomReference 唯一的用处就是跟踪 referent 何时被 enqueue 到 ReferenceQueue 中.
五:RererenceQueue
当一个 WeakReference 开始返回 null 时, 它所指向的对象已经准备被回收, 这时可以做一些合适的清理工作. 将一个 ReferenceQueue 传给一个 Reference 的构造函数, 当对象被回收时, 虚拟机会自动将这个对象插入到 ReferenceQueue 中, WeakHashMap 就是利用 ReferenceQueue 来清除 key 已经没有强引用的 entries.
public static void main(String[] args) throws Exception {
Object bject = new Object();
ReferenceQueue referenceQueue = new ReferenceQueue();
WeakReference weakReference = new WeakReference(object,
referenceQueue);
System.out.println(weakReference.isEnqueued());
/************** false **************/
Reference extends Object> polled = referenceQueue.poll();
System.out.println(null == polled);
/************** true **************/
// 取消引用
bject = null;
System.gc();
System.out.println(weakReference.isEnqueued());
/************** true **************/
Reference extends Object> removed = referenceQueue.remove();
System.out.println(null == removed);
/************** false **************/
}
Object 的 finalize 方法, 将在 gc 执行前被调用, 如果某个对象重载了 finalize 方法并故意在方法内创建本身的强引用, 这将导致这一轮的 GC 无法回收这个对象并有可能引 起任意次 GC, 最后的结果就是明明 JVM 内有很多 Garbage 却 OutOfMemory, 使用 PhantomReference 就可以避免这个问题, 因为 PhantomReference 是在 finalize 方法执行后回收的,也就意味着此时已经不可能拿到原来的引用, 也就不会出现上述问题。
CloseableThreadLocal通过接收WeakReference类型的值并且保持对每个存储值一个硬应用,当调用close()方法的时候会清楚所有的引用,这样GC就可以回收所占用的内存空间。
Java 中一共有 4 种类型的引用 : StrongReference、 SoftReference、 WeakReference 以及 PhantomReference。
一:Strong Reference
StrongReference 是 Java 的默认引用实现, 它会尽可能长时间的存活于 JVM 内, 当没有任何对象指向它时 GC 执行后将会被回收
public static void main(String[] args) {
Object bject = new Object();
// 通过赋值创建 StrongReference
Object referent = object;
System.out.println(referent == object);
/************** true **********************/
bject = null;
System.gc();
// StrongReference 在 GC 后不会被回收
System.out.println(null == referent);
/************** false **********************/
}
二:WeakReference & WeakHashMap
WeakReference, 顾名思义, 是一个弱引用, 当所引用的对象在 JVM 内不再有强引用时, GC 后 weak reference 将会被自动回收
public static void main(String[] args) {
Object bject = new Object();
// 创建 WeakReference
WeakReference referent = new WeakReference(object);
bject = null;
System.gc();
//一旦没有指向 referent 的强引用, WeakReference 在 GC 后会被自动回收
System.out.println(null == referent.get());
/************** false **********************/
}
WeakHashMap 使用 WeakReference 作为 key, 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry
public static void main(String[] args) throws Exception {
Map weakHashMap = new WeakHashMap();
Object key = new Object();
Object value = new Object();
weakHashMap.put(key, value);
System.out.println(weakHashMap.containsValue(value));
key = null;
System.gc();
/**
* 等待无效 entries 进入 ReferenceQueue 以便下一次调用 getTable 时被清理
*/
Thread.sleep(1000);
/**
* 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry
*/
System.out.println(weakHashMap.containsValue(value));
}
三:SoftReference
SoftReference 于 WeakReference 的特性基本一致, 最大的区别在于 SoftReference 会尽可能长的保留引用直到 JVM 内存不足时才会被回收(虚拟机保证), 这一特性使得 SoftReference 非常适合缓存应用
四:PhantomReference
Phantom Reference(幽灵引用) 与 WeakReference 和 SoftReference 有很大的不同, 因为它的 get() 方法永远返回 null, 这也正是它名字的由来。PhantomReference 唯一的用处就是跟踪 referent 何时被 enqueue 到 ReferenceQueue 中.
五:RererenceQueue
当一个 WeakReference 开始返回 null 时, 它所指向的对象已经准备被回收, 这时可以做一些合适的清理工作. 将一个 ReferenceQueue 传给一个 Reference 的构造函数, 当对象被回收时, 虚拟机会自动将这个对象插入到 ReferenceQueue 中, WeakHashMap 就是利用 ReferenceQueue 来清除 key 已经没有强引用的 entries.
public static void main(String[] args) throws Exception {
Object bject = new Object();
ReferenceQueue referenceQueue = new ReferenceQueue();
WeakReference weakReference = new WeakReference(object,
referenceQueue);
System.out.println(weakReference.isEnqueued());
/************** false **************/
Reference extends Object> polled = referenceQueue.poll();
System.out.println(null == polled);
/************** true **************/
// 取消引用
bject = null;
System.gc();
System.out.println(weakReference.isEnqueued());
/************** true **************/
Reference extends Object> removed = referenceQueue.remove();
System.out.println(null == removed);
/************** false **************/
}
Object 的 finalize 方法, 将在 gc 执行前被调用, 如果某个对象重载了 finalize 方法并故意在方法内创建本身的强引用, 这将导致这一轮的 GC 无法回收这个对象并有可能引 起任意次 GC, 最后的结果就是明明 JVM 内有很多 Garbage 却 OutOfMemory, 使用 PhantomReference 就可以避免这个问题, 因为 PhantomReference 是在 finalize 方法执行后回收的,也就意味着此时已经不可能拿到原来的引用, 也就不会出现上述问题。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/28624388/viewspace-763745/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/28624388/viewspace-763745/
本文深入探讨了Java中ThreadLocal类的内存管理问题,特别是ThreadLocal实例在不再被引用时如何长时间占用内存。通过引入CloseableThreadLocal解决此问题,并解释了Java中的四种引用类型(StrongReference、SoftReference、WeakReference、PhantomReference)及其作用。文章还详细阐述了如何利用WeakReference和ReferenceQueue进行内存清理,以及如何避免由于对象引用导致的无限GC循环。

1998

被折叠的 条评论
为什么被折叠?



