【017期】JavaSE面試題(十七):JVM之記憶體模型
開篇介紹
大家好,我是Java最全面試題庫
的提褲姐,今天這篇是面試系列的第十七篇,主要總結了JavaSE中JVM
相關面試題,這篇是JVM系列的第一篇,主要講解JVM的記憶體模型
,第二篇主要講解垃圾回收。在後續,會沿著第一篇開篇的知識線路一直總結下去,做到日更!如果我能做到百日百更,希望你也可以跟著百日百刷,一百天養成一個好習慣。
說一下JVM記憶體模型及分割槽,每個區放什麼?
JVM分為:
- 堆區
- 棧區
- 方法區
①方法區:主要是儲存類資訊,常量池(static量和 static變數),編譯後的程式碼(位元組碼)等資料
②堆:初始化的物件,成員變數(那種非 static的變數),所有的物件例項和陣列都要在堆上分配
③棧:棧的結構是棧幀組成的,呼叫一個方法就壓入一幀,幀上面儲存區域性變量表,運算元棧,方法出口等資訊,區域性變量表存放的是8大基礎型別加上一個引用型別,所以還是一個指向地址的指標
④本地方法棧:主要為 Native方法服務
⑤程式計數器:記錄當前執行緒執行的行號
總結:初始化的物件放在堆裡面,引用放在棧裡面,class類資訊常量池(static常量和 static變數)等放在方法區
JVM記憶體區域主要分為:
- 執行緒私有區(程式計數器、虛擬機器棧、本地方法區)
- 執行緒共享區(Java堆、方法區)
- 直接記憶體
說一下Java記憶體分配
- 暫存器:無法控制。
- 靜態域: static定義的靜態成員。
- 常量池:編譯時被確定並儲存在
. class檔案
中的final常量值
和一些文字修飾的符號引用(類和介面的全限定名,欄位的名稱和描述符,方法和名稱和描述符)。 - 非RAM儲存:硬碟等永久儲存空間。
- 堆記憶體:new建立的物件和陣列,由Java虛擬機器自動垃圾回收器管理,存取速度慢。
- 棧記憶體:基本型別的變數和物件的引用變數(堆記憶體空間的訪問地址),速度快,可以共享,但是大小與生存期必須確定,缺乏靈活性。
新生代中為什麼要分為Eden和Survivor?
如果沒有Survivor,Eden區每進行一次Minor GC
,存活的物件就會被送到老年代。老年代很快被填滿,觸發Major GC
.老年代的記憶體空間遠大於新生代,進行一次Full GC
消耗的時間比Minor GC
長得多,所以需要分為Eden和Survivor。
Survivor的存在意義,就是減少被送到老年代的物件,進而減少Full GC的發生,Survivor的預篩選保證,只有經歷16
次Minor GC還能在新生代中存活的物件,才會被送到老年代。
設定兩個Survivor區最大的好處就是解決了碎片化
物件如何晉升到老年代?
1、大物件直接進入老年態
2、經過一次Minor GC,年齡+1,若年齡超過一定限制(15),則被晉升到老年態。即長期存活的物件進入老年態
元資料區存放的是什麼?會oom嗎?
Perm Space中儲存的是載入class檔案
。
會引起OutOfMemory,出現異常可以設定 -XX:PermSize
的大小。
JDK 1.8後,字串常量不存放在永久代,而是在堆記憶體中,JDK1.8以後沒有永久代概念,而是用元空間替代,元空間不存在虛擬機器中,二是使用本地記憶體。
jvm載入class原理
JVM中類的裝載是由類載入器(ClassLoader)
和它的子類
來實現的,Java中的類載入器是一個重要的Java執行時系統元件,它負責在執行時查詢和裝入類檔案中的類。
由於Java的跨平臺性,經過編譯的Java源程式並不是一個可執行程式,而是一個或多個類檔案。當Java程式需要使用某個類時,JVM會確保這個類已經被載入、連線(驗證、準備和解析)和初始化。
類的載入是指把類的.class檔案中的資料讀入到記憶體中,通常是建立一個位元組陣列讀入.class檔案,然後產生與所載入類對應的Class物件。載入完成後,Class物件還不完整,所以此時的類還不可用。當類被載入後就進入連線階段,這一階段包括驗證
、準備
(為靜態變數分配記憶體並設定預設的初始值)和解析
(將符號引用替換為直接引用)三個步驟。最後JVM對類進行初始化,包括:
①如果類存在直接的父類並且這個類還沒有被初始化,那麼就先初始化父類;
②如果類中存在初始化語句,就依次執行這些初始化語句。類的載入是由類載入器完成的,類載入器包括:根載入器(BootStrap)、擴充套件載入器(Extension)、系統載入器(System)和使用者自定義類載入器(java.lang.ClassLoader的子類)。從Java 2(JDK 1.2)開始,類載入過程採取了父親委託機制(PDM)。PDM更好的保證了Java平臺的安全性,在該機制中,JVM自帶的Bootstrap是根載入器,其他的載入器都有且僅有一個父類載入器。類的載入首先請求父類載入器載入,父類載入器無能為力時才由其子類載入器自行載入。JVM不會向Java程式提供對Bootstrap的引用。
哪些區域可能會發生oom?
記憶體區域 | 是否執行緒私有 | 是否會發生OOM |
---|---|---|
程式計數器 | 是 | 否 |
虛擬機器棧 | 是 | 是 |
本地方法棧 | 是 | 是 |
方法區 | 否 | 是 |
直接記憶體 | 否 | 是 |
堆 | 否 | 是 |
Java堆的結構是什麼樣子的?
JVM的堆是執行時資料區,所有類的例項和陣列都是在堆上分配記憶體。
它在JVM啟動的時候被建立。
物件所佔的堆記憶體是由自動記憶體管理系統也就是垃圾收集器回收。
堆記憶體是由存活和死亡的物件組成的。
存活的物件是應用可以訪問的,不會被垃圾回收。
死亡的物件是應用不可訪問尚且還沒有被垃圾收集器回收掉的物件。
一直到垃圾收集器把這些物件回收掉之前,他們會一直佔據堆記憶體空間。