面試「JAVA進階」JVM

1、內存模型

1.1、堆

  1. 堆是所有線程共享的,主要存放對象實例和數組。
  2. 新生代和老年代的比例是1:2。
  3. 新生代中三個區域的比例是 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區域的垃圾回收。

  • FromServivor
  • ToSurvivor

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

1.1.2、老年代

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

1.2、JVM棧

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

1.3、本地方法棧

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

1.4、方法區

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

1.5、程序計數器

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

2、常量池中包括什麼

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

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

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

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

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

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

5、GC策略

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

6、具體GC收集器

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

cms和G1的區別 :

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

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

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

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

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

9、survivor區存在的意義

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

10、什麼是yangGC

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

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

11、什麼時候觸發fullGC

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

12、內存的配置參數

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

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

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

14、如何減少GC的開銷

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

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

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

16、什麼時happens-before

  1. 保證了內存的可見性
  2. 制定了四個規則:程序順序規則:一個線程中的每個操作 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產生的內存溢出及解決辦法

  1. java heap space

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

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

  1. permspace metaspace

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

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


面試「JAVA進階」JVM


分享到:


相關文章: