一、模型圖

二、JVM內(nèi)存分區(qū)
JVM運行時數(shù)據(jù)區(qū)的內(nèi)存有:方法區(qū)、Java堆、Java棧、本地方法棧、程序計數(shù)器
方法區(qū):方法區(qū)是被所有線程共享的內(nèi)存區(qū)域,用來存儲已被虛擬機加載的類信息、常量、靜態(tài)變量、JTI(just in time,即時編譯技術(shù))編譯后的代碼等數(shù)據(jù)。運行時常量池是方法區(qū)的一部分,用于存放編譯期間生成的各種字面常量和符號引用。平時通過反射獲取到的類型、方法名、字段名稱、訪問修飾符等信息就是從方法區(qū)獲取到的。在使用到CGLib對類進行增強時,增強的類越多,就需要越大的方法區(qū)類存儲動態(tài)生成的Class信息,當存放方法區(qū)數(shù)據(jù)的內(nèi)存溢出時,會報OutOfMemoryError異常,在jdk1.8中也就是Metaspace內(nèi)存溢出,可以通過參數(shù)JVM參數(shù)-XX:MetaspaceSize和-XX:MaxMetaspaceSize設置Metaspace的空間大小。
-
Java堆(Heap):和方法區(qū)一樣是被所有線程共享的內(nèi)存區(qū)域,是JVM中最大的一塊內(nèi)存區(qū)域,幾乎所有的對象實例都是在這個區(qū)域進行內(nèi)存的分配的,為對象分配內(nèi)存主要有兩種方式:
- 指針碰撞法:把堆中的內(nèi)存進行劃分(已分配的內(nèi)存+空閑的內(nèi)存),通過指針作為分界點,當需要分內(nèi)存時,把指向空閑內(nèi)存移動與對象大小相等的距離。
- 空閑列表法:JVM通過維護一個列表,記錄可用的內(nèi)存塊信息,當分配操作發(fā)生時,從列表中找到一個足夠大的內(nèi)存分配給對象實例,并更新列表上的記錄。
-
Java棧(Java Stack):又稱之為虛擬機棧,存放的是棧幀(棧的基礎單位), 是用來存儲數(shù)據(jù)和部分過程結(jié)果的數(shù)據(jù)結(jié)構(gòu)。
Java棧.png
其中:
1.局部變量表:用于存儲方法參數(shù)和方法內(nèi)部定義的局部變量,對于非static方法,局部變量表存儲的第0個位置存儲的為當前對象的引用,可以通過關(guān)鍵字this進行訪問,方法參數(shù)按照參數(shù)列表順序,從第1個位置分配下去;
2.操作數(shù)棧:用于操作運行過程中的各種中間結(jié)果與字節(jié)碼指令;
3.動態(tài)鏈接:程序運行過程中,將符號引用解析為直接引用,例如多態(tài),對象的引用在程序運行時鏈接到具體的實現(xiàn)類;
4.出口記錄:程序的出口 本地方法棧(Native Method Stack)
本地方法棧是與Java棧發(fā)揮的作用十分相似,區(qū)別是Java棧執(zhí)行的是Java方法(也就是字節(jié)碼)服務,而本地方法棧則為虛擬機使用到的本地方法服務,可能底層調(diào)用的c或者c++,我們打開jdk安裝目錄可以看到也有很多用c編寫的文件,可能就是本地方法所調(diào)用的c代碼。程序計數(shù)器(Program Counter Register):用來記錄當前方法執(zhí)行的情況,為了在線程切換可以恢復到正確執(zhí)行位置,每個線程都需有獨立的一個程序計數(shù)器,不同線程之間的程序計數(shù)器互不影響,獨立存儲。
注意:如果線程執(zhí)行的是個java方法,那么計數(shù)器記錄虛擬機字節(jié)碼指令的地址。如果為native【底層方法】,那么計數(shù)器為空。這塊內(nèi)存區(qū)域是虛擬機規(guī)范中唯一沒有OutOfMemoryError的區(qū)域。
注:Java棧、本地方法棧、程序計數(shù)器生命周期一樣,都是線程私有,隨線程的創(chuàng)建而創(chuàng)建
