WeakHashMap
是主要通过
expungeStaleEntries
这个函数的来实现移除其内部不用的条目从而达到的自动释放内存的目的的
.
基本上只要对
WeakHashMap
的内容进行访问就会调用这个函数,从而达到清除其内部不在为外部引用的条目。但是如果预先生成了
WeakHashMap
,而在
GC
以前又不曾访问该
WeakHashMap,
那不是就不能释放内存了吗?
对应的两个测试案例
:
WeakHashMapTest1
:
public class WeakHashMapTest1 {
public static void main(String[] args) throws Exception {
List<
WeakHashMap<
byte[][], byte[][]>
>
maps = new ArrayList<
WeakHashMap<
byte[][], byte[][]>
>
();
for (int i
= 0;
i
<
1000;
i
++) {
WeakHashMap<
byte[][], byte[][]>
d = new WeakHashMap<
byte[][], byte[][]>
();
d.put
(new byte[1000][1000], new byte[1000][1000]);
maps.add
(d);
System.gc
();
System.err
.println
(i
);
}
}
}
由于
Java
默认内存是
64M
,所以再不改变内存参数的情况下,该测试跑不了几步循环就内存溢出了。果不其然,
WeakHashMap
这个时候并没有自动帮我们释放不用的内存。
WeakHashMapTest2
:
public class WeakHashMapTest2 {
public static void main(String[] args) throws Exception {
List<
WeakHashMap<
byte[][], byte[][]>
>
maps = new ArrayList<
WeakHashMap<
byte[][], byte[][]>
>
();
for (int i
= 0;
i
<
1000;
i
++) {
WeakHashMap<
byte[][], byte[][]>
d = new WeakHashMap<
byte[][], byte[][]>
();
d.put
(new byte[1000][1000], new byte[1000][1000]);
maps.add
(d);
System.gc
();
System.err
.println
(i
);
for (int j = 0;
j <
i
; j++) {
System.err
.println
(j + " size" + maps.get
(j).size
());
}
}
}
}
这次测试输出正常
,
不在出现内存溢出问题
.
总结来说:
WeakHashMap
并不是你啥也干他就能自动释放内部不用的对象的,而是在你访问它的内容的时候释放内部不用的对象
问题讲清楚了
,
现在我们来梳理一下
.
了解清楚其中的奥秘
.
WeakHashMap
实现弱引用,是因为它的
Entry<K,V>
是继承自
WeakReference<K>
的
在
WeakHashMap$Entry<K,V>
的类定义及构造函数里面是这样写的
:
private static class Entry<
K,V>
extends WeakReference<
K>
implements Map.Entry
<
K,V>
Entry(K key, V value, ReferenceQueue<
K>
queue,int hash, Entry<
K,V>
next) {
super(key, queue);
this.value
= value;
this.hash
= hash;
this.next
= next;
}
请注意它构造父类的语句:
“super(key, queue);”
,传入的是
key
,因此
key
才是进行弱引用的,
value
是直接强引用关联在
this.value
之中
.
在
System.gc()
时,
key
中的
byte
数组进行了回收
,
而
value
依然保持
(value
被强关联到
entry
上
,entry
又关联在
map
中
,map
关联在
arrayList
中
.).
如何证明
key
中的
byte
被回收了呢
?
可以通过内存溢出时导出的内存镜像进行分析
,
也可以通过如下的小测试得出结论
:
把上面的value用小对象代替,
for (int i
= 0;
i
<
10000;
i
++) {
WeakHashMap<
byte[][], Object>
d = new WeakHashMap<
byte[][], Object>
();
d.put
(new byte[1000][1000], new Object());
maps.add
(d); System.gc
();
System.err
.println
(i
);
}
上面的代码,即使执行10000次也没有问题,证明key中的byte数组确实被回收了。
for
循环中每次都new一个新的WeakHashMap,在put操作后,虽然GC将WeakReference的key中的byte数组回收了,并将事件通
知到了ReferenceQueue,但后续却没有相应的动作去触发 WeakHashMap 去处理 ReferenceQueue
所以 WeakReference 包装的key依然存在在WeakHashMap中,其对应的value也当然存在。
那
value
是何时被清除的呢
?
对两个例子进行分析可知
,
例子二中的maps.get(j).size()触发了
value
的回收
,
那又如何触发的呢
.
查看
WeakHashMap
源码可知
,size
方法调用了
expungeStaleEntries方法
,
该方法对
vm
要回收的的
entry(quene
中
)
进行遍历
,
并将
entry
的
value
置空
,
回收了内存
.
所以效果是
key
在
GC
的时候被清除
,value
在
key
清除后访问
WeakHashMap
被清除
.
疑问
:key
的
quene
与
map
的
quene
是同一个
quene,poll
操作会减少一个
reference,
那问题是
key
如果先被清除
,
expungeStaleEntries遍历
quene
时那个被回收的
key
对应的
entry
还能取出来么
???
关于执行
Sys
tem.GC
时
,key
中的
byte
数据如何被回收了
,
请见
WeakReference referenceQuene
分享到:
相关推荐
在Java编程中,WeakHashMap是一种特殊的哈希表,它的键(Key)是弱引用,当键被垃圾回收器清除后,即使有值(Value)存在,该条目也会自动从哈希表中移除。 线程死锁的原因通常包括以下几点: 1. **资源互斥**:每...
WeakHashMap是弱引用map,就是Key键是一个弱引用的键,如果Key键被回收,则在get该map中值后,会自动remove掉value。如果Key键始终被强引用,则是无法被回收的;注意Value是被强引用的,所以不要让Value间接的引用了...
针对上述问题,本文提出了一种解决方案,即在Lua编程语言的上下文中,通过引入临时对象(ephemerons)机制到表格中来消除弱表格中的循环引用。临时对象是一种特殊的弱引用机制,当没有任何强引用指向它们时,它们会...
- `clear()`:清除所有键值对。 - `containsKey(Object key)`:检查Map中是否存在指定的键。 - `containsValue(Object value)`:检查Map中是否存在指定的值。 - `entrySet()`:返回所有键值对的Set视图。 - `equals...
由于HashMap不会自动清除过期或不再使用的条目,开发者需要考虑何时以及如何更新或清除缓存中的数据。此外,虽然HashMap提供了高效的内存访问,但如果缓存中存储了大量或过大的对象,可能会导致垃圾回收器的压力增大...
弱引用对象只有在没有任何其他强引用指向它时,才会在下一次垃圾回收时被清除。Tomcat 将 WebappClassLoader 作为键放入 WeakHashMap 中,如果类加载器被正确地回收,那么对应的键将从映射中消失。 以下是一个简化...
Map<Key, Value> map = new WeakHashMap(); ``` 3. **合理使用静态变量**:尽量减少静态变量的使用,特别是不要让静态变量引用可变对象。 4. **及时清理监听器和回调函数中的引用**:当不再需要监听器或回调函数...
- **WeakHashMap**:这个类的键使用弱引用,当键不再被引用时,即使值还存在,键值对也会被垃圾收集器清除。这在内存管理上有特殊的应用场景。 3. **操作Map的方法** - `put(K key, V value)`:将指定的键值对放...