面試專題(JVM 虛擬機)

Java內存模型問題

jvm 進程內存邏輯結構java

直接內存:不是虛擬機運行時數據區的一部分,也不是java虛擬機規範中定義的內存區域;
 若是使用了NIO,這塊區域會被頻繁使用,在java堆內能夠用directByteBuffer對象直接引用並操做;
 這塊內存不受java堆大小限制,但受本機總內存的限制,能夠經過MaxDirectMemorySize來設置(默認與堆內存最大值同樣),因此也會出現OOM異常;web

 

你對jvm內存結構瞭解嗎?面試

程序計數器:較小的內存空間,當前線程執行的字節碼的行號指示器;各線程之間獨立存儲,互不影響;算法

java 棧:線程私有,生命週期和線程,每一個方法在執行的同時都會建立一個 棧幀用於存儲局部變量表,操做數棧,動態連接,方法出口等信息。方法的執行就對應着棧幀在虛擬機棧中入棧和出棧的過程;棧裏面存放着各類基本數據類型和對象的引用;後端

本地方法棧:本地方法棧保存的是native方法的信息,當一個JVM建立的線程調用native方法後,JVM再也不爲其在虛擬機棧中建立棧幀,JVM只是簡單地動態連接並直接調用native方法;tomcat

堆:Java堆是Javaer須要重點關注的一塊區域,由於涉及到內存的分配(new關鍵字,反射等)與回收(回收算法,收集器等);安全

方法區:也叫永久區,用於存儲已經被虛擬機加載的類信息,常量("zdy","123"等),靜態變量(static變量)等數據。服務器

運行時常量池:運行時常量池是方法區的一部分,用於存放編譯期生成的各類字面量("zdy","123"等)和符號引用。網絡

 

你對jvm內存結構瞭解嗎?線程共享與線程私有多線程

堆和棧的區別是什麼?

  功能
 以棧幀的方式存儲方法調用的過程,並存儲方法調用過程當中基本數據類型的變量(int、short、long、byte、float、double、boolean、char等)以及對象的引用變量,其內存分配在棧上,變量出了做用域就會自動釋放;
 而堆內存用來存儲Java中的對象。不管是成員變量,局部變量,仍是類變量,它們指向的對象都存儲在堆內存中;


 線程獨享仍是共享
棧內存歸屬於單個線程,每一個線程都會有一個棧內存,其存儲的變量只能在其所屬線程中可見,即棧內存能夠理解成線程的私有內存。
堆內存中的對象對全部線程可見。堆內存中的對象能夠被全部線程訪問。

 

 空間大小
的內存要遠遠小於堆內存,棧的深度是有限制的,若是遞歸沒有及時跳出,極可能發生StackOverFlowError問題。
 你能夠經過-Xss選項設置棧內存的大小。-Xms選項能夠設置堆的開始時的大小,-Xmx選項能夠設置堆的最大值

 

你對jvm內存結構瞭解嗎?堆和棧

 

你對jvm內存結構瞭解嗎?線程安全的本質

 

jdk1.六、jdk1.7和jdk1.8內存結構區別

jdk1.8的jvm 進程內存邏輯結構

爲何去除方法區

 永久代來存儲類信息、常量、靜態變量等數據不是個好主意, 很容易遇到內存溢出的問題.JDK8的實現中將類的元數據放入 native memory, 將字符串池和類的靜態變量放入java堆中. 可使用MaxMetaspaceSize對元數據區大小進行調整;
 對永久代進行調優是很困難的,同時將元空間與堆的垃圾回收進行了隔離,避免永久代引起的Full GC和OOM等問題;

 

jvm經常使用內存參數設置

注意: java8去掉了-XX:PermSize和-XX:MaxPermSize,新增了-XX:MetaspaceSize和-XX:MaxMetaspaceSize

 

常見內存溢出異常問題

有哪些java內存溢出異常?

java內存溢出異常主要有兩個:
 OutOfMemeoryError:當堆、棧(多線程狀況)、方法區、元數據區、直接內存中數據達到最大容量時產生;
 StackOverFlowError:若是線程請求的棧深度大於虛擬機鎖容許的最大深度,將拋出StackOverFlowError,其本質仍是數據達到最大容量;

 

什麼狀況下出現堆溢出?怎麼解決?

  產生緣由
堆用於存儲實例對象,只要不斷建立對象,而且保證GC Roots到對象之間有引用的可達,避免垃圾收集器回收實例對象,就會在對象數量達到堆最大容量時產生OutOfMemoryError異常。
java.lang.OutOfMemoryError: Java heap space

  解決辦法
使用-XX:+HeapDumpOnOutOfMemoryError可讓java虛擬機在出現內存溢出時產生當前堆內存快照以便進行異常分析,主要分析那些對象佔用了內存;也可以使用jmap將內存快照導出;通常檢查哪些對象佔用空間比較大,由此判斷代碼問題,沒有問題的考慮調整堆參數;

 

什麼狀況下出現棧溢出?怎麼解決?

  產生緣由
 若是線程請求的棧深度大於虛擬機鎖容許的最大深度,將拋出StackOverFlowError;
 若是虛擬機在擴展棧時沒法申請到足夠的內存空間,拋出OutOfMemeoryError;

  解決辦法
 StackOverFlowError 通常是函數調用層級過多致使,好比死遞歸、死循環;
 OutOfMemeoryError通常是在多線程環境纔會產生,通常用「減小內存的方法」,既減小最大堆和減小棧容量來換取更多的線程支持;

 

什麼狀況下出現方法區或元數據區溢出?怎麼解決?

  產生緣由
 jdk 1.6之前,運行時常量池仍是方法區一部分,當常量池滿了之後(主要是字符串變量),會拋出OOM異常;
 方法區和元數據區還會用於存放class的相關信息,如:類名、訪問修飾符、常量池、方法、靜態變量等;當工程中類比較多,而方法區或者元數據區過小,在啓動的時候,也容易拋出OOM異常

  解決辦法
 jdk 1.7以前,經過-XX:PermSize,-XX:MaxPerSize,調整方法區的大小;
 jdk 1.8之後,經過-XX:MetaspaceSize ,-XX:MaxMetaspaceSize,調整元數據區的大小;

 

什麼狀況下出現本機直接內存溢出?怎麼解決?

  產生緣由
jdk自己不多操做直接內存,而直接內存(DirectMemory)致使溢出最大的特徵是,Heap Dump文件不會看到明顯異常,而程序中直接或者間接的用到了NIO;
  解決辦法
直接內存不受java堆大小限制,但受本機總內存的限制,能夠經過MaxDirectMemorySize來設置(默認與堆內存最大值同樣)

 

垃圾回收面試問題

關於垃圾回收咱們必需要了解的知識

 垃圾回收主要回收的是堆內存,基於分代的思想:

內存怎麼樣分配

  對象分配
 優先在Eden區分配。當Eden區沒有足夠空間分配時, VM發起一次Minor GC, 將Eden區和其中一塊Survivor區內尚存活的對象放入另外一塊Survivor區域。如MinorGC時survivor空間不夠,對象提早進入老年代,老年代空間不夠時進行Full GC;
 大對象直接進入老年代,避免在Eden區和Survivor區之間產生大量的內存複製, 此外大對象容易致使還有很多空閒內存就提早觸發GC以獲取足夠的連續空間.
  對象晉級
 年齡閾值:VM爲每一個對象定義了一個對象年齡(Age)計數器, 經第一次Minor GC後仍然存活, 被移動到Survivor空間中, 並將年齡設爲1. 之後對象在Survivor區中每熬過一次Minor GC年齡就+1. 當增長到必定程度(-XX:MaxTenuringThreshold, 默認15), 將會晉升到老年代.
 提早晉升: 動態年齡斷定;若是在Survivor空間中相同年齡全部對象大小的總和大於Survivor空間的一半, 年齡大於或等於該年齡的對象就能夠直接進入老年代, 而無須等到晉升年齡.

 

哪些要收回?對象生死斷定

  可達性分析算法
經過一系列的稱爲 GC Roots 的對象做爲起點, 而後向下搜索; 搜索所走過的路徑稱爲引用鏈/Reference Chain, 當一個對象到 GC Roots 沒有任何引用鏈相連時, 即該對象不可達, 也就說明此對象是不可用的;

在Java, 可做爲GC Roots的對象包括:
1.方法區: 類靜態屬性引用的對象;
2.方法區: 常量引用的對象;
3.虛擬機棧(本地變量表)中引用的對象.
4.本地方法棧JNI(Native方法)中引用的對象。

 

怎麼回收?方法論?分代收集

  新生代- 標記清除法
該算法分爲「標記」和「清除」兩個階段: 首先標記出全部須要回收的對象(可達性分析), 在標記完成後統一清理掉全部被標記的對象.

  缺點
 效率問題: 標記和清除過程的效率都不高;
 空間問題: 標記清除後會產生大量不連續的內存碎片, 空間碎片太多可能會致使在運行過程當中須要分配較大對象時沒法找到足夠的連續內
存而不得不提早觸發另外一次垃圾收集.

 

  新生代- 複製算法
該算法的核心是將可用內存按容量劃分爲大小相等的兩塊, 每次只用其中一塊, 當這一塊的內存用完, 就將還存活的對象複製到另一塊上面, 而後把已使用過的內存空間一次清理掉.

  優勢
 因爲是每次都對整個半區進行內存回收,內存分配時沒必要考慮內存碎片問題。
 垃圾回收後空間連續,只要移動堆頂指針,按順序分配內存便可;
 特別適合java朝生夕死的對象特色;


  缺點
 內存減小爲原來的一半,太浪費了;
 對象存活率較高的時候就要執行較多的複製操做,效率變低;、
 若是不使用50%的對分策略,老年代須要考慮的空間擔保策略

 

怎麼回收?方法論?分代收集

  老年代- 標記整理算法
該算法分爲「標記」和「清除」兩個階段:  首先標記出全部須要回收的對象(可達性分析), 在標記完成後讓全部存活的對象都向一端移動,而後清理掉端邊界之外的內存;

  優勢
 不會損失50%的空間;
 垃圾回收後空間連續,只要移動堆頂指針,按順序分配內存便可;
 比較適合有大量存活對象的垃圾回收;
  缺點
 標記/整理算法惟一的缺點就是效率也不高,不只要標記全部存活對象,還要整理全部存活對象的引用地址。從效率上來講,標記/整理算法要低於複製算法。

 

實現回收?誰來作?垃圾回收器

實現回收?誰來作?垃圾回收器

垃圾回收默認配置及互聯網後臺推薦配置

 在JVM的客戶端模式(Client)下,JVM默認垃圾收集器是串行垃圾收集器(Serial GC + Serial Old,-XX:+USeSerialGC);
 在JVM服務器模式(Server)下默認垃圾收集器是並行垃圾收集器(ParallelScavaenge +Serial Old,-XX:+UseParallelGC)
 而適用於Server模式下
 ParNew + CMS + SerialOld(失敗擔保),-XX:UseConcMarkSweepGC;
 Parallel scavenge + Parallel,-XX:UseParallelOldGC

JVM 垃圾回收面試常見面試題

 JVM 垃圾回收面試常見面試題
 垃圾回收經常使用的算法有哪些?特色是什麼?(見垃圾回收算法)
 哪幾種垃圾收集器,各自的優缺點,重點講下cms(見垃圾回收器)
 jvm中一次完整的GC流程(從ygc到fgc)是怎樣的,重點講講對象如何晉升到老年代等(見內存怎麼樣分配)
 JVM垃圾回收機制,什麼時候觸發MinorGC或FullGC等操做
答:從年輕代空間(包括 Eden 和 Survivor 區域)回收內存被稱爲 Minor GC,對老年代GC稱爲Major GC,而Full GC是對整個堆來講;Minor GC觸發條件:當Eden區滿時,觸發Minor GC。

Full GC觸發條件:
 System.gc()
 老年代空間不足
 永生區空間不足
 統計獲得的Minor GC晉升到舊生代的平均大小大於老年代的剩餘空間
 堆中分配很大的對象

 

性能調優工具問題

面試題

1. 你經常使用的調優工具備哪些?
2. 若是碰到應用故障你怎麼樣排除問題?

 

java經常使用調優工具

堆dump分析

 堆dump分析:堆dump分析主要目的是定位OOM異常的緣由;解決oom問題四部曲:
1. 分析OOM異常的緣由,堆溢出?棧溢出?本地內存溢出?
2. 若是是堆溢出,導出堆dump,並對堆內存使用有個總體瞭解;
3. 找到最有可能致使內存泄露的元兇,一般也就是消耗內存最多的對象;
4. 使用輔助工具對dump文件進行分析;
 注意其餘幾類形成OOM異常的緣由:
1. Direct Memory
2. 線程堆棧:
單線程:StackOverflowError
多線程:OutOfMemoryError:unable to create new native thread
3. Socket 緩衝區:IOException:Too many open files

線程dump分析

 線程dump分析:線程dump分析主要目的是定位線程長時間停頓的緣由;

應用故障你怎麼樣排除問題?

 應用故障通常指應用運行緩慢、用戶體驗差或者週期性的出現卡頓,排除的思路:
1. 檢查應用所在的生產環境的軟硬件以及網絡環境,排除外圍因素;
2. 肯定是否爲OOM異常,這類異常影響最惡劣,可是比較容易排查;
3. 肯定是否有大量長時間停頓的應用線程,很是佔用cpu資源;
4. 週期性的卡頓極可能是垃圾回收形成,web後端系統建議使用cms垃圾回收器;

 

類加載機制問題

類的完整生命週期

何時出發類加載?

1. 使用new關鍵字實例化對象,讀取或者設置一個類的靜態變量的時候,調用類的靜態方法的時候;
2. 對類進行反射調用的時候;
3. 初始化子類時,父類會先被初始化;
4. 對類使用動態代理的時候須要先被初始化

談下你對雙親委派模型理解?

談下你對雙親委派模型理解?

 雙親委派模型好處
Java類隨着它的類加載器一塊兒具有了帶有優先級的層次關係,保證java程序穩定運行

 

tomcat 類加載機制

 同一個tomcat容器下的兩個應用以及lib目錄中都有UserServiceImpl類,tomcat怎麼樣保證類的隔離性?

類加載器與類的惟一性:類加載器雖然只用於實現類的加載動做,可是對於任意一個類,都須要由加載它的類加載器和這個類自己共同確立其在Java虛擬機中的惟一性。通俗的說,JVM中兩個類是否「相等」,首先就必須是同一個類加載器加載的,不然,即便這兩個類來源於同一個Class文件,被同一個虛擬機加載,只要類加載器不一樣,那麼這兩個類一定是不相等的。

 Tomcat目錄結構中,有三組目錄(「/common/*」,「/server/*」和「shared/*」)能夠存放公用Java類庫,此外還有第四組Web應用程序自身的目錄「/WEB-INF/*」,把java類庫放置在這些目錄中的含義分別是:
 放置在common目錄中:類庫可被Tomcat和全部的Web應用程序共同使用。
 放置在server目錄中:類庫可被Tomcat使用,但對全部的Web應用程序都不可見。
 放置在shared目錄中:類庫可被全部的Web應用程序共同使用,但對Tomcat本身不可見。
 放置在/WebApp/WEB-INF目錄中:類庫僅僅能夠被此Web應用程序使用,對Tomcat和其餘Web應用程序都不可見。
 注意:tomcat的類加載機制是違反了雙親委託原則的,對於一些未加載的非基礎類(Object,String等),各個web應用本身的類加載器(WebAppClassLoader)會優先加載,加載不到時再交給commonClassLoader走雙親委託

相關文章
相關標籤/搜索