如果要彻底明白WeakHashMap这个类,需要联系GC和对象的可触及状态(强可触及、软可触及……)来看,可参考JVM规范里相关内容。
关于gc和对象可触及性这两块,这里我就不展开了。不过,如果要看懂本文,最好还是先去看下这两块的机制。
本文分三块:
1、jdk引用类介绍(如WeakReference)
2、WeakHashMap工作机制介绍
3、自己实现一个类,让它可以自动回收value的堆区(自认为这是本文亮点)
PS:本文有误,value的堆区也会自动回收,因为在调用put方法的时候,内部调用了getTable方法,而getTable方法内部又调用了expungeStaleEntries方法,在expungeStaleEntries方法内部处理了已经由gc的后台线程Reference Handler加入到ReferenceQueue<K>中的Reference对象,该对象的内部的value被指向null,从而提示gc未来可回收value原对象的堆区空间了。
故本文第三条完全没有必要,WeakHashMap本身已经能很好的回收value的堆区了,当初不知为何有那样的测试结果出来,抱歉了!各位
1、jdk引用类介绍
而jdk里的引用类体系为:
以软引用为例,看下引用的内存结构:
而,对应各种可触及性有很多引用类型
1、强可触及性
2、软可触及性----SoftReference
3、弱可触及性--WeakReference
4、可复活性---FinalReference
5、影子可触及性(译法可不同)---PhantomReference
6、不可触及性
引用类的学习,如果需要详细资料,请参考:
弱引用:
http://www.ibm.com/developerworks/cn/java/j-jtp11225/
软引用:
http://www.ibm.com/developerworks/cn/java/j-jtp01246.html
引用类使用指南:
https://www.ibm.com/developerworks/cn/java/j-refs/
《深入Java虚拟机》
尤其前三篇IBM开发社区的文章,分析的相当到位!!!
好了,下面正式开始谈WeakHashMap
2、WeakHashMap工作机制介绍:
这里用简单的话写出,可能不能完全描述清晰,尤其对初学者而言。先这样写着,如果需要,我会专门写一篇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之前已经被释放了所有所有的弱引用,进入可复活状态或者已经经过可复活状态等待被回收了。
3、自己实现一个类,让它可以自动回收value的堆区
不过,WeakHashMap的设计有一个地方让我们不爽,如果我们没有调用size()方法,value在堆区的空间就不会被释放。于是我写了一个类MyWeakHashMap来替代WeakHashMap:
如果要获得完整代码,请下载附件。
/**
* 自己实现的类,替代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);
}
}
}
结果
写道
0
1
……
63
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at Test.main(Test.java:18)
我们发先报出了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);
}
}
}
结果:
写道
0
1
2
3
……
不会出现OOM(内存溢出)异常。
- 大小: 38.2 KB
- 大小: 13.1 KB
分享到:
相关推荐
WeakHashMap是Java中的一种哈希映射表,它的键是弱引用的,意味着当 WeakHashMap 的键对象只有 WeakHashMap 自己持有时,垃圾回收器可以将其回收。WeakHashMap 的使用方法主要体现在以下几个方面: 1. WeakHashMap...
在《Effective Java 2nd Edition》中,第6条“消除过期的对象引用”提到,虽然Java有垃圾回收机制,但是只要是自己管理的内存,应该警惕内存泄露的问题,例如的对象池、缓存中的过期对象都有可能引发内存泄露的问题...
WeakHashMap是Java中的一种特殊的哈希表实现,它使用弱引用(Weak Reference)来保存键对象。当键对象没有被其他强引用引用时,在垃圾回收时会自动从WeakHashMap中移除对应的键值对。
- 使用`WeakHashMap`可以自动管理弱引用键,当键对象被回收时,对应的条目也会自动删除。 4. **虚引用(Phantom Reference)** - 虚引用是最弱的一种引用,不能直接获取引用对象,只能通过引用队列。虚引用对象在...
WeakHashMap是Java编程中的一种特殊的HashMap实现,它使用弱引用来保存键和值,这样可以使得垃圾回收器自动清理键和值。在WeakHashMap中,键和值都是弱引用的,这样可以避免内存泄露的问题。 WeakHashMap的实现...
- **WeakHashMap**:这个特殊的Map实现使用弱引用作为键,当键的强引用消失时,键值对将自动从映射中移除,有助于防止内存泄漏。 - **资源清理**:虚引用配合引用队列可以实现对象回收后的资源清理,例如关闭文件流...
内存分配与回收是编程语言中一个关键的概念,特别是在Java这样的自动管理内存的语言中。Java的垃圾回收机制(Garbage Collection, GC)是它与C++等语言的主要技术区别之一。理解GC的工作原理和内存分配策略对于优化...
在Java编程中,WeakHashMap是一种特殊的哈希表,它的键(Key)是弱引用,当键被垃圾回收器清除后,即使有值(Value)存在,该条目也会自动从哈希表中移除。 线程死锁的原因通常包括以下几点: 1. **资源互斥**:每...
在这里,我们将详细讲解Java中的四种引用类型:强引用、软引用、弱引用和虚引用,以及它们在内存管理和垃圾收集中的作用。 1. **强引用(Strong Reference)** - 强引用是Java中最常见的引用类型,它是默认的引用...
WeakHashMap是弱引用map,就是Key键是一个弱引用的键,如果Key键被回收,则在get该map中值后,会自动remove掉value。如果Key键始终被强引用,则是无法被回收的;注意Value是被强引用的,所以不要让Value间接的引用了...
要避免这种情况,可以使用WeakHashMap,它的键使用WeakReference,当对象不再被其他地方引用时,键会自动从集合中移除。 6. 自定义View持有可能导致泄漏的引用 自定义View在绘制过程中可能会持有外部对象的引用,...
总结,弱引用在Java中提供了一种优雅的方式,允许程序员在需要时释放不再使用的对象,避免内存泄漏,同时让垃圾收集器自动管理内存。理解和合理使用弱引用对于编写高效、健壮的Java应用程序至关重要。
总的来说,本文深入浅出地讲解了Java中的弱引用机制,以及如何利用弱引用来管理和防止内存泄漏。弱引用作为一种强大的工具,可以有效地处理那些生命周期不确定或需要灵活管理的对象,避免过度占用内存资源,保持程序...
在Java编程语言中,弱引用是一种特殊的引用类型,它允许...通过WeakReference类以及相关数据结构如WeakHashMap,Java为开发者提供了实现这一目标的工具。在实际应用中,合理使用弱引用能够提升程序的性能和稳定性。
Java 提供了 `WeakHashMap` 类,它的键使用弱引用,这样当键所对应的对象不再被其他强引用时,键及其关联的值都会被自动清除,从而避免了内存泄漏。 虚引用(Phantom Reference)是最弱的引用类型,它甚至无法获取...
弱表格中,键(key)与值(value)之间的循环引用会导致垃圾收集器无法收集这些元素,即使它们从外部不再可达。这在某些类型的应用中带来了困难。循环引用在弱表格中阻止了元素的回收,导致内存泄漏。 解决弱表格中...
- **垃圾回收机制**:当一个 WeakHashMap 中的键不再被其他地方引用时,系统执行垃圾回收后,该键就会被从 WeakHashMap 中自动移除。 - **expungeStaleEntries 方法**:为了确保 WeakHashMap 的键被正确清理,每当...
在Java 1.5及以后的版本中,除了基础的强引用(Strong Reference)之外,引入了弱引用(Weak Reference)、软引用(Soft Reference)和虚引用(Phantom Reference)等智能引用类型,以及与之相关的容器类,以帮助...