知識點乾貨--內存優化【2】之JVM

“神農嘗百草”的故事不知道大家有沒有聽過,據說有很多個版本,說的挺有意思的,有興趣的可以查一下。
神農氏本是三皇(伏羲,神農,黃帝)之一。在女媧補天之後,不知過了多長時間,在烈山的一個石洞裏,出生了一個小孩。說來奇怪,在他剛出世,石洞周圍自然涌現了九眼井,這九眼井裏的水彼此相連,若取其中一眼之水,其它八眼皆會波動起來。這個孩子天生異相,身體是透明的,五臟六腑清晰可見,頭上長有兩隻角,牛頭人身。看到的人們都說這是天神下凡,九眼井是他帶來的。於是在他長大後,大家推舉他爲部落首領,因爲他們居住在炎熱的南方,就自稱炎族,稱他爲炎帝。有一次炎帝看見一隻紅色的鳥銜着一串像種子的東西,然後鳥兒把它吐了出來,炎帝走過去拾了起來,鳥兒圍住他飛了三圈,又唧唧啾啾地叫了一陣飛走了。炎帝認爲這是天帝派紅鳥送來的食物種子,便把種子埋在土裏。又用木頭製成耒耜,教人們鬆泥土,並掘井灌溉禾苗。這年秋天,一大片禾苗成熟了。人們非常的高興,於是大家感念炎帝的功德,都稱炎帝爲神農。這樣周邊的部落又稱炎帝部落爲神農部落,而稱他爲神農氏,即農業部落的首領。
神農氏看到人們經常得病,於是就到都廣之野登建木上天帝花園取瑤草,卻偶遇天帝,天帝贈給他一個神鞭,名爲赭鞭,神農拿着這根神鞭從都廣之野走了一路鞭了一路,然後回到了烈山。
相傳神農氏爲辨別各類草藥,就親自嘗試,最後試到一種含有劇毒的草藥(傳說爲斷腸草),無法可解,最終犧牲了生命。人們爲了紀念他的恩德和功績,奉他爲藥王神,並建藥王廟四時祭祀。在我國的川、鄂、陝交界傳說是神農嘗百草的地方,稱爲神農架山區。

上一篇講了內存簡介,因爲本系列主要介紹的是和Android相關的內存優化,所以有必要先熟悉一下Java虛擬機–JVM,本篇就來簡單講一下JVM的構造和垃圾回收機制。

1、JVM結構圖

下面這張圖是JVM的結構圖,主要由四個部分組成:

(1)、Class Loader子系統。

用來加載類文件,Java源代碼文件(.java後綴)被Java編譯器編譯爲字節碼文件(.class後綴),然後由JVM中的類加載器加載各個類的字節碼文件,加載完畢之後,交由JVM執行引擎執行。

(2)、運行時數據區(RUNTIME DATA AREA)。

執行引擎在執行過程中,JVM會用一段空間來存儲程序執行期間需要用到的數據和相關信息,這段空間被稱作爲RUNTIME DATA AREA(運行時數據區),也就是我們常說的JVM內存。

(3)、執行引擎。

負責對字節碼文件進行執行,輸入的是字節碼文件、處理過程是等效字節碼解析過程、輸出的是執行結果。Java虛擬機相當於一臺虛擬的“物理機”,而物理機和虛擬機都有代碼執行能力的,其區別主要是物理機的執行引擎是直接建立在處理器、硬件、指令集和操作系統層面上的。而JVM或Java虛擬機的執行引擎是程序員實現的,因此程序員可以自行制定指令集和執行引擎的結構體系,他能夠執行那些不被硬件直接支持的指令集格式。

而在JVM規範中制定了虛擬機字節碼執行引擎的概念模型,這個模型稱之爲JVM執行引擎的統一外觀。在JVM實現中,一般會有兩種執行方式:解釋執行(通過解釋器執行)和編譯執行(通過即時編譯器產生本地代碼)。有些虛擬機只採用一種執行方式,有些則可能同時採用兩種,甚至有可能包含幾個不同級別的編譯器執行引擎。

(4)、本地方法區。

存儲Native層的接口,這些接口通過NATIVE LIBRARIES獲取。JNI是Java Native Interface的縮寫,它提供了若干的API實現了Java和其他語言的通信(主要是C和C++)。

其中重點是運行時數據區(RUNTIME DATA AREA),也就是我們常說的JVM內存,而它主要由5個部分組成:

(1)、Method Area(方法區):

方法區在JVM中也是一個非常重要的區域,它與堆一樣,是被線程共享的區域。在方法區中,存儲了每個類的信息(包括類的名稱、方法信息、字段信息)、靜態變量、常量以及編譯器編譯後的代碼等。被裝載的class的元信息存儲在Method Area中,它是線程共享的。

(2)、Heap(堆):

一個Java虛擬機實例中只存在一個堆空間,它用來存放一些對象信息,它是線程共享的。在C語言中,堆這部分空間是唯一一個程序員可以管理的內存區域。程序員可以通過malloc函數和free函數在堆上申請和釋放空間。而Java中的堆是用來存儲對象本身的以及數組的(當然,數組引用是存放在Java棧中的)。只不過和C語言中的不同,在Java中,程序員基本不用去關心空間釋放的問題,Java的垃圾回收機制會自動進行處理。

(3)、Java棧:

Java棧也稱作虛擬機棧(Java Vitual Machine Stack),也就是我們常常所說的棧,跟C語言的數據段中的棧類似。Java虛擬機直接對Java棧進行兩種操作,以幀爲單位的壓棧和出棧(非線程共享) 操作。

(4)、程序計數器(非線程共享):

程序計數器(Program Counter Register),也有稱作爲PC寄存器。過彙編的同學對程序計數器應該不陌生,在彙編語言中,程序計數器是指CPU中的寄存器,它保存的是程序當前執行的指令的地址(也可以說保存下一條指令的所在存儲單元的地址),當CPU需要執行指令時,需要從程序計數器中得到當前需要執行的指令所在存儲單元的地址,然後根據得到的地址獲取到指令,在得到指令之後,程序計數器便自動加1或者根據轉移指針得到下一條指令的地址,如此循環,直至執行完所有的指令。

JVM中的程序計數器不像彙編語言中的程序計數器一樣是物理概念上的CPU寄存器,但是JVM中的程序計數器的功能跟彙編語言中的程序計數器的功能在邏輯上是等同的,也就是說可以用來指示執行哪條指令。

(5)、本地方法棧(非線程共享):

地方法棧與Java棧的作用和原理相似。區別不過是Java棧是爲執行Java方法服務的,而本地方法棧則是爲執行本地方法(Native Method)服務的。
在這裏插入圖片描述

2、垃圾回收器(GC)

JVM的垃圾回收原理是這樣的,它把對象分爲年輕代(Young)、年老代(Tenured)、持久代(Perm),對不同生命週期的對象使用不同的垃圾回收算法。

年輕代(Young)

年輕代分爲三個區,一個eden區,兩個Survivor區。程序中生成的大部分新的對象都在Eden區中,當Eden區滿時,還存活的對象將被複制到其中一個Survivor區,當此Survivor區的對象佔用空間滿了時,此區存活的對象又被複制到另外一個Survivor區,當這個Survivor區也滿了的時候,從第一個Survivor區複製過來的並且此時還存活的對象,將被複制到年老代。

年老代(Tenured)

年老代存放的是上面年輕代複製過來的對象,也就是在年輕代中還存活的對象,並且區滿了複製過來的。一般來說,年老代中的對象生命週期都比較長。

持久代(Perm)

用於存放靜態的類和方法,持久代對垃圾回收沒有顯著的影響。

如下圖所示,
在這裏插入圖片描述
我們先來想一想,爲什麼需要把堆對象分代?不分代能完成它所做的事情麼?答案是肯定的。

其實不分代完全可以,分代的唯一理由就是優化GC性能。

我們試想,如果沒有分代,那我們所有的堆對象都在一塊,GC的時候我們要找到哪些對象沒用,這樣就會對堆的所有區域進行掃描,而我們的很多對象都是朝生夕死的,效率會非常地低。如果分代的話,我們把新創建的對象放到某一地方,當GC的時候直接先把這塊存“朝生夕死”對象的區域進行回收,這樣就會節省大量的時間開銷,也會騰出很大的空間出來。

具體的回收算法如下所示,

JVM把年輕代分爲了三部分:1個Eden區和2個Survivor區(分別叫from和to)。1個Eden區和1個Survivor區的默認比例爲8:1(from和to大小相同,所以總的比例是8:2)。一般情況下,新創建的對象都會被分配到Eden區(一些大對象特殊處理),這些對象經過第一次GC後,如果仍然存活,將會被移到Survivor區。對象在Survivor區中每熬過一次GC,年齡就會增加1歲,當它的年齡增加到一定程度時,就會被移動到年老代中。

因爲年輕代中的對象基本都是朝生夕死的(80%以上),所以在年輕代的垃圾回收算法使用的是複製算法,複製算法的基本思想就是將內存分爲兩塊,每次只用其中一塊,當這一塊內存用完,就將還活着的對象複製到另外一塊上面,因此複製算法不會產生內存碎片。

在GC開始的時候,對象只會存在於Eden區和名爲“From”的Survivor區,Survivor區“To”是空的。緊接着進行GC,Eden區中所有存活的對象都會被複制到“To”,而在“From”區中,仍存活的對象會根據他們的年齡值來決定去向。年齡達到一定值(年齡閾值可以設置)的對象會被移動到年老代中,沒有達到閾值的對象會被複制到“To”區域。經過這次GC後,Eden區和From區已經被清空。這個時候,“From”和“To”會交換他們的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎樣,都會保證名爲To的Survivor區域是空的。Minor GC會一直重複這樣的過程,直到“To”區被填滿,“To”區被填滿之後,會將所有對象移動到年老代中。

綜上就是JVM的構造圖和垃圾回收器及原理的簡單介紹,很多公司在面試招人時一般都會問到這兩個方面的知識,如果把以上總結的內容都掌握,基本上再回答這類問題時就會顯得遊刃有餘了。下篇我們將介紹Dalvik虛擬機,敬請期待。

本公衆號將以推送Android各種技術乾貨或碎片化知識,以及整理老司機日常工作中踩過的坑涉及到的經驗知識爲主,也會不定期將正在學習使用的新技術總結出來進行分享。每天一點乾貨小知識把你的碎片時間充分利用起來。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章