深入理解JVM內(nèi)存模型

JVM 內(nèi)存模型

運(yùn)行時(shí)數(shù)據(jù)區(qū)域

程序計(jì)數(shù)器(Program Conunter Regisiter)

程序計(jì)數(shù)器是一個(gè)比較小的內(nèi)存空間球碉,可以看作是當(dāng)前線程執(zhí)行的字節(jié)碼行號(hào)指示器霎箍。本質(zhì)就是記錄字節(jié)碼執(zhí)行順序寡具。 在《Java 虛擬機(jī)規(guī)范》中沒有任何 OutOfMemoryError 情況的區(qū)域唇聘。

虛擬機(jī)棧(JVM Stack)

虛擬機(jī)棧介却,存放的是線程運(yùn)行時(shí)內(nèi)部的局部變量,也可以理解為線程棧能扒。

每個(gè)方法被執(zhí)行的時(shí)候佣渴, 虛擬機(jī)會(huì)創(chuàng)建一個(gè)棧幀(Stack Frame)用于存放局部變量表(local variable),操作數(shù)棧(operand stack)初斑,動(dòng)態(tài)連接辛润,方法出口等信息。

棧幀(Stack Frame)隨著方法的調(diào)用而創(chuàng)建见秤,隨著方法的結(jié)束而銷毀(不論是正常結(jié)束還拋出異常)频蛔。

字節(jié)碼指令分析(描述 JVM Stack 操作過程)

publicintadd(){inta =1;intb =2;intc = b - a;returnc;}0iconst_1//將 a 壓入局部變量表?xiàng)m?istore_1//對(duì) a 進(jìn)行賦值 1? 2iconst_2//將 b 壓入局部變量表?xiàng)m?istore_2//對(duì) b 進(jìn)行賦值 24iload_2//讀取 b 到操作數(shù)棧5iload_1//讀取 a 到操作數(shù)棧6isub//執(zhí)行 b - a7istore_3//將 int 類型的值存入局部變量表 3 8iload_3//讀取 c 到操作數(shù)棧9ireturn//返回復(fù)制代碼

局部變量表(local variable)

局部變量表存放了各種編譯期 Java 虛擬機(jī)基本數(shù)據(jù)庫類型(boolean灵迫、byte、char晦溪、short、int挣跋、float三圆、long、dubble)和對(duì)象引用(reference 類型避咆,即對(duì)象的起始位置指針或者對(duì)象句柄), 對(duì)象的真實(shí)數(shù)據(jù)通常存放在堆空間舟肉。

局部變量表中的存儲(chǔ)空間通過變量槽(slot) 來表示,其中 64 位長度的 long? 和 double 占 2 個(gè)變量槽查库。

操作數(shù)棧(operand stack)

每個(gè)棧幀都包含一個(gè)操作數(shù)棧的先進(jìn)先出(FIFO)棧路媚,棧幀中操作數(shù)棧的深度由編譯期決定,并且通過方法的 code 屬性保存以及提供給棧幀使用樊销。

動(dòng)態(tài)鏈接

每個(gè)棧幀都包含一個(gè)指向當(dāng)前方法所在類型的運(yùn)行時(shí)常量池的引用整慎。以便對(duì)當(dāng)前方法的代碼實(shí)現(xiàn)動(dòng)態(tài)鏈接

在 class 文件中围苫,一個(gè)方法如果要調(diào)用其它方法裤园, 或者訪問局部成員變量,則需要將符號(hào)引用(synbolic reference)來表示剂府,動(dòng)態(tài)鏈接的作用就是將這些符號(hào)引用轉(zhuǎn)換為對(duì)實(shí)際方法的直接引用拧揽。

方法出口

方法正常完成,當(dāng)前棧幀恢復(fù)調(diào)用者的責(zé)任腺占,包括恢復(fù)調(diào)用者的局部變量表淤袜,操作數(shù)棧,以及正確的程序計(jì)數(shù)器遞增衰伯。跳過剛才執(zhí)行的方法調(diào)用指令等铡羡,低哦啊用著的代碼被調(diào)用的方法正返回值壓入調(diào)用者操作數(shù)棧后,會(huì)繼續(xù)正常執(zhí)行嚎研。

方法一場完成蓖墅,某些指令導(dǎo)致了 JVM 虛擬機(jī)拋出異常,或者用戶顯示的通過thorw關(guān)鍵字跑出一場临扮,同時(shí)在改該方法中沒有捕獲異常论矾。如果方法異常調(diào)用完成,那不一定有方法返回值返回給調(diào)用者杆勇。

本地方法棧(Native Method Stack)

為本地方法所分配的內(nèi)存空間贪壳,就是為native關(guān)鍵字修飾的方法提供服務(wù)的。

本地方法主要是 Java 來調(diào)用 C/C++ 函數(shù)庫的調(diào)用方法蚜退。

方法區(qū)(Method Area)

主要存放數(shù)據(jù)有:常量闰靴,靜態(tài)變量彪笼,類信息。

方法區(qū)存放的是靜態(tài)變量的內(nèi)存地址蚂且, 方法區(qū)里面有一個(gè)元空間配猫, 在JDK1.8 之前叫永久代。

堆(Heap)

JVM 管理的最大的一塊內(nèi)存空間杏死。與堆相關(guān)的一個(gè)重要概念是垃圾收集器泵肄。幾乎所有的垃圾收集器都是采用分代收集算法,所以對(duì)內(nèi)存空間也是基于這一點(diǎn)進(jìn)行相應(yīng)的劃分:新生代和老年代淑翼,新生代分為Eden 空間腐巢、From Survivor 空間、To Survivor 空間玄括。

對(duì)象創(chuàng)建的過程中首先會(huì)存在 Eden 區(qū)冯丙,然后經(jīng)過 minor gc 過后進(jìn)入 survivor ,進(jìn)過 15 次 survivor 轉(zhuǎn)移過后遭京,進(jìn)入老年代胃惜。

如果內(nèi)存都不夠用了就觸發(fā) full gc, 再次觸發(fā) GC 過后無法分配申請(qǐng)內(nèi)存洁墙,JVM 就會(huì)拋出 OOM蛹疯。

分析對(duì)象是否存在引用,是否被回收采用的是 GC ROOT 可達(dá)性分析热监。

直接內(nèi)存(Direact Memory)

直接內(nèi)存捺弦,不是 JVM 來管理,是通過操作系統(tǒng)來管理的孝扛, 與 Java NIO 密切相關(guān)列吼。 Java 通過DirectByteBuffer來操作直接內(nèi)存。

JVM 內(nèi)存參數(shù)設(shè)置

內(nèi)存參數(shù)配置

Spring-Boot 程序的 JVM 內(nèi)存參數(shù)設(shè)置格式(Tomcat 啟動(dòng)直接在 bin 目錄下的 Catalina.sh 文件設(shè)置)

java -Xms2048m -Xmx2048m? -Xmn1024 -Xss512k -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -jar? xxx-xxx.jar復(fù)制代碼

關(guān)于元空間JVM 有兩個(gè):-XX:MetaspaceSize=N 和 -XX:MaxMetaspaceSize=N苦始,對(duì)于 64 位 JVM 來說寞钥, 元空間默認(rèn)是 21MB,默認(rèn)的元空間的最大值是無限陌选。

-XX:MaxMetaspaceSize: 設(shè)置元空間最大值理郑,默認(rèn)是 -1, 即不限制咨油,或者說是受限制于本地內(nèi)存大小您炉。

-XX:MetaspaceSize:指定元空間的初始大小,以字節(jié)為單位役电,默認(rèn)是 21M赚爵,達(dá)到該值過后就會(huì)觸發(fā) full gc 進(jìn)行類型卸載,同時(shí)收集器會(huì)對(duì)該值進(jìn)行調(diào)整;如果釋放了大量的空間就適當(dāng)降低該值冀膝;如果釋放了很少的空間唁奢,那么就在不超過 -XX:MaxMetaspaceSize (如果設(shè)置)的情況下,適當(dāng)提高該值窝剖。

由于調(diào)整元空間大小需要 full gc , 這是一個(gè)非常昂貴的操作麻掸,如果在啟動(dòng)過程中發(fā)生大量 full gc, 通常都是由于永久代或者元空間發(fā)生了大小調(diào)整,基于這種情況枯芬,一般建議在 JVM 參數(shù)將 MaxMetaspaceSize 和 MetaspaceSize 設(shè)置成一樣的值论笔,并設(shè)置得比初始值要大,對(duì)于 8G 的物理內(nèi)存來說我們通常都會(huì)將這兩個(gè)值設(shè)置為 256M千所。

堆空間內(nèi)存溢出

importjava.util.ArrayList;importjava.util.List;publicclassHeapOverFlowTest{byte[] a =newbyte[1024*1024*2];// 2mbpublicstaticvoidmain(String[] args){? ? ? ? List list =newArrayList<>();while(true) {? ? ? ? ? ? list.add(newHeapOverFlowTest());? ? ? ? }? ? }}// 輸出結(jié)果Exception in thread"main"java.lang.OutOfMemoryError: Java heap spaceat cn.edu.cqvie.jvm.HeapOverFlowTest.(HeapOverFlowTest.java:8)at cn.edu.cqvie.jvm.HeapOverFlowTest.main(HeapOverFlowTest.java:13)復(fù)制代碼

虛擬機(jī)棧內(nèi)存溢出

publicclassStackOverFlowTest{// JVM 設(shè)置// -Xss128k, -Xss默認(rèn)1Mstaticintcount =0;staticvoidredo(){? ? ? ? count++;? ? ? ? redo();? ? }publicstaticvoidmain(String[] args){try{? ? ? ? ? ? redo();? ? ? ? ? ? System.out.println(count);? ? ? ? }catch(Throwable t) {? ? ? ? ? ? t.printStackTrace();? ? ? ? }? ? }}// 輸出結(jié)果: 棧溢出java.lang.StackOverflowErrorat cn.edu.cqvie.jvm.StackOverFlowTest.redo(StackOverFlowTest.java:11)at cn.edu.cqvie.jvm.StackOverFlowTest.redo(StackOverFlowTest.java:11)at cn.edu.cqvie.jvm.StackOverFlowTest.redo(StackOverFlowTest.java:11)....復(fù)制代碼

總結(jié):

-Xss 設(shè)置越小 count 值越小,說明一個(gè)線程棧里能夠分配的棧幀就越小蒜埋,但是對(duì)于 JVM 整體來說能夠開啟的線程數(shù)就會(huì)更多淫痰。

方法區(qū)內(nèi)存溢出

需要注意的是 1.8 內(nèi)模型中,將運(yùn)行時(shí)常量池?cái)?shù)據(jù)放入堆中整份,所以我們限制方法區(qū)的大小對(duì)運(yùn)行時(shí)常量池的限制毫無意義待错。最終也只會(huì)拋出java.lang.OutOfMemoryError: Java heap space異常。

下面通過GCLib 模擬方法區(qū)溢出模擬的一個(gè)例子烈评。

/**

* -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m

*/publicclassMyTest4{publicstaticvoidmain(String[] args){for(; ; ) {? ? ? ? ? ? Enhancer enhancer =newEnhancer();? ? ? ? ? ? enhancer.setSuperclass(MyTest4.class);? ? ? ? ? ? enhancer.setUseCache(false);? ? ? ? ? ? enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) ->? ? ? ? ? ? ? ? ? ? proxy.invoke(obj, args1));? ? ? ? ? ? System.out.println("hello world");? ? ? ? ? ? enhancer.create();? ? ? ? }? ? }}//輸出結(jié)果Caused by: java.lang.OutOfMemoryError: Metaspaceat java.lang.ClassLoader.defineClass1(Native Method)at java.lang.ClassLoader.defineClass(ClassLoader.java:756)? ......復(fù)制代碼

JVM 監(jiān)控工具

VisualVM

VisualVM 提供在 Java 虛擬機(jī) (Java Virutal Machine, JVM) 上運(yùn)行的 Java 應(yīng)用程序的詳細(xì)信息火俄。在 VisualVM 的圖形用戶界面中,可以方便讲冠、快捷地查看多個(gè) Java 應(yīng)用程序的相關(guān)信息瓜客。

作者:大漠北

鏈接:https://juejin.cn/post/6925664904833662983

來源:掘金

著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)竿开,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處谱仪。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市否彩,隨后出現(xiàn)的幾起案子疯攒,更是在濱河造成了極大的恐慌,老刑警劉巖列荔,帶你破解...
    沈念sama閱讀 221,430評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敬尺,死亡現(xiàn)場離奇詭異,居然都是意外死亡贴浙,警方通過查閱死者的電腦和手機(jī)砂吞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悬而,“玉大人呜舒,你說我怎么就攤上這事。” “怎么了袭蝗?”我有些...
    開封第一講書人閱讀 167,834評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵唤殴,是天一觀的道長。 經(jīng)常有香客問我到腥,道長朵逝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,543評(píng)論 1 296
  • 正文 為了忘掉前任乡范,我火速辦了婚禮配名,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘晋辆。我一直安慰自己渠脉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評(píng)論 6 397
  • 文/花漫 我一把揭開白布瓶佳。 她就那樣靜靜地躺著芋膘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪霸饲。 梳的紋絲不亂的頭發(fā)上为朋,一...
    開封第一講書人閱讀 52,196評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音厚脉,去河邊找鬼习寸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛傻工,可吹牛的內(nèi)容都是我干的霞溪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼精钮,長吁一口氣:“原來是場噩夢啊……” “哼威鹿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起轨香,我...
    開封第一講書人閱讀 39,671評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤忽你,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后臂容,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體科雳,經(jīng)...
    沈念sama閱讀 46,221評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評(píng)論 3 340
  • 正文 我和宋清朗相戀三年脓杉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了糟秘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,444評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡球散,死狀恐怖尿赚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤凌净,帶...
    沈念sama閱讀 36,134評(píng)論 5 350
  • 正文 年R本政府宣布悲龟,位于F島的核電站,受9級(jí)特大地震影響冰寻,放射性物質(zhì)發(fā)生泄漏须教。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評(píng)論 3 333
  • 文/蒙蒙 一斩芭、第九天 我趴在偏房一處隱蔽的房頂上張望轻腺。 院中可真熱鬧,春花似錦划乖、人聲如沸贬养。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽煤蚌。三九已至,卻和暖如春细卧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背筒占。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評(píng)論 1 272
  • 我被黑心中介騙來泰國打工贪庙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人翰苫。 一個(gè)月前我還...
    沈念sama閱讀 48,837評(píng)論 3 376
  • 正文 我出身青樓止邮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親奏窑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子导披,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容