我正在编写一个Java程序,它需要数千条System.out.println()条语句,在程序的整个生命周期中,这些语句将被打印数亿次(或数十亿次),以便进行调试:

if (GVar.runInDebugMode) System.out.println("Print debug message");

在现实世界中,可以停用这些语句,以加快计算量大的计算.

如果我设定:

public final static boolean runInDebugMode = false;

编译器是在每次遇到像:if (GVar.runInDebugMode)这样的语句时重新计算runInDebugMode,还是因为它被声明为final,所以会在程序开始时计算一次,不会给CPU带来额外的压力?换句话说,在部署应用程序后,我是否最好完全注释掉所有调试语句,或者设置runInDebugModefalse就足够了?

推荐答案

当你声明一个变量

public final static boolean runInDebugMode = false;

compile-time constant美元.

constant variable是原语类型或String类型的final变量,用常量表达式(§15.29)初始化.

which means that

对常量变量(§4.12.4)字段的引用必须在编译时解析为常量变量的初始值设定项表示的值V.

如果这样一个字段是static,那么二进制文件中的代码中不应该出现对该字段的引用,包括声明该字段的类或接口.

换句话说,当你在任何地方写if(runInDebugMode),在编译时runInDebugModefalse,行为就像你写if(false)一样,因为这个值必须在编译时解析,并且编译的类文件中没有对字段的引用.

您的用例已经在§14.22页中详细讨论过

然而,为了方便地将if语句用于"条件编译"目的,实际规则有所不同.

例如,以下语句会导致编译时错误:

while (false) { x=3; }

因为无法访问语句x=3;;但表面上相似的情况是:

if (false) { x=3; }

不会导致编译时错误.优化编译器可以意识到语句x=3;永远不会被执行,并且可以 Select 从生成的class文件中省略该语句的代码,但是在这里指定的技术意义上,语句x=3;不被视为"不可访问".

这种不同处理的基本原理是允许程序员定义"标志"变量,例如:

static final boolean DEBUG = false;

然后编写如下代码:

if (DEBUG) { x=3; }

其 idea 是,应该可以将DEBUG的值从false更改为true或从true更改为false,然后正确编译代码,而不对程序文本进行其他更改.

条件编译附带了一个警告.如果编译了一组使用"flag"变量的类——或者更准确地说,任何static常量变量(§4.12.4)——并且省略了条件代码,那么以后只分发包含该标志定义的类或接口的新版本是不够的.

因此,这句话清楚地表明,这种形式的条件编译符合语言设计者的意图,编译器有权省略有问题的代码(所有相关的编译器都有).原则上,编译器不需要省略代码,但由于它会生成对编译代码中字段GVar.runInDebugMode的引用,因此代码不能包含真正的条件.如果代码没有被省略,就必须以事实上无条件的方式跳过它.或者,通过goto指令,或者,当以最天真的方式编译时,通过字面上的测试falseiconst_0; ifeq ….这两种方法在解释执行模式下都是纳秒级的,对JIT编译器/优化器没有任何挑战.


值得一提的是,static final个字段是受信任的字段,它们通常甚至不能通过反射进行更改.例如,由断言功能使用,就像在后台一样,包含assert语句的类将在类初始化时初始化一个static final boolean字段(因此它不是编译时常量),每个assert语句将根据static final变量的状态有条件地跳过其判断.早在Java 1.4时代,人们就得出结论,以这种方式依赖它,在JVM中消除必要的死代码是司空见惯的.

因此,即使将调试标志从编译时常量改为初始化时常量,对性能的影响也不会明显.但是现在使用它的方式是,代码已经在编译时被删除,并且无论如何都不依赖JVM.

Java相关问答推荐

我们如何直接使用kerminldap服务票证来通过ldap进行身份验证并形成LDAP上下文

Java:根据4象限中添加的行数均匀分布行的公式

为什么Java中的两个日期有差异?

Java取消任务运行Oracle查询通过JDBC—连接中断,因为SQLSTATE(08006),错误代码(17002)IO错误:套接字读取中断

在AnyLogic中增加变量计数

如何在访问完所有文件后加入所有线程?

ApachePOI:不带换行的新行

确定Java中Math.Ranb()输出的上限

内存中的H2修剪尾随空格

具有阻塞方法的开源库是否应该为执行提供异步选项?

Jenv-相同的Java版本,但带有前缀

使用Room Database删除Jetpack合成中的所有项目后,UI未重新合成

如何为JavaFX Spring Boot应用程序制作Windows/MacOS/Linux安装程序

STREAMS减少部分结果的问题

在打开搜索结果时,如何让Eclipse打开整个文件?

Cucumber java-maven-示例表-未定义一步

如何正确使用java.time类?

获取月份';s在java中非UTC时区的开始时间和结束时间

Maven创建带有特定类的Spring Boot jar和普通jar

在不带instanceof或switch的java中记录模式