我正在撰寫一個 Java 程式,它需要數千條System.out.println()陳述句,這些陳述句將在程式的整個生命周期中列印數億(或數十億)次以進行除錯:
if (GVar.runInDebugMode) System.out.println("Print debug message");
在現實世界中,可以停用這些陳述句以加快計算量大的計算。
如果我設定:
public final static boolean runInDebugMode = false;
runInDebugMode編譯器是否會在每次遇到如下陳述句時重新評估:if (GVar.runInDebugMode)或者由于它被宣告為 final,它將在程式開始時評估一次并且不會給 CPU 帶來額外的壓力?換句話說,一旦我部署應用程式或設定runInDebugMode 為false足夠,我會更好地完全注釋掉所有除錯陳述句嗎?
uj5u.com熱心網友回復:
當你宣告一個變數時
public final static boolean runInDebugMode = false;
它是一個編譯時常量。
常量變數是
final原始型別或String使用常量運算式(第 15.29 節)初始化的型別的變數。
意思就是
對作為常量變數(§4.12.4)的欄位的參考必須在編譯時決議
V為由常量變數的初始化程式表示的值。如果這樣的欄位是
static,那么二進制檔案的代碼中不應存在對該欄位的參考,包括宣告該欄位的類或介面。
換句話說,當你if(runInDebugMode)在任何地方寫并且runInDebugMode在false編譯時,行為就像你已經寫if(false)了一樣,因為值必須在編譯時決議,并且在編譯的類檔案中不會出現對該欄位的參考。
您的用例已在第 14.22 節中專門討論
但是,為了讓
if陳述句方便地用于“條件編譯”目的,實際規則有所不同。例如,以下陳述句會導致編譯時錯誤:
while (false) { x=3; }因為該陳述句
x=3;不可訪問;但表面上類似的情況:if (false) { x=3; }不會導致編譯時錯誤。優化編譯器可能會意識到該陳述句
x=3;將永遠不會被執行,并且可能會選擇從生成的class檔案中省略該陳述句的代碼,但在x=3;此處指定的技術意義上,該陳述句不被視為“不可訪問”。這種不同處理的基本原理是允許程式員定義“標志”變數,例如:
static final boolean DEBUG = false;然后撰寫代碼,例如:
if (DEBUG) { x=3; }這個想法是應該可以更改
DEBUGfromfalsetotrue或 fromtrueto的值false,然后正確編譯代碼,而不需要對程式文本進行其他更改。Conditional compilation comes with a caveat. If a set of classes that use a "flag" variable - or more precisely, any
staticconstant variable (§4.12.4) - are compiled and conditional code is omitted, it does not suffice later to distribute just a new version of the class or interface that contains the definition of the flag.
So, this statement makes clear that this form of conditional compilation matches the intent of the language designers and that compilers are entitled to omit the code in question (all relevant compilers do). In principle, a compiler is not required to omit the code, but since it must not generate a reference to the field GVar.runInDebugMode in the compiled code, the code can’t contain a real conditional. If the code is not omitted, it must be skipped in a de-facto unconditional way. Either, by a goto instruction or, when compiling in the most na?ve way imaginable, by literally testing false, iconst_0; ifeq …. Both approaches would be on the nanosecond scale in interpreted execution mode and no challenge to the JIT compiler/ optimizer at all.
It’s worth mentioning that static final fields are trusted fields which are normally not even changeable by Reflection. This is used, e.g. by the Assertion feature, as under the hood, a class containing an assert statement will have a static final boolean field initialized at class initialization time (so it’s not a compile-time constant) and each assert statement will skip its check conditionally, depending on the state of the static final variable. It was as early as at Java 1.4 time, when it was concluded that the necessary dead code elimination is commonplace in JVMs, to rely on it in this way.
因此,即使您將除錯標志從編譯時常量轉換為初始化時常量,對性能的影響也幾乎不會引起注意。但是您現在使用它的方式,代碼已經在編譯時被洗掉,并且無論如何都不依賴于 JVM。
uj5u.com熱心網友回復:
您所擁有的場景被描述為“條件編譯”,即如果常量變數為 false ,生成位元組碼的編譯器可以優化代碼:
“...為了讓 if 陳述句方便地用于“條件編譯”目的,實際規則有所不同。
例如,以下陳述句會導致編譯時錯誤:
while (false) { x=3; }
因為陳述句 x=3; 無法到達;但表面上類似的情況:
if (false) { x=3; }
不會導致編譯時錯誤。“ x=3;_ class_ _ x=3;_
請注意粗體部分:它取決于編譯器,但使用javap反匯編程式很容易驗證,只需運行javap -v -l -p MyClass.class以查看生成的位元組碼。我很確定 Oracle/OpenJDK javac 很久以前就進行了優化,大多數編譯器也是如此。請注意,這GVar.runInDebugMode是一個常量運算式,因此它受益于這種優化。
記住:
“條件編譯帶有一個警告。如果一組使用“標志”變數的類 - 或者更準確地說,任何靜態常量變數(第 4.12.4 節) - 被編譯并且條件代碼被省略,那么以后就不夠了只分發包含標志定義的類或介面的新版本。使用標志的類將看不到它的新值,因此它們的行為可能令人驚訝。本質上,對標志值的更改是二進制檔案與預先存在的二進制檔案兼容(不會發生 LinkageError),但在行為上不兼容。”
這基本上說除了定義它的類之外,您還必須重新編譯使用該標志的代碼。如果您構建整個代碼,這應該不是問題。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/444244.html
