面試「JAVA進階」JVM

1、內存模型

1.1、堆

堆是所有線程共享的,主要存放對象實例和數組。新生代和老年代的比例是1:2。新生代中三個區域的比例是 8 : 1 : 1。

1.1.1、新生代

對象分配在eden區中,當eden區滿時會觸發minor gc,將eden區中存活的對象,複製到survivor0區中,清空eden區,當survivor0中滿了時,會將存活的對象複製到survivor1區中,然後將survivor0和survivor1交換,保持survivor1是空的。每經過一次yong gc 年齡就+1。

Eden

對象創建,對象分配在eden區,當eden區滿了,再創建對象的時候,會觸發minor gc,進行Eden和from surivior區域的垃圾回收。

FromServivorToSurvivor

minor gc後還存活的對象會被放入此區域,當對象年齡到達閾值後會進入老年代。或者to surivior區域滿了,會將對象放入老年代。

1.1.2、老年代

大對象,需要大量連續內存空間的對象長期存活的對象,對象年齡超過15(默認值)yong gc後survivor區容不下的對象。

1.2、JVM棧

線程私有的,每個線程都有一個棧,主要存放當前線程的局部變量,程序運行狀態,方法返回值,方法出口等。

1.3、本地方法棧

為虛擬機使用到的native方法服務。

1.4、方法區

用於存放已經被夾在的類信息,常量,靜態變量,1.8後取消了永久代,增加了元空間,元空間並不在虛擬機中,而是用的是本地內存。元空間中存放類的元信息,靜態變量和常量池移入堆中。

1.5、程序計數器

程序私有,生命週期與程序相同當前線程所執行的字節碼的行號指示器。用來實現分支,循環,跳轉,異常等功能。

2、常量池中包括什麼

常量池在編譯時期確定,存放在編譯生成的class文件中,包含了基本數據類型和對象類型(String和數組)

3、如何判斷對象是否存活

使用什麼方法標記一個對象可回收?

引用計數法,每個對象都有一個引用計數器,被引用+1 當引用數為0即為可被GC的對象可達性分析:從根節點出發,向下搜索,未訪問到的對象標記為不可達,可被回收

4、哪些對象可以用為GC ROOT對象

虛擬機棧中引用的對象。方法區中靜態對象引用的對象。方法區中常量引用的對象。本地方法棧中引用的對象。

5、GC策略

標記清除法,從根節點進行掃描,對存活的對象進行標記,標記完成後,再掃描整個空間中未被標記的對象,進行清理。容易造成內存碎片複製,將內存劃分為兩份,當其中一份內存滿了時,從根節點掃描,將存活的對象複製到另一份內存中。不會出現內存碎片問題,但需要兩倍的空間。標記整理,如標記清除法一樣,標記對象,清除後將所有存活對象向左移。避免了內存碎片和兩倍空間的問題,但增加了移動對象的成本。

6、具體GC收集器

串行垃圾收集器,serial並行垃圾收集器 parNew,parallel 注重吞吐量cms 注重最短回收停頓時間G1

cms和G1的區別 :

cms是新生代的垃圾收集器,採用標記清除。G1是新生代和老年代的垃圾收集器,採用標記整理。cms會產生內存碎片,G1並不會。Cms追求最小停頓時間,G1是達到可控的停頓時間,儘可能提高吞吐量。

7、什麼樣的對象進入老年代

大對象,需要大量連續內存空間的對象。長期存活的對象,對象年齡超過15(默認值)。yong Gc後survivor區容不下的對象。

8、為什麼要區分新生代和老年代

對象的生存情況不同使用不同的GC算法。新生代對象可能被頻繁的創建和回收,老年代回收較少。

9、survivor區存在的意義

為了提高對象進入老年代的門檻,減少fullGC的次數,因為fullGC很耗時。兩個survivor的作用是為了減少survivor區的內存碎片。

10、什麼是yangGC

對年輕代進行gc。觸發條件:

eden區不足清空eden from to中沒被引用的對象。將eden from中存活的對象 複製到 to中。將to中的對象晉升到old中,包括兩類對象,一個是年齡到達閾值,一個是to中放不下。full gc也會出發yong gc

11、什麼時候觸發fullGC

手動觸發的GC。老年代的空間不足。永久代的空間滿了(方法區)。統計到yong gc晉升到老年代的平均大小大於老年代剩餘的大小(老年代的空間不足)。jvm自身固定頻率的fullGC(默認一小時執行一次)。

12、內存的配置參數

xms xmx 配置堆內存的最小和最大值xmn 年輕代內存的初始大小xss jvm棧大小

13、對象分配內存的兩種方式

指針碰撞,如果內存對象是規整的,採用指針碰撞來為對象分配內存,所有使用過的內存在指針的一側,未使用過的內存在指針的另一側,分配內存只需要移動指針即可。空閒列表,內存不規整,使用過的內存和未使用過的內存交織在一起,維護一個內存使用列表,記錄那些內存是可用的,在分配的時候找到一塊足夠大的空間劃分給對象,並更新列表上的內容。

14、如何減少GC的開銷

避免顯示的調用System.gc。儘量減少臨時對象的使用。對象不使用時,最好顯式的置為null。儘量使用StringBuffer而不用String累加字符串。能用基本類型就是用基本類型。儘量少用靜態對象變量。

15、什麼是JAVA內存模型(JMM)

用於屏蔽掉各種硬件和操作系統的內存訪問差異,以實現讓java程序在各個平臺下都能達到一致的併發效果。

16、什麼時happens-before

保證了內存的可見性制定了四個規則:程序順序規則:一個線程中的每個操作 happens-before 與後續的所有操作。監視器鎖規則:一個監視器解鎖 happens-before 於 加鎖。volatile變量規則: 寫操作 happens-before 讀操作。傳遞性 A happens-before B ,B happens-before C ,那麼Ahappens-before C。

17、性能調優工具

17.1、jps

jps主要用來輸出jvm中運行的進程狀態信息。

-l 輸出main類或者jar的權限名。

17.2、jstack

jstack pid > log

可以將線程堆棧轉存到文件中。

日誌分析可以使用fastthread.io。

17.3、jstat

可以顯示出虛擬機進程中的classloader、內存、gc等運行數據。

參數

-class pid 類加載統計。

-gc 垃圾回收統計 ,後面跟兩個參數一個是間隔輸出時間,一個是總共輸出次數。

gc日誌可以使用 gceasy.io

17.4、jmap

jmap查看堆內存的使用情況:

jmap pid

17.5、jinfo

查看java程序的運行環境參數:

jinfo pid

18、內存柵欄

通過確保從另一個CPU來看,屏障的兩邊的所有指令都是正確的程序順序,而保持程序順序的外部可見性;其次可以實現內存數據可見性,確保內存數據會同步到CPU緩存子系統。

19、JVM產生的內存溢出及解決辦法

java heap space

代碼中存在大對象的分配,多次GC後仍找不到分配空間。

解決辦法:查看是否有大對象分配尤其是大數組。通過jmap把堆內存的日誌dump下來,分析日誌,如果解決不了增加堆內存的空間。

permspace metaspace

永久代或元空間溢出。生成大量的代理類或者使用自定義的類加載器。

解決辦法:查看有沒有配置永久代或者元空間的大小。是否長時間沒有重啟jvm,是否有大量的反射操作。