掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
JVM 整體架構(gòu),中間部分就是 Java 虛擬機定義的各種運行時數(shù)據(jù)區(qū)域。

創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供銅官網(wǎng)站建設(shè)、銅官做網(wǎng)站、銅官網(wǎng)站設(shè)計、銅官網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、銅官企業(yè)網(wǎng)站模板建站服務,10年銅官做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務。
圖片
Java 虛擬機定義了若干種程序運行期間會使用到的運行時數(shù)據(jù)區(qū),其中有一些會隨著虛擬機啟動而創(chuàng)建,隨著虛擬機退出而銷毀。另外一些則是與線程一一對應的,這些與線程一一對應的數(shù)據(jù)區(qū)域會隨著線程開始和結(jié)束而創(chuàng)建和銷毀。
線程私有:程序計數(shù)器、虛擬機棧、本地方法區(qū)
線程共享:堆、方法區(qū), 堆外內(nèi)存(Java7的永久代或JDK8的元空間、代碼緩存)
什么是程序計數(shù)器(線程私有)?
PC 寄存器用來存儲指向下一條指令的地址,即將要執(zhí)行的指令代碼。由執(zhí)行引擎讀取下一條指令。
多線程在一個特定的時間段內(nèi)只會執(zhí)行其中某一個線程方法,CPU會不停的做任務切換,這樣必然會導致經(jīng)常中斷或恢復。為了能夠準確的記錄各個線程正在執(zhí)行的當前字節(jié)碼指令地址,所以為每個線程都分配了一個PC寄存器,每個線程都獨立計算,不會互相影響。
主管 Java 程序的運行,它保存方法的局部變量、部分結(jié)果,并參與方法的調(diào)用和返回。每個線程在創(chuàng)建的時候都會創(chuàng)建一個虛擬機棧,其內(nèi)部保存一個個的棧幀(Stack Frame),對應著一次次 Java 方法調(diào)用,是線程私有的,生命周期和線程一致。
特點?
該區(qū)域有哪些異常?
棧幀的內(nèi)部結(jié)構(gòu)?
圖片
以如下代碼為例:
private static int add(int a, int b) {
int c = 0;
c = a + b;
return c;
}可以通過jsclass 等工具查看bytecode
圖片
壓棧的步驟如下:
0: iconst_0 // 0壓棧
1: istore_2 // 彈出int,存放于局部變量2
2: iload_0 // 把局部變量0壓棧
3: iload_1 // 局部變量1壓棧
4: iadd //彈出2個變量,求和,結(jié)果壓棧
5: istore_2 //彈出結(jié)果,放于局部變量2
6: iload_2 //局部變量2壓棧
7: ireturn //返回如果計算100+98的值,那么操作數(shù)棧的變化如下圖:
圖片
一個 Native Method 就是一個 Java 調(diào)用非 Java 代碼的接口。我們知道的 Unsafe 類就有很多本地方法。
Java 虛擬機棧用于管理 Java 方法的調(diào)用,而本地方法棧用于管理本地方法的調(diào)用
方法區(qū)(method area)只是 JVM 規(guī)范中定義的一個概念,用于存儲類信息、常量池、靜態(tài)變量、JIT編譯后的代碼等數(shù)據(jù),并沒有規(guī)定如何去實現(xiàn)它,不同的廠商有不同的實現(xiàn)。而永久代(PermGen)**是 **Hotspot** 虛擬機特有的概念, Java8 的時候又被**元空間取代了,永久代和元空間都可以理解為方法區(qū)的落地實現(xiàn)。
JDK1.8之前調(diào)節(jié)方法區(qū)大?。?/p>
-XX:PermSize=N //方法區(qū)(永久代)初始大小
-XX:MaxPermSize=N //方法區(qū)(永久代)最大大小,超出這個值將會拋出OutOfMemoryErrorJDK1.8開始方法區(qū)(HotSpot的永久代)被徹底刪除了,取而代之的是元空間,元空間直接使用的是本機內(nèi)存。參數(shù)設(shè)置:
-XX:MetaspaceSize=N //設(shè)置Metaspace的初始(和最小大小)
-XX:MaxMetaspaceSize=N //設(shè)置Metaspace的最大大小圖片
Java虛擬機規(guī)范中只定義了方法區(qū)用于存儲已被虛擬機加載的類信息、常量、靜態(tài)變量和即時編譯后的代碼等數(shù)據(jù)
對于大多數(shù)應用,Java 堆是 Java 虛擬機管理的內(nèi)存中最大的一塊,被所有線程共享。此內(nèi)存區(qū)域的唯一目的就是存放對象實例,幾乎所有的對象實例以及數(shù)據(jù)都在這里分配內(nèi)存。
為了進行高效的垃圾回收,虛擬機把堆內(nèi)存邏輯上劃分成三塊區(qū)域(分代的唯一理由就是優(yōu)化 GC 性能):
Java 虛擬機規(guī)范規(guī)定,Java 堆可以是處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯上是連續(xù)的即可,像磁盤空間一樣。實現(xiàn)時,既可以是固定大小,也可以是可擴展的,主流虛擬機都是可擴展的(通過 -Xmx 和 -Xms 控制),如果堆中沒有完成實例分配,并且堆無法再擴展時,就會拋出 OutOfMemoryError 異常。
年輕代是所有新對象創(chuàng)建的地方。當填充年輕代時,執(zhí)行垃圾收集。這種垃圾收集稱為 Minor GC。年輕一代被分為三個部分——伊甸園(Eden Memory)和兩個幸存區(qū)(Survivor Memory,被稱為from/to或s0/s1),默認比例是8:1:1
舊的一代內(nèi)存包含那些經(jīng)過許多輪小型 GC 后仍然存活的對象。通常,垃圾收集是在老年代內(nèi)存滿時執(zhí)行的。老年代垃圾收集稱為 主GC(Major GC),通常需要更長的時間。
大對象直接進入老年代(大對象是指需要大量連續(xù)內(nèi)存空間的對象)。這樣做的目的是避免在 Eden 區(qū)和兩個Survivor 區(qū)之間發(fā)生大量的內(nèi)存拷貝
圖片
新生代又被進一步劃分為 Eden區(qū) 和 Survivor區(qū),Survivor 區(qū)由 From Survivor 和 To Survivor 組成
此時 JVM 會給對象定義一個對象年輕計數(shù)器(-XX:MaxTenuringThreshold)
JVM 會把存活的對象轉(zhuǎn)移到 Survivor 中,并且對象年齡 +1
對象在 Survivor 中同樣也會經(jīng)歷 Minor GC,每經(jīng)歷一次 Minor GC,對象年齡都會+1
為對象分配內(nèi)存是一件非常嚴謹和復雜的任務,JVM 的設(shè)計者們不僅需要考慮內(nèi)存如何分配、在哪里分配等問題,并且由于內(nèi)存分配算法和內(nèi)存回收算法密切相關(guān),所以還需要考慮 GC 執(zhí)行完內(nèi)存回收后是否會在內(nèi)存空間中產(chǎn)生內(nèi)存碎片。
盡管不是所有的對象實例都能夠在 TLAB 中成功分配內(nèi)存,但 JVM 確實是將 TLAB 作為內(nèi)存分配的選擇。
在程序中,可以通過 -XX:UseTLAB 設(shè)置是否開啟 TLAB 空間。
默認情況下,TLAB 空間的內(nèi)存非常小,僅占有整個 Eden 空間的 1%,我們可以通過 -XX:TLABWasteTargetPercent 設(shè)置 TLAB 空間所占用 Eden 空間的百分比大小。
一旦對象在 TLAB 空間分配內(nèi)存失敗時,JVM 就會嘗試著通過使用加鎖機制確保數(shù)據(jù)操作的原子性,從而直接在 Eden 空間中分配內(nèi)存。

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流