Java面試題 - JVM相關

01 JVM結構

在這裏插入圖片描述
PC寄存器:

  1. 每個線程擁有⼀個pc寄存器;
  2. 指向下⼀條指令的地址。

⽅法區:

  1. 保存裝載的類的元信息:類型的常量池,字段、⽅法信息,⽅法字節碼;jdk6時,String等常量信息置於⽅法區,jdk7移到了堆中;
  2. 通常和永久區(Perm)關聯在⼀起;

堆:

  1. 應⽤系統對象都保存在java堆中;
  2. 所有線程共享java堆;
  3. 對分代GC來說,堆也是分代的;

棧:

  1. 線程私有;
  2. 棧由⼀系列幀組成(因此java棧也叫做幀棧);
  3. 幀保存⼀個⽅法的局部變量(局部變量表)、操作數棧、常量池指針;

在這裏插入圖片描述

在這裏插入圖片描述
每⼀次⽅法調⽤創建⼀個幀,並壓棧。

02 JVM內存模型

  1. 每⼀個線程有⼀個⼯作內存,和主存獨⽴;
  2. ⼯作內存存放主存中變量的值的拷⻉;
  3. 對於普通變量,⼀個線程中更新的值,不能⻢上反應在其他變量中;如果需要在其他線程中⽴即可⻅,需要使⽤volatile關鍵字;
  4. volatile不能代替鎖,⼀般認爲volatile⽐鎖性能好(不絕對),使⽤volatile的條件是語義是否滿⾜應⽤;
  5. 可⻅性:⼀個線程修改了變量,其他線程可以⽴即知道。
  • ------------ volatile;
  • ------------ synchronized(unlock之前,寫變量值回主存);
  • ------------ final(⼀旦初始化完成,其他線程可⻅)。

03 java四引⽤

  • 強引⽤:強引⽤是使⽤最普遍的引⽤。如果⼀個對象具有強引⽤,那垃圾回收器絕不會回收它。當內存空間不⾜,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終⽌,也不會靠隨意回收具有強引⽤的對象來解決內存不⾜的問題。

  • 軟引⽤:如果內存空間不⾜了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,軟引⽤可以和⼀個引⽤隊列(ReferenceQueue)聯合使⽤,如果軟引⽤所引⽤的對象被垃圾回收器回收,Java虛擬機就會把這個軟引⽤加⼊到與之關聯的引⽤隊列中。

  • 弱引⽤:弱引⽤與軟引⽤的區別在於:只具有弱引⽤的對象擁有更短暫的⽣命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程中,⼀旦發現了只具有弱引⽤的對象,不管當前內存空間⾜夠與否,都會回收它的內存。弱引⽤可以和⼀個引⽤隊列(ReferenceQueue)聯合使⽤,如果弱引⽤所引⽤的對象被垃圾回收,Java虛擬機就會把這個弱引⽤加⼊到與之關聯的引⽤隊列中。

  • 虛引⽤:虛引⽤在任何時候都可能被垃圾回收器回收,主要⽤來跟蹤對象被垃圾回收器回收的活動,被回收時會收到⼀個系統通知。虛引⽤與軟引⽤和弱引⽤的⼀個區別在於:虛引⽤必須和引⽤隊列 (ReferenceQueue)聯合使⽤。當垃圾回收器準備回收⼀個對象時,如果發現它還有虛引⽤,就會在回收對象的內存之前,把這個虛引⽤加⼊到與之關聯的引⽤隊列中。

04 GC算法分類

引⽤計數法(沒有被java採⽤):

  • 原理:對於⼀個對象A,只要有任何⼀個對象引⽤了A,則A的引⽤計數器就加1,當引⽤失效時,引⽤計數器就減1,只要對象A的引⽤計數器的值爲0,則對象A就會被回收。
  • 問題:引⽤和去引⽤伴隨加法和減法,影響性能;很難處理循環引⽤。

標記清除法:

  • 原理:現代垃圾回收算法的思想基礎。標記-清除算法將垃圾回收分爲兩個階段:標記階段和清除階段。⼀種可⾏的實現是,在標記節點,⾸先通過根節點,標記所有從根節點開始的可達對象。因此,未被標記的對象就是未被引⽤的垃圾對象。然後在清除階段,清除所有未被標記的對象。
  • 問題:標記和清除兩個過程效率不⾼,產⽣內存碎⽚導致需要分配較⼤對象時⽆法找到⾜夠的連續內存⽽需要觸發⼀次GC操作。

標記壓縮法:

  • 原理:適合⽤於存活對象較多的場合,如⽼年代。它在標記-清除算法的基礎上做了⼀些優化。標記階段⼀樣,但之後,將所有存活對象壓縮到內存的⼀端。之後,清除邊界外所有的空間。
  • 優點: 解決了標記- 清除算法導致的內存碎⽚問題和在存活率較⾼時複製算法效率低的問題。

複製算法:

  • 原理:將原有的內存空間分爲兩塊,每次只使⽤其中⼀塊,在垃圾回收時,將正在使⽤的內存中的存活對象複製到未使⽤的內存塊中,之後清除正在使⽤的內存塊中的所有對象,交換兩個內存的⻆⾊,完成垃圾回收。
  • 問題: 不適⽤於存活對象⽐較多的場合,如⽼年代。

分代回收法:

  • 原理:根據對象存活週期的不同將內存劃分爲⼏塊,⼀般是新⽣代和⽼年代,新⽣代基本採⽤複製算法,⽼年代採⽤標記整理算法。

05 MinorGC & FullGC

  1. Minor GC通常發⽣在新⽣代的Eden區,在這個區的對象⽣存期短,往往發⽣GC的頻率較⾼,回收速度⽐較快,⼀般採⽤複製-回收算法。
  2. Full GC/Major GC 發⽣在⽼年代,⼀般情況下,觸發⽼年代GC的時候不會觸發Minor GC,所採⽤的是標記-清除算法。

06 垃圾收集器

  1. Serial New收集器是針對新⽣代的收集器,採⽤的是複製算法;
  2. Parallel New(並⾏)收集器,新⽣代採⽤複製算法,⽼年代採⽤標記整理;
  3. Parallel Scavenge(並⾏)收集器,針對新⽣代,採⽤複製收集算法;
  4. Serial Old(串⾏)收集器,新⽣代採⽤複製,⽼年代採⽤標記清理;
  5. Parallel Old(並⾏)收集器,針對⽼年代,標記整理;
  6. CMS收集器,基於標記清理;
  7. G1收集器(JDK):整體上是基於標記清理,局部採⽤複製;

綜上:新⽣代基本採⽤複製算法,⽼年代採⽤標記整理算法,cms採⽤標記清理;

07 Java類加載機制

概念:

  • 虛擬機把描述類的數據⽂件(字節碼)加載到內存,並對數據進⾏驗證、準備、解析以及類初始化,最終形成可以被虛擬機直接使⽤的java類型(java.lang.Class對象)。

類⽣命週期:

  • 類加載過程:讀取⼆進制字節流到jvm—>驗證格式語義等—>爲靜態變量分配內存空間—>常量池引⽤解析—>執⾏static標識的代碼。
  1. 加載過程:通過⼀個類的全限定名來獲取定義此類的⼆進制字節流,將這個字節流所代表的靜態存儲結構轉化爲⽅法區的運⾏時數據結構。在內存中(⽅法區)⽣成⼀個代表這個類的java.lang.Class對象,作爲⽅法區這個類的各種數據的訪問⼊⼝;
  2. 驗證過程:爲了確保Class⽂件的字節流中包含的信息符合當前虛擬機的要求,⽂件格式驗證、元數據驗證、字節碼驗證、符號引⽤驗證;
  3. 準備過程:正式爲類屬性分配內存並設置類屬性初始值的階段,這些內存都將在⽅法區中進⾏分配;

準備階段,static對象會被設置默認值,static final對象會被賦上給予的值。

  1. 解析階段:虛擬機將常量池內的符號引⽤替換爲直接引⽤的過程。

i. 符號引⽤:字符串,引⽤對象不⼀定被加載;
ii. 直接引⽤:指針或者地址偏移量,引⽤對象⼀定在內存中。

  1. 初始化階段:類初始化階段是類加載過程的最後⼀步。初始化階段就是執⾏類構造<clint>()⽅法的過程。
  2. 使⽤階段:
  3. 卸載階段:

08 類加載器

java默認提供三個類加載器:
在這裏插入圖片描述

  1. BootStrap ClassLoader 啓動ClassLoader(sun.boot.class.path):最頂層的加載類,主要加載jdk中的核⼼庫,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。BootstrapClassLoader不繼承⾃ClassLoader,因爲它不是⼀個普通的Java類,底層由C++編寫,已嵌⼊到了JVM內核當中,當JVM啓動後,Bootstrap ClassLoader也隨着啓動,負責加載完核⼼類庫後,並構造Extension ClassLoader和App ClassLoader類加載器。
  2. Extension ClassLoader 擴展ClassLoader(java.ext.dirs):擴展的類加載器,加載⽬錄%JRE_HOME%\lib\ext⽬錄下的jar包和class⽂件。還可以加載-D java.ext.dirs選項指定的⽬錄。
  3. App ClassLoader 應⽤ClassLoader/系統ClassLoader(java.class.path):也稱爲SystemAppClass加載當前應⽤的classpath的所有類。 除了BootStrap ClassLoader,每個ClassLoader都有⼀個Parent作爲⽗親。

雙親委派機制:

  1. 定義:當⼀個ClassLoader實例需要加載某個類時,它會試圖親⾃搜索某個類之前,先把這個任務委託給它的⽗類加載器,這個過程是由上⾄下依次檢查的,⾸先由最頂層的類加載器Bootstrap ClassLoader試圖加載,如果沒加載到,則把任務轉交給Extension ClassLoader試圖加載,如果也沒加載到,則轉交給App ClassLoader 進⾏加載,如果它也沒有加載得到的話,則返回給委託的發起者,由它到指定的⽂件系統或⽹絡等URL中加載該類。如果它們都沒有加載到這個類時,則拋出ClassNotFoundException異常。否則將這個找到的類⽣成⼀個類的定義,並將它加載到內存當中,最後返回這個類在內存中的Class實例對象。在這裏插入圖片描述
  2. 作⽤:避免重複加載;考慮到安全因素,避免⾃定義的類去替代系統類,如String。
  3. jvm如何判定兩個class是否相同?JVM在判定兩個class是否相同時,不僅要判斷兩個類名是否相同,⽽且要判斷是否由同⼀個類加載器實例加載的。只有兩者同時滿⾜的情況下,JVM才認爲這兩個class是相同的。
  1. ⾃底向上檢查類是否已經加載;
  2. ⾃頂向下嘗試加載類。

custom classloader:⾃定義classloader
a. Java中提供的默認ClassLoader,只加載指定⽬錄下的jar和class,如果我們想加載其它位置的類或jar時,就需要定義⾃⼰的ClassLoader。
b. 步驟:

  1. 繼承java.lang.ClassLoader
  2. 重寫⽗類的findClass⽅法
    在這裏插入圖片描述

09 引起類加載的五個⾏爲

  1. 遇到new、getstatic、putstatic或invokestatic這四條字節碼指令
  2. 反射調⽤的時候,如果類沒有進⾏過初始化,則需要先觸發其初始化
  3. ⼦類初始化的時候,如果其⽗類還沒初始化,則需先觸發其⽗類的初始化
  4. 虛擬機執⾏主類的時候(有 main(string[] args))
  5. JDK1.7 動態語⾔⽀持

10 java對象創建時機

  1. 使⽤new關鍵字創建對象
  2. 使⽤Class類的newInstance⽅法(反射機制)
  3. 使⽤Constructor類的newInstance⽅法(反射機制)
  4. 使⽤Clone⽅法創建對象
  5. 使⽤(反)序列化機制創建對象

11 JVM調優

調優時機:

  • heap 內存(⽼年代)持續上漲達到設置的最⼤內存值;
  • Full GC 次數頻繁;
  • GC 停頓時間過⻓(超過1秒);
  • 應⽤出現OutOfMemory 等內存異常;
  • 應⽤中有使⽤本地緩存且佔⽤⼤量內存空間;
  • 系統吞吐量與響應性能不⾼或下降。

調優原則:

  • 多數的Java應⽤不需要在服務器上進⾏JVM優化;
  • 多數導致GC問題的Java應⽤,都不是因爲我們參數設置錯誤,⽽是代碼問題;
  • 在應⽤上線之前,先考慮將機器的JVM參數設置到最優(最適合);
  • 減少創建對象的數量;
  • 減少使⽤全局變量和⼤對象;
  • JVM優化是到最後不得已才採⽤的⼿段;
  • 在實際使⽤中,分析GC情況優化代碼⽐優化JVM參數更好;

調優⽬標:

  • GC低停頓;
  • GC低頻率;
  • 低內存佔⽤;
  • ⾼吞吐量;

調優步驟:

  • 分析GC⽇志及dump⽂件,判斷是否需要優化,確定瓶頸問題點;
  • 確定jvm調優量化⽬標;
  • 確定jvm調優參數(根據歷史jvm參數來調整);
  • 調優⼀臺服務器,對⽐觀察調優前後的差異;
  • 不斷的分析和調整,知道找到合適的jvm參數配置;
  • 找到最合適的參數,將這些參數應⽤到所有服務器,並進⾏後續跟蹤。

12 jvm調優參數

  1. 設定堆內存⼤⼩,這是最基本的。
  2. -Xms:啓動JVM時的堆內存空間。
  3. -Xmx:堆內存最⼤限制。
  4. 設定新⽣代⼤⼩。
  5. 新⽣代不宜太⼩,否則會有⼤量對象湧⼊⽼年代。
  6. -XX:NewRatio:新⽣代和⽼年代的佔⽐。
  7. -XX:NewSize:新⽣代空間。
  8. -XX:SurvivorRatio:伊甸園空間和倖存者空間的佔⽐。
  9. -XX:MaxTenuringThreshold:對象進⼊⽼年代的年齡閾值。
  10. 設定垃圾回收器
  • ----年輕代:-XX:+UseParNewGC。
  • ----⽼年代:-XX:+UseConcMarkSweepGC。
  • ----CMS可以將STW時間降到最低,但是不對內存進⾏壓縮,有可能出現“並⾏模式失敗”。⽐如⽼年代空間還有300MB空間,但是⼀些10MB的對象⽆法被順序的存儲。這時候會觸發壓縮處理,但是CMS GC模式下的壓縮處理時間要⽐Parallel GC⻓很多。
  • ----G1採⽤”標記-整理“算法,解決了內存碎⽚問題,建⽴了可預測的停頓時間類型,能讓使⽤者指定在⼀個⻓度爲M毫秒的時間段內,消耗在垃圾收集上的時間不得超過N毫秒。

13 觸發full gc的場景及應對策略

  1. System.gc()⽅法的調⽤,應對策略:通過-XX:+DisableExplicitGC來禁⽌調⽤System.gc ;
  2. ⽼年代代空間不⾜,應對策略:讓對象在Minor GC階段被回收,讓對象在新⽣代多存活⼀段時間,不要創建過⼤的對象及數組;
  3. 永⽣區空間不⾜,應對策略:增⼤PermGen空間;
  4. GC時出現promotionfailed和concurrent mode failure,應對策略:增⼤survivor space;
  5. Minor GC後晉升到舊⽣代的對象⼤⼩⼤於⽼年代的剩餘空間,應對策略:增⼤Tenured space 或下CMSInitiatingOccupancyFraction=60;
  6. 內存持續增漲達到上限導致Full GC,應對策略:通過dumpheap 分析是否存在內存泄漏。

14 jvm堆的基本結構

在這裏插入圖片描述

  1. JVM中堆空間可以分成三個⼤區,新⽣代、⽼年代、永久代
  2. 新⽣代可以劃分爲三個區,Eden區,兩個Survivor區,在HotSpot虛擬機Eden和Survivor的⼤⼩⽐例爲8:1

15 如何查看jvm內存使⽤情況

可以使⽤JDK⾃帶的JConsole、JVisualVM、JMap、JHat等⼯具,或者使⽤第三⽅⼯具,⽐如 Eclipse Memory Analyzer。

16 jvm內存溢出例⼦

  1. 內存溢出,⽐如給JVM分配的內存不夠⼤,或者程序中存在死循環⼀直申請內存。
  2. 內存泄露,⽐如下⾯這段代碼,list持有o的引⽤,o暫時是⽆法被JVM垃圾回收的,只有當list被垃圾回收或者o從對象list刪除掉後,o才能被JVM垃圾回收。

在這裏插入圖片描述

17 常⽤的GC策略,什麼時候會觸發YGC,什麼時候觸發FGC?

YGC(Young GC):

  • 概念:對新⽣代堆進⾏GC。頻率⽐較⾼,因爲⼤部分對象的存活壽命較短,在新⽣代⾥被回收。性能耗費較⼩。
  • 觸發時機:Eden區空間不⾜。

FGC(Full GC):

  • 概念:全堆範圍的GC。默認堆空間使⽤到達80%(可調整)的時候會觸發FGC。以我們⽣產環境爲例,⼀般⽐較少會觸發FGC,有時10天或⼀周左右會有⼀次。
  • 觸發時機:
  • ------ Old空間不⾜;
  • ------ Perm空間不⾜;
  • ------ 顯示調⽤System.gc() ,包括RMI等的定時觸發;
  • ------ YGC時的悲觀策略;
  • ------ dump live的內存信息時(jmap –dump:live)。

18 獲得Class對象的方式

  1. 靜態類的.class語法:GuideUtil.class
  2. 普通類對象的getClass()⽅法:new Test().getClass()
  3. 通過Class對象的forName()⽅法:
    Class.forName(“com.zhenai.modules.guide.utils.GuideUtil");
  4. 對於包裝類,可以通過.TYPE語法⽅式:Integer.TYPE

19 內存溢出的可能原因和解決⽅法

  1. 數據加載過多,如1次從數據庫中取出過多數據。
  2. 集合類中有對對象的引⽤,⽤完後沒有清空或者集合對象未置空導致引⽤存在等,是的JVM⽆法回收。
  3. 死循環,過多重複對象。
  4. 第三⽅軟件的bug。
  5. 啓動參數內存值設定的過⼩。

解決⽅法:修改JVM啓動參數,加內存(-Xms,-Xmx);錯誤⽇志,是否還有其他錯誤;代碼⾛查。

20 內存泄漏的原因

  1. 未對作廢數據內存單元置爲null,儘早釋放⽆⽤對象的引⽤,使⽤臨時變量時,讓引⽤變量在推出活動域後⾃動設置爲null。

垃圾收集器收集;

  1. 程序避免⽤String拼接,⽤StringBuffer,因爲每個String會佔⽤內存⼀塊區域;
  2. 儘量少⽤靜態變量(全局不會回收);
  3. 不要集中創建對象尤其⼤對象,可以使⽤流操作;
  4. 儘量使⽤對象池,不再循環中創建對象,優化配置;
  5. 創建對象到單例getInstance中,對象⽆法回收被單例引⽤;
  6. 服務器session時間設置過⻓也會引起內存泄漏。

21 ⽅法區oom

  1. ⽅法區⽤於存放Class的相關信息,如:類名,訪問修飾符,常量池,字符描述,⽅法描述等。
  2. 原因:運⾏時產⽣⼤量的類去填滿⽅法區,直到溢出。

22 哪些情況下對象會進⼊⽼年代?

  1. 新⽣代對象每次經歷⼀次minor gc,年齡會加1,當達到年齡閾值(默認爲15歲)會直接進⼊⽼年代;
  2. ⼤對象直接進⼊⽼年代;
  3. 新⽣代複製算法需要⼀個survivor區進⾏輪換備份,如果出現⼤量對象在minor gc後仍然存活的情況時,就需要⽼年代進⾏分配擔保,讓survivor⽆法容納的對象直接進⼊⽼年代;
  4. 如果在Survivor空間中相同年齡所有對象⼤⼩的總和⼤於Survivor空間的⼀半,年齡⼤於或等於該年齡的對象就可以直接進⼊年⽼代。

23 jvm中哪些地⽅會出現oom?分別說說oom的可能原因?

java堆溢出(heap):

  • Java堆內存主要⽤來存放運⾏過程中所以的對象,該區域OOM異常⼀般會有如下錯誤信息: java.lang.OutofMemoryError:Java heap space
  • 此類錯誤⼀般通過Eclipse Memory Analyzer分析OOM時dump的內存快照就能分析出來,到底是由於程序原因導致的內存泄露,還是由於沒有估計好JVM內存的⼤⼩⽽導致的內存溢出。
  • 另外,Java堆常⽤的JVM參數:

-Xms:初始堆⼤⼩,默認值爲物理內存的1/64(<1GB),默認(MinHeapFreeRatio參數可以調整)空餘堆內存⼩於40%時,JVM就會增⼤堆直到
-Xmx:最⼤堆⼤⼩,默認值爲物理內存的1/4(<1GB),默認(MaxHeapFreeRatio參數可以調整)空餘堆內存⼤於70%時,JVM會減少堆直到
-Xmn:年輕代⼤⼩(1.4or lator),此處的⼤⼩是(eden + 2 survivor space),與jmap -heap中顯示的New gen是不同的。

棧溢出(stack):

  • 棧⽤來存儲線程的局部變量表、操作數棧、動態鏈接、⽅法出⼝等信息。如果請求棧的深度不⾜時拋出的錯誤會包含類似下⾯的信息:java.lang.StackOverflowError。
  • 另外,由於每個線程佔的內存⼤概爲1M,因此線程的創建也需要內存空間。操作系統可⽤內存-Xmx-MaxPermSize即是棧可⽤的內存,如果申請創建的線程⽐較多超過剩餘內存的時候,也會拋出如下類似錯誤:java.lang.OutofMemoryError: unable to create new native thread
  • 相關的JVM參數有:

-Xss: 每個線程的堆棧⼤⼩,JDK5.0以後每個線程堆棧⼤⼩爲1M,以前每個線程堆棧⼤⼩爲256K.
在相同物理內存下,減⼩這個值能⽣成更多的線程.但是操作系統對⼀個進程內的線程數還是有限制的,不能⽆限⽣成,經驗值在3000~5000

  • 可能原因:

遞歸:遞歸⾥⽤到的局部變量存儲在堆棧中,堆棧的訪問效率⾼,速度快,但空間有限,遞歸太多變量需要⼀直⼊棧⽽不出棧,導致需要的內存空間⼤於堆棧的空間,棧空間是2M,堆空間內存空間。

運⾏時常量溢出(constant):

  • 運⾏時常量保存在⽅法區,存放的主要是編譯器⽣成的各種字⾯量和符號引⽤,但是運⾏期間也可能將新的常量放⼊池中,⽐如String類的intern⽅法。如果該區域OOM,錯誤結果會包含類似下⾯的信息:java.lang.OutofMemoryError: PermGen space
  • 相關的JVM參數有:

-XX:PermSize:設置持久代(perm gen)初始值,默認值爲物理內存的1/64
-XX:MaxPermSize:設置持久代最⼤值,默認爲物理內存的1/4

⽅法區溢出:

  • ⽅法區主要存儲被虛擬機加載的類信息,如類名、訪問修飾符、常量池、字段描述、⽅法描述等。理論上在JVM啓動後該區域⼤⼩應該⽐較穩定,但是⽬前很多框架,⽐如Spring和Hibernate等在運⾏過程中都會動態⽣成類,因此也存在OOM的⻛險。如果該區域OOM,錯誤結果會包含類似下⾯的信息:java.lang.OutofMemoryError: PermGen space
  • 相關的JVM參數有:

-XX:PermSize:設置持久代(perm gen)初始值,默認值爲物理內存的1/64
-XX:MaxPermSize:設置持久代最⼤值,默認爲物理內存的1/4

24 如何定位jvm內存信息?

方法一:打印⽇志

-XX:+PrintGC:輸出形式: 
[GC 118250K->113543K(130112K), 0.0094143 secs] 
[Full GC 121376K->10414K(130112K), 0.0650971 secs]
-XX:+PrintGCDetails:輸出形式: 
[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs] 
[GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs
-XX:+PrintGCTimeStamps:打印GC停頓耗時
-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期間程序暫停的時間. 
-XX:+PrintHeapAtGC:打印GC前後的詳細堆棧信息
-Xloggc:filename:把相關⽇志信息記錄到⽂件以便分析.

方法二:錯誤調試

-XX:ErrorFile=./hs_err_pid<pid>.log:如果JVM crashed,將錯誤⽇志輸出到指定⽂件路徑。
-XX:HeapDumpPath=./java_pid<pid>.hprof:堆內存快照的存儲⽂件路徑。
-XX:-HeapDumpOnOutOfMemoryError:在OOM時,輸出⼀個dump.core⽂件,記錄當時的堆內存快照

方法三:類狀態器相關

-XX:-TraceClassLoading:打印class裝載信息到stdout。記Loaded狀態。
-XX:-TraceClassUnloading:打印class的卸載信息到stdout。記Unloaded狀態。

25 當對象A創建之後,對象A在各個區之間的流轉過程

jvm堆結構圖:

  • 在這裏插入圖片描述
  • 新⽣代通常佔JVM堆內存的1/3,因爲新⽣代存儲都是新創建的對象,⽐較⼩的對象,⽽⽼年代存的都是⽐較⼤的,活的久的對象,所以⽼年代佔JVM堆內存較⼤;
  • 新⽣代⾥的Eden區通常佔年輕代的4/5,兩個Survivor分別佔新⽣代的1/10。因爲Survivor中存儲的是GC之後倖存的對象,實際上只有很少⼀部分會倖存,所以Survivor佔的⽐例⽐較⼩。

對象流轉流程(新⽣代複製算法,可以減少內存碎⽚)

  • 對象A被new出來之後,是被存放在Eden(伊甸園)區的。
  • 當發⽣⼀次GC後,Eden區存活下來的對象A會被複制到s1區,s0中存活的對象也會被複制到s1中。

如果對象年齡超過閾值年齡(默認15歲),會被複制到⽼年區。部分對象也需要⽼年代分擔。

  • GC會清空Eden和s0中存儲的所有對象;
  • 交換s0和s1的⻆⾊;
  • 重複上⾯的步驟。

26 jvm堆持久代

⽤於存放靜態類型數據,如 Java Class, Method 等。持久代對垃圾回收沒有顯著影響。但是有些應⽤可能動態⽣成或調⽤⼀些Class,例如 Hibernate CGLib 等,在這種時候往往需要設置⼀個⽐較⼤的持久代空間來存放這些運⾏過程中動態增加的類型。

27 synchronized和ReentrantLock

synchronized⽤的鎖是存在Java對象頭⾥的

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