简体   繁体   English

完整GC期间是否清除了WeakHashMap?

[英]Are WeakHashMap cleared during a full GC?

I encountered some troubles with WeakHashMap. 我遇到了WeakHashMap的一些麻烦。

Consider this sample code: 考虑以下示例代码:

List<byte[]> list = new ArrayList<byte[]>();

Map<String, Calendar> map = new WeakHashMap<String, Calendar>();
String anObject = new String("string 1");
String anOtherObject = new String("string 2");

map.put(anObject, Calendar.getInstance());
map.put(anOtherObject, Calendar.getInstance());
// In order to test if the weakHashMap works, i remove the StrongReference in this object
anObject = null;
int i = 0;
while (map.size() == 2) {
   byte[] tab = new byte[10000];
   System.out.println("iteration " + i++ + "map size :" + map.size());
   list.add(tab);
}
System.out.println("Map size " + map.size());

This code works. 这段代码有效。 Inside the loops, i'm creating object.When a minor GC occurs, the map size is equal to 1 at the 1360th iteration. 在循环内部,我正在创建对象。当发生次要GC时,映射大小在第1360次迭代时等于1。 All is OK. 一切都好。

Now when i comment this line: 现在,当我评论这一行:

//anObject = null; 

I expect to have an OutOfMemoryError because the mapSize is always equal to 2. However at the 26XXX th iteration, a full GC occurs and the map size is equal to 0. I dont understand why? 我希望有一个OutOfMemoryError,因为mapSize总是等于2.但是在第26XXX次迭代时,发生了一个完整的GC并且地图大小等于0.我不明白为什么?

I thought that the map shouldn't have cleared because there are also strong references to both objects. 我认为地图不应该已经清除,因为还有对这两个对象的强引用。

The just-in-time compiler analyzes the code, sees that anObject and anOtherObject are not used after the loop, and removes them from the local variable table or sets them to null , while the loop is still running. 即时编译器分析代码,看到循环后没有使用anObjectanOtherObject ,并将它们从局部变量表中删除或将它们设置为null ,同时循环仍在运行。 This is called OSR compilation. 这称为OSR编译。

Later the GC collects the strings because no strong references to them remain. 后来GC收集字符串,因为没有对它们的强引用。

If you used anObject after the loop you'd still get an OutOfMemoryError . 如果您使用anObject在循环之后你仍然会得到一个OutOfMemoryError

Update: You'll find a more detailed discussion about OSR compilation in my blog. 更新:您将在我的博客中找到有关OSR编译的更详细讨论。

Bit of digging reveals that this is explicitly covered in the JLS, section 12.6.1: 挖掘的部分揭示了JLS第12.6.1节中明确涵盖的内容:

Optimizing transformations of a program can be designed that reduce the number of objects that are reachable to be less than those which would naively be considered reachable. 可以设计优化程序的转换,以减少可达到的对象的数量,使其少于可以被认为可达的对象的数量。 For example, a compiler or code generator may choose to set a variable or parameter that will no longer be used to null to cause the storage for such an object to be potentially reclaimable sooner. 例如, 编译器或代码生成器可以选择设置将不再用于null的变量或参数,以使得此类对象的存储可能更快地被回收。

(Bolding is my addition.) (Bolding是我的补充。)

http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.6.1 http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.6.1

So in essence, the JIT is allowed to remove strong references whenever it wants if it can work out that they'll never be used again - which is exactly what's happening here. 所以从本质上讲,JIT可以随时删除强引用,如果它能够解决它们永远不会被再次使用的话 - 这正是这里发生的事情。

This is a great question though and makes for a great puzzler that can easily show just because an object appears to have a strong reference in scope, doesn't necessarily mean it hasn't been garbage collected. 这是一个很好的问题,并且可以很容易地显示一个非常好的益智游戏,因为一个对象似乎在范围上有很强的引用,并不一定意味着它没有被垃圾收集。 Following on from this it means you explicitly can't guarantee anything about when a finalizer will run, this may even be in the case where it seems like the object is still in scope! 继续这一点意味着你明确地无法保证什么时候终结器会运行,这甚至可能是在对象仍然在范围内的情况下!

Eg: 例如:

List<byte[]> list = new ArrayList<byte[]>();

Object thing = new Object() {
    protected void finalize() {
        System.out.println("here");
    }
};
WeakReference<Object> ref = new WeakReference<Object>(thing);

while(ref.get()!=null) {
    list.add(new byte[10000]);
}
System.out.println("bam");

The above is a simpler example that shows the object gets finalized and GC'd first even though the reference to thing still exists (here is printed, then bam.) 以上是一个更简单的示例,显示对象最终完成并首先GC,即使对thing的引用仍然存在(这里打印,然后是bam。)

Just to add a little thing to the excellent answers from Joni Salonen and berry120 . 只是为Joni Salonenberry120的优秀答案添加一些东西。 It can be shown that the JIT is actually the responsible for the "variable removing" simply turning it off with -Djava.compiler=NONE . 可以证明,JIT实际上负责“变量删除”,只需将其与-Djava.compiler=NONE关闭-Djava.compiler=NONE Once you turn it off, you get the OOME. 一旦你把它关掉,你就得到了OOME。

If we want to know what is happening under the hoods, the option XX:+PrintCompilation shows the JIT activity. 如果我们想知道XX:+PrintCompilation发生了什么,选项XX:+PrintCompilation显示JIT活动。 Using it with the code from the question the output we get is the following: 将它与问题中的代码一起使用我们获得的输出如下:

1       java.lang.String::hashCode (64 bytes)
2       java.lang.String::charAt (33 bytes)
3       java.lang.String::indexOf (151 bytes)
4       java.util.ArrayList::add (29 bytes)
5       java.util.ArrayList::ensureCapacity (58 bytes)
6  !    java.lang.ref.ReferenceQueue::poll (28 bytes)
7       java.util.WeakHashMap::expungeStaleEntries (125 bytes)
8       java.util.WeakHashMap::size (18 bytes)
1%      WeakHM::main @ 63 (126 bytes)
Map size 0

The last compilation (with the @ flag) is a OSR (On Stack Replacement) compilation (check https://gist.github.com/rednaxelafx/1165804#osr for further details). 最后一次编译(使用@标志)是OSR(On Stack Replacement)编译(有关详细信息,请查看https://gist.github.com/rednaxelafx/1165804#osr )。 In simple words, it enables the VM to replace a method while it is running and it is used to improve performance of Java methods stuck in loops. 简单来说,它使VM能够在运行时替换方法,并用于提高陷入循环的Java方法的性能。 I would guess that after this compilation is triggered, the JIT removes the variables that are no longer used. 我猜想在触发这个编译之后,JIT会删除不再使用的变量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM