JVM面试速记
文章目錄
- 1.jvm位置
- 2.jvm體系結構
- 3.類加載器
- 4.雙親委派機制
- 6.Native
- 7.PC寄存器
- 8.方法區
- 9.棧
- 10.三種JVM
- 11.堆
- 12.新生區,老年區
- 13.永久區
- 14.堆內存調優
- 15.GC-垃圾回收機制
1.jvm位置
2.jvm體系結構
3.類加載器
- 虛擬機自帶的加載器
- 啟動類(根)加載器 —底層是c寫的,java調用不到,返回null
- 擴展類加載器
- 應用程序加載器
4.雙親委派機制
類加載器在接收到類的請求后,會將任務委托給父類加載器,父類加載器再委托給上一級的加載器,直到根加載器,如果根加載器可以完成此加載任務則返回,如果不能則返回給下一級子加載器。
意義:防止內存中出現多份同樣的字節碼
例子:當你寫了一個java.lang.String方法時,類加載器會找到根加載器中的java.lang.String的方法直接加載根加載器的String方法而不會加載自己寫的方法。
擴展:在Thread中的start方法啟動線程的底層會返回給一個沒有返回值的start0() 但是是被native修飾,這表示調用本地的C++或c的方法棧,因為java無法調用操作系統。
5.沙箱安全機制
6.Native
- native關鍵字:凡是帶native的關鍵字的,說明java的作用范圍到不了,會去調用底層的C語言的庫
- 會進入本地方法棧,調用本地方法接口 JNI - java native interface
- JNI作用:擴展java的使用,讓java可以調用不同的編程語言的接口
- 在JAVA初期,c,c++橫行,想要立足,就必須能夠調用c和c++
- 它內存區域專門開辟了一塊標記區域,本地方法棧,native method stack 用于登記native方法
- 在最終執行的時候,通過JNI加載本地的方法
- 用處:java執行打印機等,在企業級開發中較為少見。
7.PC寄存器
每個線程都有一個程序計數器,是線程私有的,就是一個指針,指向方法區中的字節碼,在執行引擎讀取下一條指令,是一個非常小的空間,可以忽略不計
8.方法區
Method Area 方法區
方法區是被所有線程共享,所有字段和方法字節碼,以及一些特殊的方法,如構造函數,接口代碼也在此定義,簡單說,所有定義的方法的信息都保存在該區域。此區域屬于共享空間
靜態變量,常量,類信息(構造方法,接口定義),運行時的常量池存在方法區中,但是 實例變量存在堆內存中,和方法區無關。
static,final,Class,常量池
9.棧
先進后出
棧:棧內存,主管程序的運行,生命周期和線程同步,
線程結束,棧內存也就釋放,對于棧來說,不存在垃圾回收問題
棧中存放的東西:8大基本類型+對象引用+實例的方法
棧運行原理:棧幀,
10.三種JVM
SUN,IBM,BEA
對于一般的應用而言,建議采用SUN的JVM就足夠了;對于對性能要求很高的應用而言,建議采用BEA的JVM,如java版的游戲服務器;對于有錢的公司而言,建議采用IBM的JVM,那是一整套解決方案,后期維護方便
11.堆
Heap,一個JVM只有一個堆內存,堆內存大小是可以調節的
類加載器讀取類文件后,一般 把類,方法,常量,變量,保存我們所有引用類型的真實對象,放到堆中
堆內存中還要細分為3個區域
- 新生區(伊甸園區)
- 養老區
- 永久區
GC垃圾回收,主要是在伊甸園區和養老區。
假設內存滿了就會報OutOfMemory堆內存不夠錯誤
在JDK8以后,永久存儲區改名為元空間。
12.新生區,老年區
新生區:
- 類誕生,成長和死亡的地方
- 伊甸園:所有的對象都是在伊甸園區new出來的
- 幸存者區(0,1):在伊甸園區滿了之后進行輕GC存留下來的對象進入幸存者區
- 99%的對象都是臨時對象
老年區:
- 在幸存者區經過一定次數的GC后存留下來的對象進入養老區,默認為15次。
13.永久區
這個區域是常駐內存的。用來存放JDK自身攜帶的Class對象。Interface元數據,存儲的是一些java運行時的環境和類信息,這個區域不存在垃圾回收,關閉JVM虛擬機就會釋放這個內存。
在一個啟動類,加載了大量的第三方jar包。Tomcat部署了太多的應用,大量動態生成的反射類。不斷被加載,直到內存滿,就會出現OOM。
- jdk1.6之前:永久代,常量池放在方法區
- jdk1.7 :永久代。常量池在堆中
- jdk1.8之后:無永久代,常量池放在元空間
元空間:邏輯上存在,物理上不存在
public class Test3 {public static void main(String[] args) {//返回JVM試圖使用的最大內存long max = Runtime.getRuntime().maxMemory();//返回jvm的初始化內存long total = Runtime.getRuntime().totalMemory();System.out.println("max="+max+"字節\t"+(max/(double)1024/1024)+"MB");System.out.println("total="+total+"字節\t"+(total/(double)1024/1024)+"MB");//默認情況下:分配的總內存是電腦內存的1/4,而初始化的內存為1/64} }默認情況下:分配的總內存是電腦內存的1/4,而初始化的內存為1/64
設置固定的堆內存,并打印GC信息
新生區+老年區等于總內存(699392+305664)/1024=981.5M
所以說元空間在邏輯上存在,在物理上不存在
14.堆內存調優
在JDK1.8中,元空間取代永久代。元空間和永久代的最大的區別是永久代使用的是JVM的堆內存,元空間不在虛擬機中,而是使用本機物理內存。默認清空下,元空間只受本地內存限制,類的元數據放入本地內存,字符串常量池和類型靜態變量放入java堆,類的元數據的加載量不再受MaxPermSize控制,而是由系統實際的可用空間來控制。
-Xms:初始分配大小,默認為物理內存的1/64
-Xmx:最大分配內存,默認為物理內存的1/4
-XX:+PrintGCDetails:輸出詳細的GC處理日志
15.GC-垃圾回收機制
JVM在進行GC時,并不是對這三個區域統一回收,大部分時候,回收的都是新生代中的Eden。
每次GC 都會將Eden獲得對象移到幸存者區中,一旦Eden被GC后,就會是空的。當一個對象經歷了15次GC,都還沒有死,那么就會進入老年代。
-XX:MaxTenuringTreshold=…,通過這個參數可以設定進入老年代的時間。
GC兩種類:輕GC(普通的GC),重GC(全局GC)
GC題目:
- JVM的內存模型和分區,詳細到每個區都放什么?
- 堆里面的分區有哪些?Eden,from,to,old,說說他們的特點
- GC的算法有哪些?標記清除法,標記壓縮法,復制算法,引用計數器,有什么用?
- 輕GC和重GC分別在什么時候發生?
GC常用算法:
引用計數法:
給每個對象分配一個計數器,每使用一次這個對象就+1。使用次數為0的對象就被回收。
缺點是,計數器本身也會有消耗,而對象的個數太多。內存占用過多。
復制算法:
主要用在新生代。
如果兩個幸存者區都不為空,則會將其中一個幸存者區中的對象放入另一個幸存者區,保證兩個幸存者區中有一個是空的。from和to,誰是空的誰就是to。
- 好處:沒有內存碎片。集中存放在一個區域
- 壞處:浪費了內存空間。一直有一半空間是空的 ,to。假設對象100%存活,那么浪費的空間就會極大。
所以復制算法最佳使用場景:對象存活度比較低的時候。 新生代。
標記清除算法:
? 標記:掃描對象,對活著的對象進行標記。
? 清除:掃描對象,對沒有標記的對象進行清除。
- 缺點:兩次掃描,嚴重浪費時間。會產生內存碎片。
- 優點:不需要額外的空間。
標記壓縮:
對標記清除再優化:
壓縮:防止內存碎片產生,再次掃描,向一端移動存活的對象,多了一個移動成本。
標記清除壓縮:
先標記清除幾次,再進行壓縮
總結
內存效率:復制算法>標記清除法>標記壓縮算法(時間復雜度)
內存整齊度:復制算法=標記壓縮算法>標記清除算法
內存利用率:標記壓縮算法=標記清除算法>復制算法
GC:分代收集算法
年輕代:
- 存活率低
- 復制算法
老年代:
- 區域大,存活率高
- 標記清除(內存碎片不是太多就可以再次清除)+標記壓縮混合實現
總結
- 上一篇: Springbooot集成Shiro简单
- 下一篇: 多线程基础与JUC进阶笔记