Eri*_*son 6 java debugging performance final compiler-optimization
我正在编写一个 Java 程序,该程序需要数千条System.out.println()语句,这些语句将在程序的整个生命周期中打印数亿(或数十亿)次,以用于调试目的:
if (GVar.runInDebugMode) System.out.println("Print debug message");
Run Code Online (Sandbox Code Playgroud)
在现实世界中,可以停用这些语句以加速计算量大的计算。
如果我设置:
public final static boolean runInDebugMode = false;
Run Code Online (Sandbox Code Playgroud)
runInDebugMode编译器是否会在每次遇到如下语句时重新计算:if (GVar.runInDebugMode)或者由于它被声明为final,因此它将在程序开始时计算一次,并且不会对CPU造成额外的压力?换句话说,一旦部署应用程序或设置足够,我最好完全注释掉所有调试语句runInDebugMode 吗false?
当你声明一个变量时,比如
\npublic final static boolean runInDebugMode = false;\nRun Code Online (Sandbox Code Playgroud)\nit\xe2\x80\x99是一个编译时常量。
\n\n\n\n常量变量是
\nfinal原始类型或使用常量表达式 ( \xc2\xa715.29String)初始化的类型的变量。
\n\n对常量变量 ( \xc2\xa74.12.4 )字段的引用必须在编译时解析为
\nV常量变量的初始值设定项表示的值。如果这样的字段是
\nstatic,则二进制文件的代码中不应存在对该字段的引用,包括声明该字段的类或接口。
换句话说,当您在编译时在任何地方编写时if(runInDebugMode),行为就好像您编写了 \xe2\x80\x99 一样,因为该值必须在编译时解析,并且编译后的类文件中不会出现对该字段的引用。runInDebugModefalseif(false)
您的用例已在\xc2\xa714.22中专门讨论
\n\n\n然而,为了让该
\nif语句能够方便地用于“条件编译”目的,实际规则有所不同。例如,以下语句会导致编译时错误:
\nRun Code Online (Sandbox Code Playgroud)\nwhile (false) { x=3; }\n因为该语句
\nx=3;不可到达;但表面上相似的情况:Run Code Online (Sandbox Code Playgroud)\nif (false) { x=3; }\n不会导致编译时错误。优化编译器可能会意识到该语句
\nx=3;永远不会被执行,并可能选择从生成的文件中省略该语句的代码class,但x=3;在此处指定的技术意义上,该语句不被视为“不可访问”。这种不同处理的基本原理是允许程序员定义“标志”变量,例如:
\nRun Code Online (Sandbox Code Playgroud)\nstatic final boolean DEBUG = false;\n然后编写代码,例如:
\nRun Code Online (Sandbox Code Playgroud)\nif (DEBUG) { x=3; }\n这个想法是应该可以更改
\nDEBUGfromfalsetotrue或 fromtrueto的值false,然后正确编译代码,而不需要对程序文本进行其他更改。条件编译附带一个警告。如果编译了一组使用“标志”变量(或更准确地说,任何
\nstatic常量变量(\xc2\xa74.12.4))的类,并且省略了条件代码,则稍后仅分发该类的新版本是不够的。包含标志定义的类或接口。
因此,该声明清楚地表明,这种形式的条件编译符合语言设计者的意图,并且编译器有权省略有问题的代码(所有相关编译器都这样做)。原则上,编译器不需要省略代码,但由于它不能在编译后的代码中生成对该字段的引用GVar.runInDebugMode,因此代码可以\xe2\x80\x99t包含真正的条件。如果代码没有被省略,那么它必须以事实上无条件的方式被跳过。要么通过goto指令,要么以可以想象到的最 na\xc3\xafve 方式编译时,通过逐字测试false, iconst_0; ifeq \xe2\x80\xa6。这两种方法在解释执行模式下都是纳秒级的,并且对 JIT 编译器/优化器没有任何挑战。
值得一提的是,static final字段是受信任的字段,通常甚至不能通过反射进行更改。这是由断言功能使用的,因为在幕后,包含assert语句的类将有一个static final boolean在类初始化时初始化的字段(因此 it\xe2\x80\x99s 不是编译时常量),并且每个assert语句将跳过根据static final变量的状态进行有条件的检查。早在Java\xc2\xa01.4的时候,就断定必要的死代码消除在JVM中是司空见惯的,所以就以这种方式依赖它。
因此,即使将调试标志从编译时常量转换为初始化时常量,对性能的影响也几乎不会被注意到。但按照您现在使用它的方式,代码已在编译时删除,并且无论如何都不依赖 JVM。
\n