【问题标题】:Java final boolean optimizationJava最终布尔优化
【发布时间】:2014-07-02 09:15:12
【问题描述】:

考虑以下设置:

// debugger class
public class Debug
{
    // setting
    public final static boolean DEBUG = false;

    void print_checked( String s )
    {
        if( DEBUG )
            System.out.print( s );
    }

    void print_unchecked( String s )
    {
        System.out.print( s );
    }
}

// worker class
public class Algorithm
{
    Debug debugger;

    public void heavyWeightAlgorithmImplementation()
    {
        // method 1
        debugger.print_checked( "This method 1" );

        // method 2
        if( debugger.DEBUG )
            debugger.print_unchecked( "Or this method 2" );
    }
}

我读过here,Java 中的final booleans 很可能会被优化掉。然而,在我的设置中,if 条件在另一个类中,目前在print_checked() 中实现。 Java 是否也优化了函数调用(方法 1),还是我必须像方法 2 一样重写所有内容?

编辑#1:再次添加静态

【问题讨论】:

  • 我很确定该方法将被内联,因此使用方法 2 将一无所获。
  • 你有没有试过两者看看是否有任何可衡量的差异?如果此代码对时间要求不是很高,请使用更易于维护的版本。
  • 您可能会节省 0.000000000001 秒
  • 如果重复数十亿次,即使是 1ns 的差异也会变得显着。
  • 它将被优化出来。 Java 可以看到 if 语句确实读取了if(false) 并将删除整个代码块。我会在 JLS 中寻找位置

标签: java optimization


【解决方案1】:

我用 jdk1.7 编译了上面的例子,并在 jd-gui 中打开了 *.class 文件。
例子:

class Debug
{
    // setting
    public static final boolean DEBUG = false;
    void print_checked( String s )
    {
        if( DEBUG )
            System.out.print( s );
    }
    void print_unchecked( String s )
    {
        System.out.print( s );
    }
}

public class Main 
{
    public static void main(String[] argc)
    {
        Debug debugger = new Debug();
        debugger.print_checked(" This method 1");
        if (debugger.DEBUG)
            debugger.print_unchecked( "Or this method 2");
    }
}

jd-gui:

// Debug.class
import java.io.PrintStream;
class Debug
{
  public static final boolean DEBUG = false;
  void print_checked(String paramString) {
  }
  void print_unchecked(String paramString) {
    System.out.print(paramString);
  }
}
// Main.class
public class Main
{
  public static void main(String[] paramArrayOfString)
  {
    Debug localDebug = new Debug();
    localDebug.print_checked(" This method 1");
  }
}

如您所见,方法“print_checked”保留在 Main.class 代码中,但“print_unchecked”已被删除。我不确定,但我认为 Oracle JVM 也可能会忽略空方法“print_checked”。

【讨论】:

  • 感谢 jd-gui 的提示
【解决方案2】:

Java 会随着时间的推移而变化,并且会变得更加复杂。因此,请加少许盐。

在javac编译过程中,如果编译器可以证明一段代码永远不会被访问,那么它将被删除。

在运行时,现代 Oracle JVM 倾向于以解释模式开始运行字节码,在这种情况下,它将进行检查,直到 JVM 认为代码“热”到足以进行优化。此时 Hotspot 开始发挥作用,它正朝着认为字段将被假定为最终字段的观点前进,直到它可以证明并非如此。此时它将尝试预测分支,并相当积极地内联代码。

但是,如果我记得,运行时不能将非静态字段的最终关键字视为 final 进行优化,因为有办法在运行时更改这些字段的值。对 JVM 内部人员的厌恶。

因此,在您的选择中,我会推荐方法 1,因为它减少了 if 块需要复制的位置数量。但是,如果可能的话,我会将 DEBUG 字段设为静态,这样可以更可靠地删除 if 语句。

【讨论】:

  • 哦,好吧,我的错。误解了 C++ 的 const 和 Java 的 final 的区别。现在将添加static
  • 设置静态最终布尔值的一个好技巧是推断提供给 JVM 的 -ea 标志的值。这样,您可以保持 DEBUG 关闭并从执行代码中剥离,直到您希望它打开,然后在不重新编译的情况下打开它。有关如何执行此操作的示例,请参阅 github.com/kirkch/SandboxMosaic/blob/master/lang-sandbox/src/…
【解决方案3】:

在您的示例中,恕我直言,第一种方法更好。但是,如果您在调试时需要一些串联操作,则最好使用第二个变体。

这是一个例子:

if( debugger.DEBUG )
        debugger.print_unchecked( "Or this method 2" + oldValue + "//" + newValue);

在这种情况下,编译器可以避免不必要的字符串连接和 toString() 方法的调用。

这是我的测试:

public class DebuggerTest {

    static final int ITER_NUM = 5000000;
    public static class Debug
    {
        // setting
        public final static boolean DEBUG = false;
        private final List<String> collector = new ArrayList<String>(ITER_NUM);
        void print_checked( String s )
        {
            if( DEBUG )
                collector.add(s);
        }

        void print_unchecked( String s )
        {
            collector.add(s);
        }
    }

    public static void main(String[] args) {
        Debug debug = new Debug();
        long time = System.currentTimeMillis();
        for (int i = 0; i < ITER_NUM; i++) {
            debug.print_checked("Debug message " + i);
        }
        System.out.println("First: " + (System.currentTimeMillis() - time));
        debug = new Debug();
        time = System.currentTimeMillis();
        for (int i = 0; i < ITER_NUM; i++) {
            if (Debug.DEBUG)
                debug.print_unchecked("Debug message " + i);
        }
        System.out.println("Second: " + (System.currentTimeMillis() - time));
    }
}

结果:

First: 339
Second: 2

【讨论】:

    猜你喜欢
    • 2013-02-01
    • 2017-01-24
    • 1970-01-01
    • 2010-11-23
    • 1970-01-01
    • 2014-12-14
    • 2010-11-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多