我正在测试gc如何使用java.lang.ref包中的类,仅供学习:)
以下是我的代码。
public static void main(String [] args) {
int mb = 1024*1024;
//Getting the runtime reference from system
Runtime runtime = Runtime.getRuntime();
System.out.println("##### Heap utilization statistics [MB] #####");
ArrayList<Object> sb = new ArrayList<Object>();
for(int i =0; i < 5000000; i++){
sb.add(new Object());
if( i % 1000 == 0) {
System.out.println(i);
}
}
SoftReference<ArrayList> wr = new SoftReference<ArrayList>(sb);
// System.gc()
//Print used memory
System.out.println("Used Memory:"
+ (runtime.totalMemory() - runtime.freeMemory()) / mb);
//Print free memory
System.out.println("Free Memory:"
+ runtime.freeMemory() / mb);
//Print total available memory
System.out.println("Total Memory:" + runtime.totalMemory() / mb);
//Print Maximum available memory
System.out.println("Max Memory:" + runtime.maxMemory() / mb);
}
结果:
Used Memory:95,
Free Memory:28,
Total Memory:123,
Max Memory:247
我解除对“System.gc()”的评论,并重新编写代码,结果是
Used Memory:1,
Free Memory:122,
Total Memory:123,
Max Memory:247
是的,首先,收集了ArrayList的实例。据我所知,仅由SoftReference引用的实例是softreachable,因为缺少剩余堆空间而在真正需要GC时收集。代码的第一个结果的左侧空间大约是150(自由mem 28 + left max mem 124)。我不明白为什么收集了ArrayList的实例。
其次,我运行代码并修改:
sb.add(new Object()); -> sb.add(new StringBuffer(i));
结果:
Used Memory:245,
Free Memory:2,
Total Memory:247,
Max Memory:247
为什么会有所不同?
最后,我再次运行代码修改: 从
SoftReference<ArrayList> wr = new SoftReference<ArrayList>(sb);
到
WeakReference<ArrayList> wr = new WeakReference<ArrayList>(sb);
结果:
Used Memory:245,
Free Memory:2,
Total Memory:247,
Max Memory:247
我猜测收集了ArrayList的实例,因为实例仅由WeakReferece引用,因此这些实例是弱可达的。但他们没有收集。
我现在假设我对参考工作方式的理解不正确。
请有人告诉我原因。
Thx ^^
答案 0 :(得分:2)
嵌入式问题最容易回答:如果您将new Object()
替换为new StringBuffer(i)
并增加i
,则会创建容量增加的StringBuffer
个实例,因此不应该惊讶于这些对象比无状态Object
实例需要更多的内存。
主要问题并不是那么容易回答,因为你向我们展示了一个难以重现的结果,并且中间变成了一个更容易再现的结果,这表明你已经改变了代码比你说的或者你的测试环境有细微的变化。原则上,这两种结果都是可能的,但与您所做的改变完全无关。
首先,当您调用System.gc()
时,您在局部变量中持有对ArrayList
的强引用,因此无论其他引用是弱还是软,都是完全无关紧要的。在大多数设置和测试运行中,您将体验ArrayList
并且所包含的对象仍占用内存。
但这不是故事的结局。正如“finalize() called on strongly reachable object in Java 8”中所讨论的,即使持有强引用,如果JVM可以证明不会使用此引用,也可以收集对象。正如进一步讨论的那样,是否会发生这种情况仅取决于JVM的优化状态和执行的代码,因此您的示例程序不太可能发生,该程序由通常在解释器中运行的唯一main
方法组成,但是这不是不可能的。
但如果发生这种情况,此逻辑适用于方法中所有未使用的引用,其中包括对SoftReference
resp的引用。 WeakReference
个实例。如果该Reference
对象本身被收集,那么它对指示对象的语义将再次无关紧要。然后,ArrayList
,包含的对象和参考对象被收集在一起。
如果在调用sb
之前明确将null
变量设置为System.gc()
并在之后的引用对象上调用get()
,则可能会遇到不同的结果,但请保留请注意,System.gc()
仍然只是JVM的提示,可能会被忽略,因此根本没有效果。