從編譯角度看java域和方法

來源:互聯網
上載者:User

上圖展示了,我們java類是如何從java位元組碼檔案中載入到記憶體中,再從記憶體中解析到方法區中的,然後被使用的過程。 java域的編譯

而我們的java域初始化是在解析和初始化這兩個階段完成的,解析先把域中基本類型和String類型初始化為0和“”,域中參考型別初始化為null(系統預設設定),然後在初始化階段進行clinit()和init()的初始化(使用者代碼設定)。
而域又分為靜態域和非靜態域。
靜態域:在clinit()中初始化,屬於類變數被儲存在方法區中。
非靜態域:在init()中初始化,屬於執行個體變數儲存在堆中對象中。

程式碼範例:


編譯後的代碼,可以看到初始化的順序:

代碼塊執行的時機:靜態代碼塊在編譯期間被添加到clinit()中,非靜態代碼塊編譯期間被添加到init()中,添加的順序和代碼塊在代碼中的順序是一致的。

得出的結論
只有一個類被載入之後才會按圖示進行接下來的操作,而我們的靜態域是屬於方法區的,每個類對應這一個class執行個體,而我們的執行個體對象就是根據這個class執行個體來產生的,因此靜態域的初始化只有一次,而我們的非靜態域是和執行個體對應的,處於對象中,也就是建立一個對象就初始化一次。 final static域的編譯

final static靜態常量域,分為兩個情況,第一就是final static修飾的變數類型且初始化的資料是常量類型和基本類型,那麼這個變數就是在編譯期間就被初始化的,因為編譯過程中常量是存在常量池中可以確定的,虛擬機器會把這些變數當做常量,如果是參考型別的,那麼就需要在clinit()初始化,它的初始化和static的初始化是一樣道理。

如下代碼所示:

編譯後的代碼:

我們可以看 i2和s2都沒有在clinit()中,其他的非final 的靜態域都在clinit()中初始化了。這就要分辨“haha”和 new String(“heihei”);的區別了,前者是常量字串,後者是參考型別的對象。實際上,類在初始化步驟之前會在解析階段初始化系統預設值,而我們的final static修飾的變數系統預設值是我們看到的“haha”,2;而其他的預設值就是null或者0或者“”;接下來就會在初始化階段執行clinit()和init()初始化函數。

結論
- final static 修飾的原始類型和Sring類型(非參考型別),並不會被翻譯在clinit方法中,而是在類初始化步驟之前執行initSFields方法時候得到初始化賦值。
- final static 修飾的參考型別,初始化任然在clinit方法中。

誤區:常常看到如果一個field是常量,那麼推薦使用final static修飾。很明顯這句話有問題,得到最佳化的僅僅是final static的原始類型和String類型域(非參考型別),如果是參考型別那麼還是不會有什麼最佳化的。

java中解析一個域的話,必須確認該域所在的類被解析了,而解析一個類還要保證自己的父類被載入並且被解析,該類的被載入並且解析後,就可以拿到域的索引,然後就可以得到域了。 在熱部署中,我們的final static域如果是基本類型或者是String(非引用 類型)是可以被替換的,因為可以找到方法區中的常量進行替換。 我們的final static域是個參考型別的話,是不允許的,因為這個fielld的初始化是在clinit()中進行的,我們知道類從 載入到使用只會調用一次clinit()函數,這樣就沒辦法修改了。 java方法的編譯

在類被解析之後,會為該類在方法區中產生一個對應的虛方法表。除了內部類和匿名內部類可能會造成method的新增,我們發現項目中如果應用了混淆,也可能導致方法的內聯和裁剪,那麼最後也會導致method的新增/減少,一下介紹哪些情境會造成方法的內聯和裁剪。

方法內聯 方法沒有被其他任何地方應用到,毫無疑問,方法會被內聯掉 方法足夠簡單,比如一個方法的實現就只要一行,該方法會被內聯掉,那麼任何調用該方法的地方都會被該方法的實現替換掉。

方法只被一個地方引用到,這個地方會被方法的實現替換掉。

程式碼範例 如下:

那麼編譯後會發現不會出現print方法,因為滿足只被一個地方引用的條件。

方法裁剪

當方法的參數沒有被使用過,那麼該參數就會被裁剪掉。
如下代碼所示:

編譯後的代碼:

會發現編譯後的代碼中方法中不存在context參數,該參數被裁剪掉了,有什麼辦法可以保證方法不被裁剪掉嗎。當然有,如下代碼:

注意這裡不能用基本類型false,必須用封裝類Boolean,因為如果寫基本類型,if語句可能會被最佳化掉。

在熱部署中,最好不要裁剪和內聯,因為會導致方法的減少或者增加,裁剪導致method增加,因為新補丁內是有參數的會被認為是新方法,故統一各類增加了新方法;內聯導致method減少,因為滿足條件的方法會被合成到被調用的方法中,這樣方法數就減少了一個。在熱部署中是不允許域/方法的新增和減少的。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.