`
mikab
  • 浏览: 17812 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

WeakHashMap的神话

阅读更多

在广大的Java界,关于WeakHashMap一直都存在这么个传说:

 

在WeakHashMap 中,当某个键不再正常使用时,将自动移除其条目

 可是WeakHashMap是真的自动移除其条目吗?

 

今天因为闲来无事,所以想看看WeakHashMap是如何自动实现移除其内部不用的条目从而达到的自动释放内存的目的的。仔细的看了看JVM自带的源代码的实现,在WeakHashMap是主要通过expungeStaleEntries这个函数的来实现的。基本上只要对WeakHashMap的内容进行访问就会调用这个函数,从而达到清除其内部不在为外部引用的条目。但是如果预先生成了WeakHashMap,而在GC以前又不曾访问该WeakHashMap,那不是就不能释放内存了吗?

 

写个代码测试一把:

	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这个时候并没有自动帮我们释放不用的内存。

 

再加个对会对map进行访问的测试试试:

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并不是你啥也干他就能自动释放内部不用的对象的,而是在你访问它的内容的时候释放内部不用的对象。这两句话看似区别不大,但是有时候一个小小的区别就会要了命的。

分享到:
评论
22 楼 mercyblitz 2010-12-10  
IcyFenix 写道
WeakHashMap实现弱引用,是因为它的Entry<K,V>是继承自WeakReference<K>的

在WeakHashMap$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被清除了,WeakHashMap本身根据ReferenceQueue中接收到的清除通知来清理value值,这个动作实现在expungeStaleEntries()方法之内,在getTable()之中对这个方法进行了调用,而WeakHashMap几乎在所有public的方法中都是要调用getTable()的。所以效果是key在GC的时候被清楚,value在key清除后访问WeakHashMap的时候被清除。

WeakHashMap的说明之中也是说“An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use”。所以WeakHashMap的实现并没有问题,只是人们大多想当然的理解为value会自动清除而已。


嗯,不过不是被清除了,而是被标记成了弱引用,在下次调用expungeStaleEntries()方法时,Value才被GC掉,伴随着entry被清除时!
21 楼 IcyFenix 2010-12-10  
WeakHashMap实现弱引用,是因为它的Entry<K,V>是继承自WeakReference<K>的

在WeakHashMap$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被清除了,WeakHashMap本身根据ReferenceQueue中接收到的清除通知来清理value值,这个动作实现在expungeStaleEntries()方法之内,在getTable()之中对这个方法进行了调用,而WeakHashMap几乎在所有public的方法中都是要调用getTable()的。所以效果是key在GC的时候被清楚,value在key清除后访问WeakHashMap的时候被清除。

WeakHashMap的说明之中也是说“An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use”。所以WeakHashMap的实现并没有问题,只是人们大多想当然的理解为value会自动清除而已。
20 楼 mercyblitz 2010-12-10  
<div class="quote_title">mercyblitz 写道</div>
<div class="quote_div">
<div class="quote_title">mikab 写道</div>
<div class="quote_div">
<p>在广大的Java界,关于WeakHashMap一直都存在这么个传说:</p>
<p> </p>
<pre name="code" class="java">在WeakHashMap 中,当某个键不再正常使用时,将<strong><span style="background-color: #ffffff; color: #ff0000;">自动</span></strong>移除其条目</pre>
<p> 可是WeakHashMap是真的自动移除其条目吗?</p>
<p> </p>
<p>今天因为闲来无事,所以想看看WeakHashMap是如何自动实现移除其内部不用的条目从而达到的自动释放内存的目的的。仔细的看了看JVM自带的源代码的实现,在WeakHashMap是主要通过expungeStaleEntries这个函数的来实现的。基本上只要对WeakHashMap的内容进行访问就会调用这个函数,从而达到清除其内部不在为外部引用的条目。但是如果预先生成了WeakHashMap,而在GC以前又不曾访问该WeakHashMap,那不是就不能释放内存了吗?</p>
<p> </p>
<p>写个代码测试一把:</p>
<pre name="code" class="java"> public static void main(String[] args) throws Exception {

List&lt;WeakHashMap&lt;byte[][], byte[][]&gt;&gt; maps = new ArrayList&lt;WeakHashMap&lt;byte[][], byte[][]&gt;&gt;();

for (int i = 0; i &lt; 1000; i++) {
WeakHashMap&lt;byte[][], byte[][]&gt; d = new WeakHashMap&lt;byte[][], byte[][]&gt;();
d.put(new byte[1000][1000], new byte[1000][1000]);
maps.add(d);
System.gc();
System.err.println(i);


}

}</pre>
<p> </p>
<p>由于Java默认内存是64M,所以再不改变内存参数的情况下,该测试跑不了几步循环就内存溢出了。果不其然,WeakHashMap这个时候并没有自动帮我们释放不用的内存。</p>
<p> </p>
<p>再加个对会对map进行访问的测试试试:</p>
<pre name="code" class="java">public static void main(String[] args) throws Exception {

List&lt;WeakHashMap&lt;byte[][], byte[][]&gt;&gt; maps = new ArrayList&lt;WeakHashMap&lt;byte[][], byte[][]&gt;&gt;();

for (int i = 0; i &lt; 1000; i++) {
WeakHashMap&lt;byte[][], byte[][]&gt; d = new WeakHashMap&lt;byte[][], byte[][]&gt;();
d.put(new byte[1000][1000], new byte[1000][1000]);
maps.add(d);
System.gc();
System.err.println(i);

for (int j = 0; j &lt; i; j++) {
System.err.println(j+  " size" + maps.get(j).size());
}
}
}</pre>
<p> </p>
<p>这下测试就顺利通过了。</p>
<p> </p>
<p>总结来说:WeakHashMap并不是你啥也干他就能自动释放内部不用的对象的,而是在你访问它的内容的时候释放内部不用的对象。这两句话看似区别不大,但是有时候一个小小的区别就会要了命的。</p>
</div>
<p> </p>
<p>楼上很多人没有答到点子上面。</p>
<p> </p>
<p>首先,请允许我把你的程序修改一下:</p>
<p> </p>
<pre name="code" class="java"> public static void main(String[] args) throws Exception {

//List&lt;WeakHashMap&lt;byte[][], byte[][]&gt;&gt; maps = new ArrayList&lt;WeakHashMap&lt;byte[][], byte[][]&gt;&gt;();
WeakHashMap&lt;byte[][], byte[][]&gt; d = new WeakHashMap&lt;byte[][], byte[][]&gt;();
for (int i = 0; i &lt; 1000; i++) {
d.put(new byte[1000][1000], new byte[1000][1000]);
//maps.add(d);
System.gc();
System.err.println(i);
}</pre>
<p> </p>
<p>List&lt;WeakHashMap&lt;byte[][], byte[][]&gt;&gt; maps = new ArrayList&lt;WeakHashMap&lt;byte[][], byte[][]&gt;&gt;();</p>
<p> </p>
<p>List对象强引用,它不会释放WeakHashMap对象。要知道你的程序有一个致命的缺点在:</p>
<p> </p>
<pre name="code" class="java">for (int i = 0; i &lt; 1000; i++) {
WeakHashMap&lt;byte[][], byte[][]&gt; d = new WeakHashMap&lt;byte[][], byte[][]&gt;(); 
d.put(new byte[1000][1000], new byte[1000][1000]);   //问题出在这里,看下面的描述。
maps.add(d);
System.gc();
System.err.println(i);
}</pre>
<p> </p>
<p>在循环中,每个新的<span>WeakHashMap在调用put方法中,</span></p>
<pre name="code" class="java">public V put(K key, V value) {
        K k = (K) maskNull(key);
        int h = HashMap.hash(k.hashCode());
        Entry[] tab = getTable();
        int i = indexFor(h, tab.length);
......</pre>
<p> 而getTable方法中,</p>
<pre name="code" class="java"> private Entry[] getTable() {
        expungeStaleEntries();
        return table;
}</pre>
<p> </p>
<p>因为每个新的<span>WeakHashMap实例,刚开始是空Map,不可能肮脏的数据,因此不存在回收的问题。换句话说,这里保存的都是强引用(暂时的),即使后来成为了“弱引用”对象的话,但是每个</span><span>WeakHashMap实例,<span style="background-color: #ffffff; color: #ff0000;"><strong>仅仅调用了一次put,没有第二次,所以不会判断前面的Entity弱引用对象。</strong></span></span></p>
<p> </p>
<p>WeakHashMap不是全能的,是因为每次put的时候,虽然判断那些对象是“弱引用”的对象,这些“弱引用”的对象是不定的情况下,下个GC阶段何时执行不确定。</p>
<p> </p>
<p>从API层次来说,这个设计还行,但是引用淘汰入队则是由JVM负责。</p>
<p> </p>
<p>PS:LZ研究精神可嘉,不做评价!</p>
</div>
<p> </p>
<p> </p>
<p>补充一点:</p>
<p>之所以调用 System.gc()(抛开gc方法的特殊情况),就是JVM在GC时,代码中的字节数组还是没有被释放。因为他们是弱引用,注意还是在引用,还没有被清扫掉。因此OOM是必须的。</p>
<p> </p>
<p> </p>
19 楼 mercyblitz 2010-12-10  
<div class="quote_title">mikab 写道</div>
<div class="quote_div">
<p>在广大的Java界,关于WeakHashMap一直都存在这么个传说:</p>
<p> </p>
<pre name="code" class="java">在WeakHashMap 中,当某个键不再正常使用时,将<strong><span style="background-color: #ffffff; color: #ff0000;">自动</span></strong>移除其条目</pre>
<p> 可是WeakHashMap是真的自动移除其条目吗?</p>
<p> </p>
<p>今天因为闲来无事,所以想看看WeakHashMap是如何自动实现移除其内部不用的条目从而达到的自动释放内存的目的的。仔细的看了看JVM自带的源代码的实现,在WeakHashMap是主要通过expungeStaleEntries这个函数的来实现的。基本上只要对WeakHashMap的内容进行访问就会调用这个函数,从而达到清除其内部不在为外部引用的条目。但是如果预先生成了WeakHashMap,而在GC以前又不曾访问该WeakHashMap,那不是就不能释放内存了吗?</p>
<p> </p>
<p>写个代码测试一把:</p>
<pre name="code" class="java"> public static void main(String[] args) throws Exception {

List&lt;WeakHashMap&lt;byte[][], byte[][]&gt;&gt; maps = new ArrayList&lt;WeakHashMap&lt;byte[][], byte[][]&gt;&gt;();

for (int i = 0; i &lt; 1000; i++) {
WeakHashMap&lt;byte[][], byte[][]&gt; d = new WeakHashMap&lt;byte[][], byte[][]&gt;();
d.put(new byte[1000][1000], new byte[1000][1000]);
maps.add(d);
System.gc();
System.err.println(i);


}

}</pre>
<p> </p>
<p>由于Java默认内存是64M,所以再不改变内存参数的情况下,该测试跑不了几步循环就内存溢出了。果不其然,WeakHashMap这个时候并没有自动帮我们释放不用的内存。</p>
<p> </p>
<p>再加个对会对map进行访问的测试试试:</p>
<pre name="code" class="java">public static void main(String[] args) throws Exception {

List&lt;WeakHashMap&lt;byte[][], byte[][]&gt;&gt; maps = new ArrayList&lt;WeakHashMap&lt;byte[][], byte[][]&gt;&gt;();

for (int i = 0; i &lt; 1000; i++) {
WeakHashMap&lt;byte[][], byte[][]&gt; d = new WeakHashMap&lt;byte[][], byte[][]&gt;();
d.put(new byte[1000][1000], new byte[1000][1000]);
maps.add(d);
System.gc();
System.err.println(i);

for (int j = 0; j &lt; i; j++) {
System.err.println(j+  " size" + maps.get(j).size());
}
}
}</pre>
<p> </p>
<p>这下测试就顺利通过了。</p>
<p> </p>
<p>总结来说:WeakHashMap并不是你啥也干他就能自动释放内部不用的对象的,而是在你访问它的内容的时候释放内部不用的对象。这两句话看似区别不大,但是有时候一个小小的区别就会要了命的。</p>
</div>
<p> </p>
<p>楼上很多人没有答到点子上面。</p>
<p> </p>
<p>首先,请允许我把你的程序修改一下:</p>
<p> </p>
<pre name="code" class="java"> public static void main(String[] args) throws Exception {

//List&lt;WeakHashMap&lt;byte[][], byte[][]&gt;&gt; maps = new ArrayList&lt;WeakHashMap&lt;byte[][], byte[][]&gt;&gt;();
WeakHashMap&lt;byte[][], byte[][]&gt; d = new WeakHashMap&lt;byte[][], byte[][]&gt;();
for (int i = 0; i &lt; 1000; i++) {
d.put(new byte[1000][1000], new byte[1000][1000]);
//maps.add(d);
System.gc();
System.err.println(i);
}</pre>
<p> </p>
<p>List&lt;WeakHashMap&lt;byte[][], byte[][]&gt;&gt; maps = new ArrayList&lt;WeakHashMap&lt;byte[][], byte[][]&gt;&gt;();</p>
<p> </p>
<p>List对象强引用,它不会释放WeakHashMap对象。要知道你的程序有一个致命的缺点在:</p>
<p> </p>
<pre name="code" class="java">for (int i = 0; i &lt; 1000; i++) {
WeakHashMap&lt;byte[][], byte[][]&gt; d = new WeakHashMap&lt;byte[][], byte[][]&gt;(); 
d.put(new byte[1000][1000], new byte[1000][1000]);   //问题出在这里,看下面的描述。
maps.add(d);
System.gc();
System.err.println(i);
}</pre>
<p> </p>
<p>在循环中,每个新的<span>WeakHashMap在调用put方法中,</span></p>
<pre name="code" class="java">public V put(K key, V value) {
        K k = (K) maskNull(key);
        int h = HashMap.hash(k.hashCode());
        Entry[] tab = getTable();
        int i = indexFor(h, tab.length);
......</pre>
<p> 而getTable方法中,</p>
<pre name="code" class="java"> private Entry[] getTable() {
        expungeStaleEntries();
        return table;
}</pre>
<p> </p>
<p>因为每个新的<span>WeakHashMap实例,刚开始是空Map,不可能肮脏的数据,因此不存在回收的问题。换句话说,这里保存的都是强引用(暂时的),即使后来成为了“弱引用”对象的话,但是每个</span><span>WeakHashMap实例,<span style="background-color: #ffffff; color: #ff0000;"><strong>仅仅调用了一次put,没有第二次,所以不会判断前面的Entity弱引用对象。</strong></span></span></p>
<p> </p>
<p>WeakHashMap不是全能的,是因为每次put的时候,虽然判断那些对象是“弱引用”的对象,这些“弱引用”的对象是不定的情况下,下个GC阶段何时执行不确定。</p>
<p> </p>
<p>从API层次来说,这个设计还行,但是引用淘汰入队则是由JVM负责。</p>
<p> </p>
<p>PS:LZ研究精神可嘉,不做评价!</p>
18 楼 i2534 2010-12-10  
找我个人的经验来说,WeakHashMap只有对此map发生操作时(修改,size),把过期的对象加入回收序列,直到GC回收了序列中的东西才释放内存.
不操作map,或者GC运行了但是还没回收这些都不算真的释放.
17 楼 agapple 2010-12-10  
楼主看的是还挺细的,是一个好的问题。 值得投个良好
16 楼 mikab 2010-12-10  
Anddy 写道
Hi。。

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.out.println(Runtime.getRuntime().freeMemory()/1024); // 观察可用内存大小的变化
            //System.err.println("iterator "+i +": "+maps.get(i).size());
        }

    }

稍微修改程序同样会出现outofmemory exception , 然后在System.gc()后面添加
Thread.sleep(1000);

sleep时间越长越不会抛出内存溢出的异常。。
为什么会出现这种情况,分析如下:
考虑两个线程:
1,gc线程;
2,上面程序所在的当前线程。
两线程竞争这CPU的使用。
不加“Thread.sleep(1000);”时,gc“竞争”不错当前线程,所以会出现 gc线程来不及释放无用的对象,导致outofmemory异常。
加“Thread.sleep(1000);”时,gc线程有充分的CPU去回收weakreference所指的对象。

楼主的观点:
引用
WeakHashMap 在你访问它的内容的时候释放内部不用的对象
 
严格来讲:准确来说应该是在gc回收后,才释放不用的对象。当操作WeakHashMap内容(比如size,put,get等)的时候,才清除已经被gc掉的对象所对应的Entry,变化是对象WeakHashMap,内存情况并没有变化,所以说不上“WeakHashMap 在你访问它的内容的时候释放不用的对象”。可以理解楼主在措辞方面的不当。

如上描述有误,跟贴。

真怀疑你自己有没有试过你自己写的代码。

按照你的程序,并不是sleep时间越长越不会抛出内存溢出的异常,而是sleep时间越长越晚抛出内存溢出的异常。

15 楼 Anddy 2010-10-18  
Hi。。

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.out.println(Runtime.getRuntime().freeMemory()/1024); // 观察可用内存大小的变化
            //System.err.println("iterator "+i +": "+maps.get(i).size());
        }

    }

稍微修改程序同样会出现outofmemory exception , 然后在System.gc()后面添加
Thread.sleep(1000);

sleep时间越长越不会抛出内存溢出的异常。。
为什么会出现这种情况,分析如下:
考虑两个线程:
1,gc线程;
2,上面程序所在的当前线程。
两线程竞争这CPU的使用。
不加“Thread.sleep(1000);”时,gc“竞争”不错当前线程,所以会出现 gc线程来不及释放无用的对象,导致outofmemory异常。
加“Thread.sleep(1000);”时,gc线程有充分的CPU去回收weakreference所指的对象。

楼主的观点:
引用
WeakHashMap 在你访问它的内容的时候释放内部不用的对象
 
严格来讲:准确来说应该是在gc回收后,才释放不用的对象。当操作WeakHashMap内容(比如size,put,get等)的时候,才清除已经被gc掉的对象所对应的Entry,变化是对象WeakHashMap,内存情况并没有变化,所以说不上“WeakHashMap 在你访问它的内容的时候释放不用的对象”。可以理解楼主在措辞方面的不当。

如上描述有误,跟贴。
14 楼 holan 2010-02-27  
Agrael 写道
WeakHashMap本就不是即时的清除包装。需要在操作时清除。
不然我也不会写自己的引用类型Map

敢问你的引用类型map是如何实现的
能够做到即使不调用map的方法也能在垃圾回收的时候自动释放entry?
13 楼 Agrael 2010-02-11  
WeakHashMap本就不是即时的清除包装。需要在操作时清除。
不然我也不会写自己的引用类型Map
12 楼 mikab 2010-02-09  
yangkai1217 写道
楼主运行你第一个会出OOM的例子,将其中的System.err.println(i)改为System.err.println(i+"   weakhashmap size="+d.size());
因为是在gc后,每次查看d.size的情况,,

在gc以前,for里的每次执行都没有去访问d,但是可以看到很多时候size都是0,说明已经被自动清除了,个别时候是1,?好像和你的说法有冲突啊

0   weakhashmap size=0
1   weakhashmap size=0
2   weakhashmap size=0
3   weakhashmap size=1
4   weakhashmap size=0
5   weakhashmap size=0
6   weakhashmap size=0
7   weakhashmap size=0
8   weakhashmap size=0


请看JDK中WeakHashMap的size方法的实现:
    
public int size() {
        if (size == 0)
            return 0;
        expungeStaleEntries();
        return size;
    }


在size返回之前,先去清除已经失去了KEY的ENTRY。至于为什么有的时候是0有的时候是1,请看
joachimz 写道
2 WeakReference也不是一有gc就会消失

11 楼 yangkai1217 2010-02-08  
楼主运行你第一个会出OOM的例子,将其中的System.err.println(i)改为System.err.println(i+"   weakhashmap size="+d.size());
因为是在gc后,每次查看d.size的情况,,

在gc以前,for里的每次执行都没有去访问d,但是可以看到很多时候size都是0,说明已经被自动清除了,个别时候是1,?好像和你的说法有冲突啊

0   weakhashmap size=0
1   weakhashmap size=0
2   weakhashmap size=0
3   weakhashmap size=1
4   weakhashmap size=0
5   weakhashmap size=0
6   weakhashmap size=0
7   weakhashmap size=0
8   weakhashmap size=0
10 楼 pipilu 2010-02-08  
nishizhutoua 写道
这种情况下,可以尝试把存Map的list也设置为弱引用.



从证明楼主要表达的主题的角度来说,楼主的示例代码挺合适的。
9 楼 pipilu 2010-02-08  
linliangyi2007 写道
应给个良好的,不管怎么说,楼主提出了一个有意义的命题,虽然部分观点没有全都正确。
楼上的例子肯定溢出咯,key一直被你引用着,说明不了楼主要说明的问题。


嗯,我是想举例说明他们说的不移除key只移除value的说法是错的。
8 楼 linliangyi2007 2010-02-08  
应给个良好的,不管怎么说,楼主提出了一个有意义的命题,虽然部分观点没有全都正确。
楼上的例子肯定溢出咯,key一直被你引用着,说明不了楼主要说明的问题。
7 楼 nishizhutoua 2010-02-08  
这种情况下,可以尝试把存Map的list也设置为弱引用.


6 楼 pipilu 2010-02-07  
iamlotus 写道
joachimz 写道
本想投新手贴了事,版主读书还是不仔细啊,至少犯几个错误:
1 System.gc并不一定会马上gc
2 WeakReference也不是一有gc就会消失
3 WeakHashMap中,只有值对象可能被回收
4 被回收后,只是获得了一个null并非Map的 Entry也被回收,或者说Map size并不减少


估计你根本没看清楚楼主在说什么。
假设每个Value为1M,楼主说的不是往1个WeakHashMap中塞N个Value的情况 ,而是往N个WeakHapMap中各塞一个Value的情况。
由于WeakHashMap是在有操作的情况下才会去执行expungeStaleEntries(),将entry中对value的引用释放。如果作了N个WeakHaspMap出去又不touch它们,确实是会抛OOM。


应该是对entry都释放——在API文档中描述如下:
引用
An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use.

楼主给的第二个例子打出的size明明是“0”嘛。

对楼主的第二个例子稍做修改,如下:
public static void main(String[] args) throws Exception {  
		  
	    List<WeakHashMap<Object, byte[][]>> maps = new ArrayList<WeakHashMap<Object, byte[][]>>();  
	    Object o = new Object();
	    for (int i = 0; i < 1000; i++) {  
	        WeakHashMap<Object, byte[][]> d = new WeakHashMap<Object, byte[][]>();  
	        d.put(o, 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());  
            }  
	    }  
	  
	}

运行后,结果跟第一个例子一样——内存不够了。
5 楼 mikab 2010-02-07  
joachimz 写道
本想投新手贴了事,版主读书还是不仔细啊,至少犯几个错误:
1 System.gc并不一定会马上gc
2 WeakReference也不是一有gc就会消失
3 WeakHashMap中,只有值对象可能被回收
4 被回收后,只是获得了一个null并非Map的 Entry也被回收,或者说Map size并不减少




1、2是对的,3、4却是错了。

正确与否,请试后再说。如果你又兴趣,请自己看看expungeStaleEntries的实现,WeakHashMap是直接把ENTRY从Map中去掉,并非像WeakReference一样留下一个NULL让你自己判断。

另Entry是按照键去判断是否该从map中去掉该ENTRY的,并不是回收值对象:
private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> {
4 楼 mikab 2010-02-07  
iamlotus 写道
joachimz 写道
本想投新手贴了事,版主读书还是不仔细啊,至少犯几个错误:
1 System.gc并不一定会马上gc
2 WeakReference也不是一有gc就会消失
3 WeakHashMap中,只有值对象可能被回收
4 被回收后,只是获得了一个null并非Map的 Entry也被回收,或者说Map size并不减少


估计你根本没看清楚楼主在说什么。
假设每个Value为1M,楼主说的不是往1个WeakHashMap中塞N个Value的情况 ,而是往N个WeakHapMap中各塞一个Value的情况。
由于WeakHashMap是在有操作的情况下才会去执行expungeStaleEntries(),将entry中对value的引用释放。如果作了N个WeakHaspMap出去又不touch它们,确实是会抛OOM。



楼上正解。

我并不想给大家普及System.gc、WeakReference、WeakHashMap的用法,只是想指出WeakHashMap的用处中的一个需要注意的地方。
3 楼 iamlotus 2010-02-07  
joachimz 写道
本想投新手贴了事,版主读书还是不仔细啊,至少犯几个错误:
1 System.gc并不一定会马上gc
2 WeakReference也不是一有gc就会消失
3 WeakHashMap中,只有值对象可能被回收
4 被回收后,只是获得了一个null并非Map的 Entry也被回收,或者说Map size并不减少


估计你根本没看清楚楼主在说什么。
假设每个Value为1M,楼主说的不是往1个WeakHashMap中塞N个Value的情况 ,而是往N个WeakHapMap中各塞一个Value的情况。
由于WeakHashMap是在有操作的情况下才会去执行expungeStaleEntries(),将entry中对value的引用释放。如果作了N个WeakHaspMap出去又不touch它们,确实是会抛OOM。

相关推荐

    WeakHashMap的使用方法详解

    WeakHashMap的使用方法详解 WeakHashMap是Java中的一种哈希映射表,它的键是弱引用的,意味着当 WeakHashMap 的键对象只有 WeakHashMap 自己持有时,垃圾回收器可以将其回收。WeakHashMap 的使用方法主要体现在...

    java集合-WeakHashMap的使用

    WeakHashMap是Java中的一种特殊的哈希表实现,它使用弱引用(Weak Reference)来保存键对象。当键对象没有被其他强引用引用时,在垃圾回收时会自动从WeakHashMap中移除对应的键值对。

    Java编程WeakHashMap实例解析

    Java编程WeakHashMap实例解析 WeakHashMap是Java编程中的一种特殊的HashMap实现,它使用弱引用来保存键和值,这样可以使得垃圾回收器自动清理键和值。在WeakHashMap中,键和值都是弱引用的,这样可以避免内存泄露...

    线程死锁CPU过高,请求原因分析

    在Java编程中,WeakHashMap是一种特殊的哈希表,它的键(Key)是弱引用,当键被垃圾回收器清除后,即使有值(Value)存在,该条目也会自动从哈希表中移除。 线程死锁的原因通常包括以下几点: 1. **资源互斥**:每...

    Java弱引用与WeakHashMap

    书中还提到可以用WeakHashMap来作为缓存的容器可以有效解决这一问题。之前也确实遇到过类似问题,但是没有接触过“弱引用”相关的问题,于是查阅了一些资料。  《Java 理论与实践: 用弱引用堵住内存泄漏》一文也...

    弱类型语言允许将一块内存看做多种类型

    - **expungeStaleEntries 方法**:为了确保 WeakHashMap 的键被正确清理,每当访问 WeakHashMap 的时候,都会调用 expungeStaleEntries 方法来清除已经无效的键值对。 #### 七、WeakHashMap 的局限性 - **访问触发...

    比较Vector、ArrayList和hashtable hashmap

    这意味着如果键不再被任何对象引用,那么即使在 WeakHashMap 中,这个键也会被垃圾收集器回收。这种方式可以防止内存泄漏,但也可能导致数据丢失。 总结: - Vector 和 ArrayList 都实现了 List 接口,其中 Vector ...

    集合类及其分支

    WeakHashMap是一种特殊的哈希表,它使用弱引用作为键,当键不再被引用时,即使没有显式地从集合中移除,也会自动清理。 **Map接口** Map接口存储键值对,不直接继承自Collection接口。常见的Map实现有Hashtable、...

    java集合总结.pdf

    WeakHashMap是Map接口的基于弱键的实现,以弱键实现的基于哈希表的Map。在WeakHashMap中,当某个键不再正常使用时,将自动移除其条目。 Java集合类提供了多种实现,用于解决不同的数据存储和操作问题。开发者可以...

    picketlink-jbas-common-2.6.0.CR4.zip

    【描述】"Java-WeakIdentityHashMap.zip, weakhashmap identityhashmapa独立库的组合,用于weakidentityhashmap实现内/外字段" 描述的是一个特定的Java数据结构实现,即WeakIdentityHashMap。这是一个结合了...

    Java中List、ArrayList、Vector及map、HashTable、HashMap分别的区别.

    WeakHashMap是一种特殊的Map实现,它使用弱引用作为键,当键不再被引用时,即使在Map中,也会被垃圾回收器清除,从而释放内存资源。 总结来说,选择哪种容器取决于具体的需求:如果需要有序的元素集合,可以使用...

    set,list,map区别与联系.docx

    - **WeakHashMap**和**IdentityHashMap**:特殊类型的Map,WeakHashMap的键使用弱引用,键在不再被引用时会被自动清理;IdentityHashMap使用对象的引用地址而非equals()方法来判断键的相等性。 4. **关联操作** -...

    2019阿里内推面经1

    而在`WeakHashMap`中,键是弱引用,当垃圾回收器回收了键所引用的对象,`WeakHashMap`会自动移除相应的键值对,从而允许垃圾回收。 最后,`IdentityHashMap`是一个特殊版本的哈希表,它使用对象的内存地址(`==`...

    java集合的介绍很全

    这意味着当没有强引用指向这些键时,垃圾回收器可以回收它们,即使它们仍然存在于`WeakHashMap`中。这使得`WeakHashMap`非常适合用于缓存场景,其中键可能不再被程序的其他部分使用。 总之,Java集合框架提供了一...

    多年的开发经验,与大家共享

    `WeakHashMap`使用弱引用作为键,当键不再被其他对象引用时,即使没有调用`remove`,键值对也会自动从`WeakHashMap`中消失,这是一种内存管理策略。 了解并熟练运用这些基础数据结构和集合框架,对于提升开发效率和...

    各种集合的总结

    总结了集合中常用的一点点,希望可以共享 List:LinkedList,ArrayList,Vector(Stack),Set Map:Hashtable,HashMap,WeakHashMap

    Android仿String的对象驻留示例分析

    private final WeakHashMap, WeakReference&lt;T&gt;&gt; pool = new WeakHashMap(); public T get(T object) { T res; WeakReference&lt;T&gt; ref = pool.get(object); if (ref != null) { res = ref.get(); } else { ...

    Java集合详解,详细讲解java的集合类

    当键不再被引用时,即使没有调用remove,键值对也会自动从WeakHashMap中移除。 总结一下,Java集合框架为开发者提供了丰富的选择,可以根据实际需求选择合适的集合类型。了解它们的特性和性能特点对于优化代码和...

Global site tag (gtag.js) - Google Analytics