Pac*_*ier 5 java garbage-collection jvm
大家下午好,
我被教导当函数返回时,变量(在该函数的范围内)自动超出范围,因此我们不必将它们设置为null.
但是,这似乎不是真的.
我有一个测试代码,它创建一个指向java.lang.Object实例的java.lang.ref.PhantomReference.对该对象的唯一强引用是在函数F的范围内.
换句话说,当该函数返回时,不应再对该对象有任何强引用,并且该对象现在应该可由GC收集.
但是,无论我多么努力地使内存的JVM饿死,GC都会拒绝收集该对象.令人惊讶的是,如果我将变量设置为null(obj = null;),GC现在会收集该对象.
这种古怪背后的解释是什么?
public class Test {
public static void main(String args[]) {
// currently testing on a 64-bit HotSpot Server VM, but the other JVMs should probably have the same behavior for this use case
Test test = new Test();
test.F(new Object());
}
public <T> void F(T obj) {
java.lang.ref.ReferenceQueue<T> ref_queue = new java.lang.ref.ReferenceQueue<T>();
java.lang.ref.PhantomReference<T> ref = new java.lang.ref.PhantomReference<T>(obj, ref_queue); // if this line isn't an assignment, the GC wouldn't collect the object no matter how hard I force it to
obj = null; // if this line is removed, the GC wouldn't collect the object no matter how hard I force it to
StartPollingRef(ref_queue);
GoOom();
}
private <T> void StartPollingRef(final java.lang.ref.ReferenceQueue<T> ref_queue) {
new java.lang.Thread(new java.lang.Runnable() {
@Override
public void run() {
System.out.println("Removing..");
boolean removed = false;
while (!removed) {
try {
ref_queue.remove();
removed = true;
System.out.println("Removed.");
} catch (InterruptedException e) { // ignore
}
}
}
}).start();
}
private void GoOom() {
try {
int len = (int) java.lang.Math.min(java.lang.Integer.MAX_VALUE, Runtime.getRuntime().maxMemory());
Object[] arr = new Object[len];
} catch (Throwable e) {
// System.out.println(e);
}
}
}
Run Code Online (Sandbox Code Playgroud)
符合标准的JVM永远不必收集内存.也就是说,你不能编写一个程序,其正确性取决于在特定时间收集的特定内存位:你既不能强制JVM收集(甚至通过System.gc()!)也不能依赖它.
所以,你所观察到的行为在定义上不能错误:你有意识地试图让环境做一些没有责任的事情.
这就是说,你的问题是你的对象没有超出范围.它是创建的main,然后以正常的Java引用方式传递给F.在F返回之前,该T obj名称仍然是对象的引用.
使goOom静态并调用它main,你应该看到对象被收集.但是,再说一遍,你可能仍然没有,这不会是错的 ......