java - 防止Java 7过早发生GC

标签 java garbage-collection benchmarking jit

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/

相关文章:

java - JCas 类型 ...Timex3... 在 Java 代码中使用,但未在 XML 类型描述符 heideltime 中声明

java - MySQL结果集到java数组

java - 我们是否可以手动触发 Full GC,或者使用堆的某个百分比的参数来触发 Full GC,这是建议的吗?

python - 为什么 Python 代码在函数中运行得更快?

java - 具有 IP 验证且仅允许用户投票的机制 : problem in getting user IP address with two app servers and apache in front

java - 为什么 LocalEntityManagerFactoryBean 类被称为 "Local"?

java - 频繁的垃圾收集java web应用程序

scala - 在 scala 项目中 - 运行 sbt test 命令时出现错误 GC 开销限制超出

hash - Bob Jenkins 的 Hash 性能不佳

multithreading - 比较 CPU 速度可能提高的商业硬件升级理由