成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

JVM JIT編譯能改變某些反射的執(zhí)行結(jié)果

lcodecorex / 1970人閱讀

摘要:某個(gè)測(cè)試服務(wù)器試圖通過(guò)反射來(lái)修改變量的值,出現(xiàn)了時(shí)靈時(shí)不靈的現(xiàn)象。這個(gè)閾值隨時(shí)會(huì)變,只是測(cè)著玩的編譯是可以取消的,現(xiàn)在修改如下,在用反射設(shè)值后,再次執(zhí)行萬(wàn)次直接取值現(xiàn)在的執(zhí)行結(jié)果又是了。結(jié)論不要修改變量,會(huì)出問(wèn)題的關(guān)于編譯期優(yōu)化的更多知識(shí)

某個(gè)測(cè)試服務(wù)器試圖通過(guò)反射來(lái)修改static final變量的值,出現(xiàn)了時(shí)靈時(shí)不靈的現(xiàn)象。

開發(fā)環(huán)境無(wú)法重現(xiàn)。這是怎么回事呢?

先介紹背景知識(shí)

一般認(rèn)為,static final常量會(huì)被編譯器執(zhí)行內(nèi)聯(lián)優(yōu)化,即它的值會(huì)被內(nèi)聯(lián)到調(diào)用位置。

這對(duì)于如下方式初始化的字面常量有效:

private static final boolean MY_VALUE = false;

但對(duì)于如下方式初始化的運(yùn)行時(shí)常量無(wú)效:

private static final boolean MY_VALUE = System.getProperty("dsasdkdfskdsdfk") != null;

為什么會(huì)不一樣呢?因?yàn)榈谝环N方式字面量(literal, 硬編碼在代碼里的值,可以是布爾值、數(shù)值、字符串等等)是編譯時(shí)就能確定的,而第二種方式的值是某個(gè)調(diào)用的返回值,直到運(yùn)行的那一刻才確定。

具體的常量?jī)?yōu)化規(guī)則可參考語(yǔ)言規(guī)范:http://docs.oracle.com/javase...

然后我就發(fā)現(xiàn)一個(gè)危險(xiǎn)現(xiàn)象:引用自另一個(gè)jar的常量也會(huì)被內(nèi)聯(lián)!

如果你引用一個(gè)第三方庫(kù)中的常量,然后升級(jí)了這個(gè)庫(kù)的版本,新版本改變了常量的值,那么你的程序就錯(cuò)了!除非你重新編譯你的程序!

有時(shí)候這是很隱蔽的!例如你引用的是Tomcat的一個(gè)常量,然后你直接把程序放在新版本的Tomcat中運(yùn)行!

然后解決當(dāng)前的問(wèn)題

服務(wù)器上的問(wèn)題是:用反射強(qiáng)行修改static final變量的值,用反射能取得修改后的值,然而Java調(diào)用直接取得的值卻仍是舊值。

可用如下Test.java MyEnv.java兩個(gè)文件來(lái)重現(xiàn),但是在開發(fā)環(huán)境并沒(méi)有重現(xiàn)出問(wèn)題:

Test.java

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class Test {
  public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    Field myField = MyEnv.class.getDeclaredField("MY_VALUE");
    myField.setAccessible(true);
    
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(myField, myField.getModifiers() & ~Modifier.FINAL);
    
    myField.set(null, true);

    System.out.println("Get via reflection: " + myField.get(null)); // true on the server
    System.out.println("Get directly:" + MyEnv.getValue()); // false on the server
  }
}

MyEnv.java

public class MyEnv {
 private static final boolean MY_VALUE = System.getProperty("dsasdkdfskdsdfk") != null;
 
 public static boolean getValue() {
 return MY_VALUE;
 }
}

按照語(yǔ)言規(guī)范里的編譯器常量?jī)?yōu)化規(guī)則,這個(gè)常量不會(huì)被內(nèi)聯(lián),所以開發(fā)環(huán)境的執(zhí)行結(jié)果(兩個(gè)都是true)似乎是對(duì)的?

但是JVM有運(yùn)行時(shí)優(yōu)化——當(dāng)代碼頻繁執(zhí)行時(shí),會(huì)觸發(fā)JIT編譯!

我們修改Test.java如下,執(zhí)行了10萬(wàn)次直接取值:

Test.java

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class Test {
  public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    for (int i = 0; i < 100000; i++) {
      MyEnv.getValue();
    }
    Field myField = MyEnv.class.getDeclaredField("MY_VALUE");
    myField.setAccessible(true);
 
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(myField, myField.getModifiers() & ~Modifier.FINAL);
 
    myField.set(null, true);

    System.out.println("Get via reflection: " + myField.get(null)); // true on the server
    System.out.println("Get directly:" + MyEnv.getValue()); // false on the server
  }
}

現(xiàn)在的執(zhí)行結(jié)果是true, false,重現(xiàn)了服務(wù)器的問(wèn)題。原因是JVM在運(yùn)行時(shí)通過(guò)JIT編譯再次內(nèi)聯(lián)了常量。

在我的電腦上,觸發(fā)這個(gè)JIT編譯的閾值是15239,遠(yuǎn)小于10萬(wàn)。(這個(gè)閾值隨時(shí)會(huì)變,只是測(cè)著玩的)

JIT編譯是可以取消的,現(xiàn)在修改Test.java如下,在用反射設(shè)值后,再次執(zhí)行10萬(wàn)次直接取值:

public class Test {
  public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    for (int i = 0; i < 100000; i++) {
      MyEnv.getValue();
    }
    Field myField = MyEnv.class.getDeclaredField("MY_VALUE");
    myField.setAccessible(true);
 
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(myField, myField.getModifiers() & ~Modifier.FINAL);
 
    myField.set(null, true);
    for (int i = 0; i < 100000; i++) {
      MyEnv.getValue();
    }
   System.out.println("Get via reflection: " + myField.get(null)); // true on the server
   System.out.println("Get directly:" + MyEnv.getValue()); // false on the server
  }
}

現(xiàn)在的執(zhí)行結(jié)果又是true, true了。
與其說(shuō)是取消了JIT,不如說(shuō)是觸發(fā)了新一次JIT!可以用代碼驗(yàn)證這一推測(cè),這個(gè)就留作思考題了:)
(注意,要想觸發(fā)新的JIT,需要更大量的執(zhí)行次數(shù)。)

結(jié)論:不要修改final變量,會(huì)出問(wèn)題的!

關(guān)于編譯期優(yōu)化的更多知識(shí) https://briangordon.github.io...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/67623.html

相關(guān)文章

  • JAVA運(yùn)行時(shí)簡(jiǎn)述(HotSpot)

    摘要:拆解虛擬機(jī)的基本步聚如下首先,要等待到自身成為唯一一個(gè)正在運(yùn)行的非守護(hù)線程時(shí),在整個(gè)等待過(guò)程中,虛擬機(jī)仍舊是可工作的。將相應(yīng)的事件發(fā)送給,禁用,并終止信號(hào)線程。 本文簡(jiǎn)單介紹HotSpot虛擬機(jī)運(yùn)行時(shí)子系統(tǒng),內(nèi)容來(lái)自不同的版本,因此可能會(huì)與最新版本之間(當(dāng)前為JDK12)存在一些誤差。 1.命令行參數(shù)處理HotSpot虛擬機(jī)中有大量的可影響性能的命令行屬性,可根據(jù)他們的消費(fèi)者進(jìn)行簡(jiǎn)...

    hosition 評(píng)論0 收藏0
  • Class對(duì)象和Java反射機(jī)制

    摘要:四后記理解好對(duì)象不僅能讓我們更好的認(rèn)識(shí)一切皆對(duì)象這個(gè)觀點(diǎn),對(duì)之后學(xué)習(xí)泛型,類型擦除都是很有幫助的,而對(duì)于反射機(jī)制我們只需在適當(dāng)?shù)膱?chǎng)合利用它即可。 一 前言 很多書上都說(shuō),在java的世界里,一切皆對(duì)象。其實(shí)從某種意義上說(shuō),在java中有兩種對(duì)象:實(shí)例對(duì)象和Class對(duì)象。實(shí)例對(duì)象就是我們平常定義的一個(gè)類的實(shí)例: /** * Created by aristark on 3/28/16...

    Rainie 評(píng)論0 收藏0
  • 吃透這套Java面試題,拿offer成功率再翻一番

    摘要:語(yǔ)言通過(guò)字節(jié)碼的方式,在一定程度上解決了傳統(tǒng)解釋型語(yǔ)言執(zhí)行效率低的問(wèn)題,同時(shí)又保留了解釋型語(yǔ)言可移植的特點(diǎn)。有針對(duì)不同系統(tǒng)的特定實(shí)現(xiàn),,,目的是使用相同的字節(jié)碼,它們都會(huì)給出相同的結(jié)果。 showImg(https://segmentfault.com/img/bVbsjCK?w=800&h=450); 一、面向?qū)ο蠛兔嫦蜻^(guò)程的區(qū)別 面向過(guò)程優(yōu)點(diǎn): 性能比面向?qū)ο蟾?,因?yàn)轭愓{(diào)用時(shí)需要實(shí)...

    elva 評(píng)論0 收藏0
  • Java編程中那些再熟悉不過(guò)知識(shí)點(diǎn)(持續(xù)更新)

    摘要:語(yǔ)言通過(guò)字節(jié)碼的方式,在一定程度上解決了傳統(tǒng)解釋型語(yǔ)言執(zhí)行效率低的問(wèn)題,同時(shí)又保留了解釋型語(yǔ)言可移植的特點(diǎn)。有針對(duì)不同系統(tǒng)的特定實(shí)現(xiàn),,,目的是使用相同的字節(jié)碼,它們都會(huì)給出相同的結(jié)果。項(xiàng)目主要基于捐贈(zèng)的源代碼。 本文來(lái)自于我的慕課網(wǎng)手記:Java編程中那些再熟悉不過(guò)的知識(shí)點(diǎn),轉(zhuǎn)載請(qǐng)保留鏈接 ;) 1. 面向?qū)ο蠛兔嫦蜻^(guò)程的區(qū)別 面向過(guò)程 優(yōu)點(diǎn): 性能比面向?qū)ο蟾?。因?yàn)轭愓{(diào)用時(shí)需要實(shí)例...

    taowen 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<