与Can JIT be prevented from optimising away method calls?相似,我试图跟踪长期存在的数据存储对象的内存使用情况,但是我发现,如果我初始化一个存储,记录系统内存,然后初始化另一个存储,有时是编译器(大概是JIT) )足够聪明,可以注意到不再需要这些对象。
public class MemTest {
public static void main(String[] args) {
logMemory("Initial State");
MemoryHog mh = new MemoryHog();
logMemory("Built MemoryHog");
MemoryHog mh2 = new MemoryHog();
logMemory("Built Second MemoryHog"); // by here, mh may be GCed
}
}
现在,链接线程中的建议是保留指向这些对象的指针,但是GC似乎很聪明,可以告诉
main()
不再使用这些对象。我可以在上一个logMemory()
调用之后添加对这些对象的调用,但这是一种相当手动的解决方案-每次测试一个对象时,我都必须在最后一个logMemory()
调用之后进行某种副作用触发调用,否则我可能会得到不一致的结果。我正在寻找一般案例解决方案;我知道在
System.out.println(mh.hashCode()+mh2.hashCode())
方法末尾添加像main()
这样的调用就足够了,但是出于某些原因,我不喜欢这样做。首先,它引入了上述测试的外部依赖性-如果删除了SOUT调用,则在内存记录调用期间JVM的行为可能会更改。其次,它容易出现用户错误;如果上面测试的对象发生了更改,或者添加了新对象,则用户必须记住也要手动更新此SOUT调用,否则它们将在测试中引入难以检测到的不一致之处。最后,我完全不喜欢打印此解决方案-似乎可以通过更好地理解JIT优化来避免不必要的黑客攻击。最后一点,Patricia Shanahan的答案提供了一个合理的解决方案(明确显示输出是出于记忆理智的目的),但我仍然希望尽可能避免这种情况。因此,我最初的解决方案是将这些对象存储在静态列表中,然后在主类的finalize方法*中对其进行迭代,如下所示:
public class MemTest {
private static ArrayList<Object> objectHolder = new ArrayList<>();
public static void main(String[] args) {
logMemory("Initial State", null);
MemoryHog mh = new MemoryHog();
logMemory("Built MemoryHog", mh); // adds mh to objectHolder
MemoryHog mh2 = new MemoryHog();
logMemory("Built Second MemoryHog", mh2); // adds mh2 to objectHolder
}
protected void finalize() throws Throwable {
for(Object o : objectHolder) {
o.hashCode();
}
}
}
但是现在,我只将问题转移了一个步骤-如果JIT在finalize方法中优化了循环,并决定不需要保存这些对象该怎么办?诚然,对于Java 7来说,简单地将对象保留在主类中就足够了,但是除非有文献证明finalzie方法无法进行优化,否则从理论上讲,仍然没有任何东西可以阻止JIT / GC尽早摆脱这些对象,因为我的finalize方法的内容没有任何副作用。
一种可能是将finalize方法更改为:
protected void finalize() throws Throwable {
int codes = 0;
for(Object o : loggedObjects) {
codes += o.hashCode();
}
System.out.println(codes);
}
据我了解(这里我可能是错的),调用
System.out.println()
将防止JIT删除此代码,因为它是一种具有外部副作用的方法,因此即使它不影响程序,它也可以不会被删除。这是有前途的,但是如果可以的话,我真的不希望输出任何乱码。 JIT无法(或不应该!)优化掉System.out.println()
调用这一事实向我暗示了JIT具有副作用的概念,如果我能告诉它这个finalize块具有此类副作用,它应该永远不要优化它。所以我的问题是:
holdijng是否足以在主类中列出对象列表,以防止它们被垃圾回收?
遍历这些对象并在finalize方法中调用诸如
.hashCode()
这样的琐碎事情就足够了吗?用这种方法计算和打印一些结果是否足够?
JIT知道还有其他方法(例如
System.out.println
)无法优化,甚至更好吗,是否有某种方法告诉JIT不要优化方法调用/代码块?*一些快速测试证实了,正如我所怀疑的那样,JVM通常不会运行主类的finalize方法,而是突然退出。 JIT / GC可能仍然不够聪明,不能仅仅因为finalize方法存在而就可以对我的对象进行GC,即使它无法运行,但我不确定情况总是如此。如果这不是记录在案的行为,我将无法合理地相信它会保持真实,即使现在是真实的。
最佳答案
这是一个计划,可能有些大材小用,但是应该是安全且相当简单的:
保留对对象的引用列表。
最后,遍历列表,对hashCode()结果求和。
打印哈希码的总和。
打印总和可确保无法优化最终循环。对于每个对象创建,您唯一需要做的就是将其放入List add调用中。
关于java - 防止Java 7过早发生GC,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15193878/