友快網

導航選單

Java(JVM)面試題(2021最新版)

類載入順序

載入: 使用類載入器將class檔案載入到記憶體中;

驗證: 驗證class檔案是否符合jvm的規範;

準備: 為class類和裡面的static變數準備記憶體空間, 併為一些型別賦予初始值, 比如int型別賦予0;

解析: 將符號引用變為直接引用(待補充);

初始化: 例項化一個物件, 這裡是真正的為static變數以及static塊中的資料賦值, 此時, 如果該類的父類不沒有被載入, 會先載入父類, 初始化的時間, 則是該類被new的時候, 就是這個類被使用的時候;

使用: 就是使用類執行程式碼邏輯;

解除安裝: 解除安裝這個類;

類載入的雙親委派機制

前提: 一個類被不同的類載入器載入, 會則會變為兩個不同類, 因此一個類只能有一個唯一的類載入器載入;

實現上述原則, 使用雙親委派模型:

#啟動類載入器: Bootstrap ClassLoader, 載入jdk lib目錄下在類;#擴充套件類載入器Extention classCloader, lib/ext目錄下的類;#應用程式類載入器Application classLoader, classPath下的類;#自定義類載入器根據自己的需求去載入某些類;執行機制: 每個classLoader拿到類之後, 都不會自己去載入, 而是交給父載入器載入, 一層一層向上傳遞, 如果最後父類都不能載入, 則在向下傳遞, 如果都找不到, 就會丟擲異常;

jvm的分割槽

元資料區

用來存放類相關的資料, 比如我們載入的class檔案;

堆記憶體

用來我們在執行程式的過程中建立的各種例項物件, 這是jvm最佳化最重要的部分;

java虛擬機器棧

每一段程式碼的執行都是由一個執行緒執行的, 包括main函式, 也是由main線

程, 因此每一執行緒都有自己的虛擬機器棧, 來儲存自己的區域性變數, 方法也

有自己的虛擬機器棧, 當類執行完成, 或者方法執行完成, 這些區域性變數就

會從棧中彈出, 注意, 這裡的區域性變數只是一個引用, 而真正的例項是在

堆記憶體中;

程式計數器

由於系統在執行程式的時候並不是一直在執行這個系統的功能, 而是cpu

在不同的程序和執行緒中來回切換, 因此要有一個區域來記錄這個程式碼執行

到什麼位置, 保證下次cpu來執行這段程式碼的時候直到從哪裡開始, 程式

計數器就是這個作用;

其他資料區

例如和NIO相關的資料區, socket網路相關的資料區;

JVM的分代模型

年輕代

在jvm中絕大多數的類存在時間是很短的, 用完之後就直接解除安裝掉, 還有一部分類是要長期使用的, 比如我們的啟動類, 以及啟動類中的靜態變數, 因此存在時間短的會儲存在年輕代;

老年代

這裡就是用來儲存長期存活的物件的;

永久代

永久代就是上面提到的元資料區(jdk1。8以前叫方法區);

物件在各個區域時如何流轉的

我們建立的物件, 會先進入年輕代, 隨著程式的不斷執行, 建立的物件越來越多, 年輕的空間會被逐漸佔滿, 此時就會觸發一次Yong GC, 來回收此時已經不被使用的物件, 當這個物件躲過15次Yong GC還沒被回收的時候, 這個物件就會被放進老年代;

JVM引數解釋

-Xms: java堆記憶體大小;-Xmx: java堆記憶體最大大小;-Xmn: java堆記憶體新生代大小;-XX:MatespaceSize: 永久代大小;-XX:MatespaceSize: 永久代最大大小;-Xss: 每個執行緒棧記憶體大小;XX:MaxTenuringThreshold: 物件經歷多次Yong GC之後進入老年代;-XX:PretenureSizeThreshold: 多大的物件直接進入老年代(預設位元組);-XX:-HandlePromotionFailure: 這個引數用來在每次Yong GC之前檢查老年代是否能存下年輕代現在所有的物件, 如果配置了這個引數, 即使存不下, 也不會觸發老年代gc, 如果沒有配置則會觸發老年代gc(jdk1。6之後廢棄);-XX:SurvivorRatio=8: 設定survivor區域進行動態年齡判斷剩餘空間百分比;-XX:UseParNewGC: 指定新生代使用ParNewGC垃圾回收器;-XX:ParallelGCThreads: 設定ParNew gc時的執行緒數, 一般不建議設定;-XX:CMSInitialtingOccupancyFaction: 定義老年代佔用記憶體到了多少後就會自動觸發垃圾回收, jdk預設是92%;-XX:UseConcMarkSweepGC: 老年代使用cms垃圾回收器;-XX:CMSFullGCsBeforeCompaction=0: 設定每次full gc之後進行記憶體碎片整理;-XX:+PrintGCDetils: 列印詳細的gc日誌;-XX:+PrintGCTimeStamps: 打印出來每次GC發生的時間;-Xloggc:gc。log: 將gc寫入一個磁碟檔案;-XX:CMSParallelInitialMarkEnabled: cms初識標記階段開啟多執行緒;-XX:CMSScavengeBeforeRemark: cms開始重新標記之前, 先執行一次yong gc;-XX:TraceClassLoading -XX:TraceClassUnloading: 可以用來跟蹤類的載入和解除安裝;-XX:+DisableExplicitGC: 禁止使用程式碼呼叫gc

可以用如下方式設定:

如何根據自己的系統配置jvm

1。 需要記錄web應用的tps, 以及yong GC時間間隔和空間差值;2。 這樣就可以知道, 每秒鐘的請求量, 以及每次GC時間間隔和空間, 使用公式: 每個請求平均佔用空間=空間/(tps*GC);3。 每個網站都有訪問高峰(根據業務而定)或者公司規定的tps要達到的要求, 使用峰值tps*每個請求的空間=新生代空間大小4。 調優的目的: 要是jvm不進行老年代垃圾回收, 同時新生代回收頻率較低, 則要讓所有的物件都在新生代儲存, 這樣能保證系統的穩定性;5。 一般來說新生代和老年的空間大小是分配的相同的, 這樣就可以估算出java堆記憶體的最小大小, 然後就可以根據自己的系統執行情況分配記憶體;

6。 對於永久代和棧記憶體, 沒有可以參考的規範, 一般前者設定幾百兆的空間, 後者設定1M左右的空間, 應該是足夠的, 這些區域在有些情況下也會發生溢位現象, 根據實際情況調整;

以上的只是估算, 而且只是一個大概的估算的方法, jvm調優是一個反覆除錯的過程, 而且每個系統在不同情況下的記憶體需求也不相同;

通常都使用4核8G記憶體伺服器, 可以應對大部分web應用;

物件的引用

1。 jvm使用可達性分析演算法查詢哪些物件還在被引用, 一般方法(方法未

從棧區彈出)的區域性變數和類的靜態變數引用的物件都是不會被回收的;

2。 物件的引用分為強引用, 軟引用, 弱引用和虛引用

強引用: 根據1中來判斷;

軟引用: 一般不會回收, 但是當記憶體空間不足時, 會將軟引用回收;

弱引用: 每次垃圾回收都會回收引用物件;

垃圾回收演算法

複製演算法(新生代)

基本原理:

將記憶體區域分為兩部分, 每次使用的時候只向一個記憶體區域儲存數

據, 當一個記憶體區域要滿的時候, 進行引用查詢, 將還存活的物件儲存到

另一個區域中, 然後將原來的記憶體一次清空;缺點:

記憶體的使用率太低, 因為每次垃圾回收的物件只是一小部分, 卻需要

一大塊記憶體來儲存;解決辦法: 引入Eden區域和兩塊survivor區域

survivor區域佔記憶體的10%, Eden區域佔新生代記憶體的80%, 每次存

活的物件儲存進其中一個survivor區域中;

標記清理演算法(老年代)

就是標記處哪些物件存活, 哪些物件已經不被引用, 將不被引用的物件清

理掉, 存活的物件移動到記憶體區域的集中位置, 避免記憶體碎片;

物件什麼時候進入老年代

15次yang gc之後, 依然沒有回收的物件進入老年代(可配置);

動態年齡判斷: survivor區域存放物件超過區域的50%, 那麼此時大於等於survivor區域內物件年齡的物件會被放到老年代;

大物件直接進入老年代;

Yong GC之後, 存活物件survivor區域不能儲存;

老年代Full GC的觸發條件

YongGC之前的檢查, 如果每次yong gc之後存活物件的平均值大於老年代剩餘空間, 觸發;

老年代空間不足以存放物件時觸發;

垃圾回收器分類

ParNew:

用於新生代, serial是原始的新生代垃圾回收器, parNew比他好的地方是可以使用多核, 啟動多個執行緒, 而serial只能使用單執行緒, 基於複製演算法;

CMS:

用於老年代, 基於標記清理演算法, 原理:

基本: 垃圾回收執行緒和系統工作執行緒同時進行;

1。 初識標記: 停止系統執行, 對老年代物件標記, 檢視哪些已經不被引

用(只判斷GC Root直接引用的物件), 雖然stop world, 但是執行快;

2。 併發標記: 系統繼續執行, 對已經標記的物件進行深入跟蹤(檢視GC

root引用還有哪些), 判斷到底有沒有被真正的引用, 比較耗時, 但系統

繼續執行;

3。 重新標記: 停止系統執行, 對已經標記的和新加入的物件進行重新標

記, 速度快;

4。 併發清除: 系統執行, 對物件進行清除, 速度慢;

如果在上述階段, 老年代的剩餘記憶體不夠用了, 就會觸發Concurrent

Mode Failure, 自動使用old serial垃圾收集器進行垃圾回收, 此時就

會造成系統長時間停頓;

G1: 大資料系統使用的垃圾回收器;

不同於ParNew+CMS, G1自己就可以搞定年輕代和老年代, 他將堆記憶體區

域劃分為多個Region區域, 老年代和年輕代只是邏輯上的區分, 他最好的

地方是可以設定多久的時間內gc時間不能超多多少時間, 主要是透過標記

region區域中的垃圾物件, 判斷回收這個region區域需要多久來做到的;

region到底是屬於新生代還是老年代, 其實在G1中是動態分配的, 這次是

新生代, 下次可能就是老年代了;

G1專有引數:

-XX:UseG1GC: 使用g1垃圾回收器, region區域預設有2048個, 分配大小使用堆記憶體大小/2048;

-XX:G1HeapRegionSize: 定義region大小, 大小隻能是2的倍數, 1M, 2M, 4。。。;

-XX:G1NewSizePercent: 新生代初始時佔用多少記憶體;

-XX:G1MaxNewSizePercent: 定義新生代最多佔多少記憶體, 預設2%, 最大不超過60%;

-XX:MaxGCPauseMills: 設定停頓最大時間;

-XX:InitiatingHeapOccupancyPercent: 定義老年代佔用多少記憶體時, 觸發一次新生代和老年回收;

-XX:G1MixedGCCountTarget: 定義最後一個階段幾次混合回收;

-XX:G1HeapWastePercent: 回收時基於複製標記演算法進行;

-XX:G1MixedGCLiveThresholdPercent: 定義存活物件低於多少時才去回收這個region;

G1特性:

新生代還是有eden和survivor區域的區分, 對於大物件也不同, 有專門

的存放區域, 既不屬於新生代, 也不屬於老年代, 每次垃圾回收的時候都

會去回收這個區域;

G1回收步驟:    1。 初識標記    2。 併發標記    3。 最終標記    4。 回收

為什麼Yong gc比full gc速度快很多?

年輕代比老年代存活的物件要少很多;

老年代除了標記清除之外, 還要進行記憶體碎片的清理, 另外還有可能發生Concurrent Mode Failure;

jvm命令使用

jstat -gc PID

1。 S0C:這是From Survivor區的大小

2。 S1C:這是To Survivor區的大小

3。 S0U:這是From Survivor區當前使用的記憶體大小

4。 S1U:這是To Survivor區當前使用的記憶體大小

5。 EC:這是Eden區的大小

6。 EU:這是Eden區當前使用的記憶體大小

7。 OC:這是老年代的大小

8。 OU:這是老年代當前使用的記憶體大小

9。 MC:這是方法區(永久代、元資料區)的大小

10。 MU:這是方法區(永久代、元資料區)的當前使用的記憶體大小11。 YGC:這是系統執行迄今為止的Young GC次數

12。 YGCT:這是Young GC的耗時

13。 FGC:這是系統執行迄今為止的Full GC次數

14。 FGCT:這是Full GC的耗時

15。 GCT:這是所有GC的總耗時

jstat其他命令

jstat -gccapacity PID:堆記憶體分析

jstat -gcnew PID:年輕代GC分析,這裡的TT和MTT可以看到物件在年輕代存活的年齡和存活的最大年齡

jstat -gcnewcapacity PID:年輕代記憶體分析

jstat -gcold PID:老年代GC分析

jstat -gcoldcapacity PID:老年代記憶體分析

jstat -gcmetacapacity PID:元資料區記憶體分析

jstat -gc pid 1000 10: 每個一秒列印一次, 列印10次;

jmap -heap pid: 列印資訊如下

Heap Configuration:

MinHeapFreeRatio         = 40

MaxHeapFreeRatio         = 70

MaxHeapSize              = 478150656 (456。0MB)

NewSize                  = 10485760 (10。0MB)

MaxNewSize               = 159383552 (152。0MB)

OldSize                  = 20971520 (20。0MB)

NewRatio                 = 2

SurvivorRatio            = 8

MetaspaceSize            = 21807104 (20。796875MB)

CompressedClassSpaceSize = 1073741824 (1024。0MB)

MaxMetaspaceSize         = 17592186044415 MB

G1HeapRegionSize         = 0 (0。0MB)Heap Usage:New Generation (Eden + 1 Survivor Space):

capacity = 9502720 (9。0625MB)

used     = 883072 (0。8421630859375MB)

free     = 8619648 (8。2203369140625MB)

9。292834051724139% usedEden Space:

capacity = 8454144 (8。0625MB)

used     = 840504 (0。8015670776367188MB)

free     = 7613640 (7。260932922363281MB)

9。941917242005815% usedFrom Space:

capacity = 1048576 (1。0MB)

used     = 42568 (0。04059600830078125MB)

free     = 1006008 (0。9594039916992188MB)

4。059600830078125% usedTo Space:

capacity = 1048576 (1。0MB)

used     = 0 (0。0MB)

free     = 1048576 (1。0MB)

0。0% usedtenured generation:

capacity = 20971520 (20。0MB)

used     = 13647432 (13。015205383300781MB)

free     = 7324088 (6。984794616699219MB)

65。0760269165039% used

jmap -histo pid: 列印資訊如下, 類佔用空間大小

1543:             1             16  sun。reflect。GeneratedMethodAccessor331544:             1             16  sun。reflect。GeneratedMethodAccessor41545:             1             16  sun。reflect。GeneratedMethodAccessor51546:             1             16  sun。reflect。GeneratedMethodAccessor61547:             1             16  sun。reflect。GeneratedMethodAccessor71548:             1             16  sun。reflect。GeneratedMethodAccessor81549:             1             16  sun。reflect。GeneratedMethodAccessor91550:             1             16  sun。reflect。ReflectionFactory1551:             1             16  sun。reflect。generics。tree。BottomSignature1552:             1             16  sun。reflect。generics。tree。IntSignature1553:             1             16  sun。reflect。generics。tree。VoidDescriptor1554:             1             16  sun。security。util。AlgorithmDecomposer1555:             1             16  sun。security。util。ByteArrayLexOrder1556:             1             16  sun。security。util。ByteArrayTagOrder1557:             1             16  sun。security。util。DisabledAlgorithmConstraints$Constraints1558:             1             16  sun。util。calendar。Gregorian1559:             1             16  sun。util。locale。provider。AuxLocaleProviderAdapter$NullProvider1560:             1             16  sun。util。locale。provider。CalendarDataUtility$CalendarWeekParameterGetter1561:             1             16  sun。util。locale。provider。SPILocaleProviderAdapter1562:             1             16  sun。util。locale。provider。TimeZoneNameUtility$TimeZoneNameGetter1563:             1             16  sun。util。resources。LocaleData1564:             1             16  sun。util。resources。LocaleData$LocaleDataResourceBundleControl1565:             1             16  websocket。drawboard。DrawboardContextListener

jmap -dump:live,format=b,file=dump。hprof pid

在當前目錄下生成儲存快照

jhat -J-mx768m -port 7000 dump。hprof

使用瀏覽器檢視儲存快照, 訪問ip:7000

發生OOM的區域

Matespace區域: 區域設定過小, 或者程式碼編寫有問題, 載入了大量class

虛擬機器棧: 無限制的呼叫方法, 比如遞迴呼叫, 迴圈呼叫;

堆記憶體: 即使在gc之後, 堆記憶體依然不夠用;

#在發生oom時進行dump快照輸出-XX:+HeapDumpOnOutOfMemeryError-XX:HeapDumpPath=/。。。

上一篇:出現這三行程式碼,意味著你的鴻蒙手機最佳化好了,我等了半個月
下一篇:華為內部首次公開原始碼筆記,看完你還敢說你不會SpringBoot?