【问题标题】:Will JVM optimization break down my code?JVM 优化会破坏我的代码吗?
【发布时间】:2014-06-17 20:57:14
【问题描述】:

我有以下方法被多个线程调用:

private final static Object lock = new Object();
public String createDirectory()
{
    File file = new File("D:"+File.separator+"test");
    if(!file.exists() || !file.isDirectory())//if file doesn't exist then create a new directory.
    {
        synchronized(lock)
        {
            if(!file.exists() || !file.isDirectory())//----> (1)
            {
                boolean isCreated = file.mkdir();
            }
        }
    }
    return file.getAbsolutePath();
}

JVM优化器是否有可能注释掉上面给定方法中标记为(1)的代码?我怀疑是因为,目录的存在会立即连续检查两次。将其视为不必要的冗余检查 JVM 优化器可能会注释掉该行 --> (1)。

【问题讨论】:

  • JVM 永远无法优化掉涉及方法调用的表达式,因为它不知道这些方法调用是否有副作用。
  • A JVM 只优化 BYTECODE 而不是 java 代码。它通过采用常见的 java 模式并将它们映射到更有效的字节码形式来进行优化。它永远不会“优化你的 java”cubrid.org/blog/dev-platform/understanding-jvm-internals
  • @DaoWen 不一定。如果已知函数没有任何副作用,则可以对其进行优化。一个简单的案例是boolean doNothing() {return true;}。但在这种情况下,您的说法是正确的,因为 File 方法最终将作为操作系统调用而结束,并且 JVM 不能假设这些没有副作用(并且不受除他们的论点)。
  • @Mac 当然。首先,方法可以内联;方法中的代码被“复制粘贴”到调用站点,从而节省了调用方法的开销。此外,如果该方法没有任何副作用并且总是返回相同的值(或者可能返回不同的值,但从未使用过该值——老实说,我不确定优化能走多远),该方法可以完全删除。你可以在gist.github.com/yshavit/73d4bb96dcdaecd652d8 看到一个例子。如果 JVM 没有优化方法,那将花费 很长 时间。
  • @Mac 这不仅仅是冗余调用,如果每个冗余调用都可以被证明什么都不做的话。但基本上优化器的一般规则是,“如果你能告诉它在那里,除非看到方法运行得更快,那么它就坏了。”

标签: java jvm jit


【解决方案1】:

没有。它不会被优化出来。

如果 JVM 优化出标准的双重检查锁定模式,那就有点垃圾了。

【讨论】:

    【解决方案2】:

    没有。编译器优化不会改变程序的流程。具体来说,永远不会跳过方法调用。

    【讨论】:

      【解决方案3】:

      正如@yshavit 指出的那样

      因为 File 方法最终将作为 OS 调用而结束,并且 JVM 不能假设这些方法没有副作用(并且不受其参数以外的状态影响),所以 JVM 不会通过注释掉该部分来优化涉及if(!file.exists() || !file.isDirectory()) 的代码。

      【讨论】:

      • JVM 在方法调用方面不运行。它将内联被调用方法的代码并优化生成的代码。因此,当它检测到在代码中读取相同的变量而没有中间写入或线程同步时,它可能会将多次读取合并为一次读取。由于java.io.File 维护一个持续大约一分钟左右的缓存,因此在短时间内对同一个File 进行两次exists 调用不太可能导致两次操作系统调用。
      • @Holger 所以根据你的说法,exists 上的两个调用 File 可能会产生相同的值,即使 File 存在状态在两个调用之间发生了变化?
      • 我过去遇到过这个问题,不得不插入等待以注意到外部变化。但请记住,这仅与您的 synchronized 声明无关的 external 更改有关。关于 JVM 中的线程,您的代码是安全的,因为 JVM 可能会优化双倍操作,但不会消除正确同步的影响。如果离开synchronized 语句的线程有可能改变了状态,那么使用相同锁实例进入synchronized 块的线程必须重新读取状态。
      • 如果我错了,请纠正我。简而言之,我可以放心,File 上对exist 方法的两次调用肯定会显示File 的最新状态因为那里是两个调用之间的同步
      • 为了清楚起见:同步只与 JVM 内由线程在同一实例上同步所做的更改相关。对于这些,第二次调用(在synchronized 块内)保证读取最新的值。
      猜你喜欢
      • 1970-01-01
      • 2016-04-22
      • 2020-06-23
      • 2012-10-08
      • 2021-02-01
      • 2012-08-12
      • 1970-01-01
      • 2022-07-05
      • 2011-04-13
      相关资源
      最近更新 更多