引用类,WeakHashMap,以及让value自动回收

如果要彻底明白WeakHashMap这个类,需要联系GC和对象的可触及状态(强可触及、软可触及……)来看,可参考JVM规范里相关内容。
关于gc和对象可触及性这两块,这里我就不展开了。不过,如果要看懂本文,最好还是先去看下这两块的机制。
[b]本文分三块:
1、jdk引用类介绍(如WeakReference)
2、WeakHashMap工作机制介绍
3、自己实现一个类,让它可以自动回收value的堆区(自认为这是本文[color=red]亮点[/color])[/b]
[color=red]PS:本文有误,value的堆区也会自动回收,因为在调用put方法的时候,内部调用了getTable方法,而getTable方法内部又调用了expungeStaleEntries方法,在expungeStaleEntries方法内部处理了已经由gc的后台线程Reference Handler加入到ReferenceQueue<K>中的Reference对象,该对象的内部的value被指向null,从而提示gc未来可回收value原对象的堆区空间了。
故本文第三条完全没有必要,WeakHashMap本身已经能很好的回收value的堆区了,当初不知为何有那样的测试结果出来,抱歉了!各位[/color]

[size=xx-large]1、jdk引用类介绍[/size]

而jdk里的引用类体系为:
[img]http://dl.iteye.com/upload/attachment/462493/69d0a6bf-d948-36c0-91d4-8707c95fe3fa.png[/img]
以软引用为例,看下引用的内存结构:
[img]http://dl.iteye.com/upload/attachment/462495/95315037-2b66-30aa-a4e6-534c926c5641.png[/img]

而,对应各种可触及性有很多引用类型
1、强可触及性
2、软可触及性----SoftReference
3、弱可触及性--WeakReference
4、可复活性---FinalReference
5、影子可触及性(译法可不同)---PhantomReference
6、不可触及性
引用类的学习,如果需要详细资料,请参考:
弱引用:[url]http://www.ibm.com/developerworks/cn/java/j-jtp11225/[/url]
软引用:[url]http://www.ibm.com/developerworks/cn/java/j-jtp01246.html[/url]
引用类使用指南:[url]https://www.ibm.com/developerworks/cn/java/j-refs/[/url]
《深入Java虚拟机》
尤其前三篇IBM开发社区的文章,分析的相当到位!!!
好了,下面正式开始谈WeakHashMap


[size=xx-large]2、WeakHashMap工作机制介绍:[/size]
这里用简单的话写出,可能不能完全描述清晰,尤其对初学者而言。先这样写着,如果需要,我会专门写一篇WeakHashMap源码分析文章,然后附上链接,那就看大家需不需要了。
在put一个新pair的时候,实际上是将key的引用封装成了一个 WeakReference<K>对象(这不同于HashMap中的直接使用key作为Entry的属性)。于是在每次调用WeakHashMap的
 gettable()
size()
resize()


方法的时候都会调用expungStaleEntries()方法来清除处理已经被gc加入到队列中的弱引用。处理方法见expungStaleEntries方法体:

private void expungeStaleEntries() {
Entry<K,V> e;
while ( (e = (Entry<K,V>) queue.poll()) != null) {
int h = e.hash;
int i = indexFor(h, table.length);

Entry<K,V> prev = table[i];
Entry<K,V> p = prev;
while (p != null) {
Entry<K,V> next = p.next;
if (p == e) {
if (prev == e)
table[i] = next;
else
prev.next = next;
e.next = null; // Help GC
e.value = null; // " "
size--;
break;
}
prev = p;
p = next;
}
}
}

上面这段代码实际上做的工作是:将已经被加入队列中的弱引用对应的Entry的从整个map的结构中移除,然后断开Entry指向 value的引用,加速gc回收value的堆区空间。而key指向的堆区对象已经在这个引用对象本身被GC加入到queue之前已经被释放了所有所有的弱引用,进入可复活状态或者已经经过可复活状态等待被回收了。


[size=xx-large]3、自己实现一个类,让它可以自动回收value的堆区[/size]
不过,WeakHashMap的设计有一个地方让我们不爽,如果我们没有调用size()方法,value在堆区的空间就不会被释放。于是我写了一个类MyWeakHashMap来替代WeakHashMap:
[color=red]如果要获得完整代码,请下载附件。[/color]

/**
* 自己实现的类,替代jdk中的类java.util.WeakHashMap。
* 和WeakHashMap比较,无需调用该java.util.WeakHashMap
* 中的size()等方法,该类可以自动释放内部类Entry中的
* value的堆空间。
* @author 贾懂凯
*
* @param <K>
* @param <V>
*/
public class MyWeakHashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>{
//如果有兴趣,请下载运行
}


附件中同时有一个测试类Test,通过测试,能很明确地看出我设计的类和jdk自带的WeakHashMap类的区别。
下面给出测试代码和结果:
如果是jdk自带类WeakHashMap:
import java.util.ArrayList;
import java.util.WeakHashMap;
import java.util.List;
public class Test {
public static void main(String args[]){

//测试1

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);
}
}
}

结果
[quote=""][color=red]0
1
……
63[/color]
[color=red]Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at Test.main([/color][u][color=blue]Test.java:18[/color][/u][color=red])[/color][/quote]
我们发先报出了OOM(内存溢出)异常。使用我定义的类,就不会出现OOM:



import java.util.WeakHashMap;
import java.util.List;
public class Test {
public static void main(String args[]){
//测试2
List<MyWeakHashMap<byte[][], byte[][]>> maps = new ArrayList<MyWeakHashMap<byte[][], byte[][]>>();
for (int i = 0; i < 1000; i++) {
MyWeakHashMap<byte[][], byte[][]> d = new MyWeakHashMap<byte[][], byte[][]>();
d.put(new byte[1000][1000], new byte[1000][1000]);
maps.add(d);
System.gc();
System.err.println(i);
}
}
}

结果:
[quote=""][color=red]0
1
2
3
……[/color][/quote]
不会出现OOM(内存溢出)异常。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值