1. 程式人生 > >【朝花夕拾】Android效能優化篇之(一)序言及JVM篇

【朝花夕拾】Android效能優化篇之(一)序言及JVM篇

序言

筆者從事Anroid開發有些年頭了,深知掌握Anroid效能優化方面的知識的必要性,這是一個程式設計師必須修煉的內功。在面試中,它是面試官的摯愛,在工作中,它是程式碼質量的攔路虎,其重要性可見一斑。在團隊中,效能優化的工作又往往由經驗豐富的老師傅來完成,可見要做好效能優化,絕不是一件容易的事情。

       效能優化方面涉及的知識點比較廣,有理論基礎知識,也有實際操作技能,筆者將通過一系列的文章來進行整理,將主要包括Java虛擬機器、記憶體分配、垃圾回收,android虛擬機器、程序管理、記憶體優化、記憶體洩漏,常用記憶體分析及優化工具介紹等。由於筆者水平和經驗有限,也是在邊研究邊整理,有不當之處,請不吝賜教。

        當前系列內容已完成如下篇章:

前言

        之所以在講記憶體之前先介紹JVM,是因為JVM就像盤古一樣開天闢地,在機器上開闢了一個虛擬的空間,然後才有了java生存的土壤。該系列文章的主角——記憶體就是JVM中的一部分。同時,這也是中高階程式設計師,架構師,系統調優師等職位所必須要了解甚至深入掌握的知識點。

一、什麼是JVM?

       咱們這裡借鑑百度百科的解釋:

       JVM是Java Virtual Machine 的縮寫,即Java虛擬機器,它是一種用於計算裝置的規範,通過在實際的計算機上模擬模擬各種計算機功能來實現,可以簡單理解為,它是一個在實際計算機中虛構出來的寄生計算機。Java原始檔經過編譯器編譯成.class檔案(位元組碼檔案)後,由JVM來執行。JVM擁有一套支援java位元組碼檔案(.class檔案)執行的環境,可以將.class檔案解釋成具體平臺上的機器指令並執行。Java程式只需要生成.class檔案,即可以在任何裝有JVM的裝置上執行,而不需要理會具體作業系統平臺相關的資訊。此時的JVM就像一個翻譯官一樣連線連線著.class檔案和系統平臺。Java的這個特性叫做平臺無關性,實現了“一次編譯,到處執行”,JVM就是實現Java這個功能的大功臣。

二、JDK,JRE,JVM是什麼關係?

       如下截圖為JDK安裝包結構

        

  1、JDK

      Java Development Kit 的縮寫,即 Java開發工具包,提供了開發java程式所需要的基本條件。在上圖JDK安裝包截圖的紅色邊框中

  • bin:最主要包含了編輯器javac.exe,功能是將java原始檔編譯為.class檔案。
  • include:包含了Java和JVM互動用的標頭檔案。
  • lib:包含了ava開發所需的類庫。
  • jre:為Java執行環境。

  2、JRE

       Java Runtime Environment的縮寫,即Java執行環境,下圖為JRE資料夾的內容。注意,JRE是執行環境,而不是開發環境。

     

  • bin:可以理解為JVM,其中javaw.exe或java.exe用於執行.class檔案。

  • lib:提供了JVM執行所需要的庫             

  3、JVM

        執行.class檔案。

  4、結論

        這三者的關係可以通過下圖(摘自某網站)來理解:JDK包含了JRE,JRE又包含了JVM(對於比較懶得童鞋,可以先記住這個結論)。圖中提到的Java開發工具,整合有類庫和編譯工具,只需關聯jre即可。                

       JDK、JRE、JVM三者的區別與聯絡 - 以德糊人 - ——挨踢民工 Playkid—— 

三、JVM執行Java程式的流程        

       下圖顯示了JVM在jiava程式工作流中的位置,可以結合前面的內容來看。

    

       一個JVM例項對應一個獨立執行的Java程式,是屬於程序級別的,也有自己的生命週期:

  1、啟動

        當啟動一個Java程式的時候,就產生了一個JVM例項。我們還記得Java的入口函式 public static void main(String[] args)嗎?每個擁有該函式的.class都可以作為JVM例項執行的起點。

  2、執行

        main()作為起點,啟動主執行緒,其他任何執行緒都在該執行緒中啟動。JVM有兩種執行緒:User Thread(使用者執行緒)和Daemon Thread(守護執行緒:為使用者執行緒服務的執行緒),main()執行緒為使用者執行緒,而守護執行緒通常由JVM自己使用。

  3、消亡

        當程式中的所有使用者執行緒都終止時,JVM才會退出,守護執行緒無執行緒需要服務了,也就跟著退出了歷史的舞臺。若安全管理器允許,程式也可以使用Runtime或者System.exit()來退出。

四、JVM架構及工作原理

       

       上圖更詳細地展示了JVM的架構,由圖可知,JVM主要是劃分為三個子系統 :(1)類載入器子系統 (2)執行時資料區 (3)執行引擎 和Java本地介面、本地方法庫。

  1、類載入器子系統(Class Loader SubSystem)

       Java的動態類載入功能由該子系統處理,當它在執行時(不是編譯時)首次引用一個類時,它載入、連結並初始化該類檔案(.class檔案)。

    (1)載入(Loading)

        類的載入在此元件中完成,載入過程主要由下面三個載入器實現:

        1)啟動類載入器(BootStrap class Loader)。負責從啟動類路徑中載入類,無非就是rt.jar。這個載入器會被賦予最高優先順序。

        2)擴充套件類載入器(Extension class Loader)。負責載入ext目錄(jre\lib)內的類。

        3)應用程式類載入器(Application class Loader)。負責載入應用程式級別類路徑,涉及到路徑的環境變數等。

        這三個類載入器會遵循委託層次演算法(Delegation Hierarchy Algorithm)載入類檔案。

    (2)連結(Linking)

        該過程主要完成以下三個步驟:

        1)校驗(Verify)。位元組碼校驗器會校驗生成的位元組碼是否正確,如果校驗失敗,我們會得到校驗錯誤。

        2)準備(Prepare)。分配記憶體並初始化預設值給所有的靜態變數。

        3)解析(Resolve)。所有符號記憶體引用被方法區(Method Area)的原始引用所替代。

    (3)初始化(Initialization)

       這是類載入的最後階段,這裡所有的靜態變數會賦初始值,並且靜態塊將被執行。

  2、執行時資料區(Runtime Data Area)

  3、執行引擎(Execution Engine)。

        分配給執行時資料區的位元組碼將由執行引擎執行,執行引擎讀取位元組碼並逐段執行。它包含了如下三個部分:直譯器、編譯器、垃圾回收器。

    (1)直譯器(interpreter)。

        直譯器能夠快速地解釋位元組碼,但執行卻很慢。它的一個缺點就是,當一個方法被呼叫多次,每次都需要重新解釋。

    (2)JIT編譯器。

        JIT編譯器消除了直譯器的缺點,執行引擎利用直譯器轉換位元組碼,但如果是重複的程式碼,則使用JIT編譯器將全部位元組碼編譯成本機程式碼,本機程式碼將直接用於重複的方法呼叫,這樣提高了系統的效能。它的工作由下面4個工具協同完成:

        1)中間程式碼生成器。負責生成中間程式碼。

        2)程式碼優化器。負責優化上面生成的中間程式碼。

        3)目的碼生成器。負責生成機器程式碼或本機程式碼。

        4)探測器(Profiler)。一個特殊的元件,負責尋找被多次呼叫的方法。

    (3)垃圾回收器。

       蒐集並刪除未引用的物件。可以通過呼叫“System.gc()”來觸發垃圾回收,但並不保證會確實進行垃圾回收。JVM的垃圾回收只收集那些由new關鍵字建立的物件。所以,如果不是用new建立的物件,你可以使用finalize函式來執行清理。

  4、Java本地介面(JNI,Native Method Interface Interface)

       JNI會與本地方法庫進行互動並提供執行引擎所需的本地庫。

  5、本地方法庫(Native Method Library)

      它是一個執行引擎的本地庫集合。 

五、參考資料

       本文主要參考瞭如下內容(吃水不忘挖井人嘛,要尊重原作者)  

       最後推薦一本術,周志明著的《深入理解Java虛擬機器——JVM高階特性與最佳實踐》,業內評價很高,口碑非常好。