一線互聯網面試必問的JVM應該怎麼學(面試題含答案)

方法區與Java堆同樣,是各個線程共享的區域,它用於存儲已被虛擬機加載的類信息,常量,靜態變量,即時編譯(JIT)後的代碼等數據。對於JDK1.8以前的HotSpot虛擬機而言,不少人常常將方法區稱爲咱們上圖中所描述的永久代,實際上二者並不等價,由於這僅僅是HotSpot的設計團隊選擇利用永久代來實現方法區而言。同時對於其餘虛擬機好比IBM J9中是不存在永久代的概念的。 其實,移除永久代的工做從JDK1.7就開始了。JDK1.7中,存儲在永久代的部分數據就已經轉移到了Java Heap或者是 Native Heap。但永久代仍存在於JDK1.7中,並沒徹底移除,譬如符號引用(Symbols)轉移到了native heap;字面量(interned strings)轉移到了java heap;類的靜態變量(class statics)轉移到了java heap。而在JDK1.8以後永久代概念也已經再也不存在取而代之的是元空間metaspace。java

常量池實際上是方法區中的一部分,由於這裏比較重要,因此咱們拿出來單獨看一下。注意咱們這裏所說的運行時的常量池並僅僅是指Class文件中的常量池,由於JVM可能會進行即時編譯進行優化,在運行時將部分常量載入到常量池中。面試

程序計數器

JVM中的程序計數器和計算機組成原理中提到的程序計數器PC概念相似,是線程私有的,用來記錄當前執行的字節碼位置。仍是稍微解釋一下吧,CPU的佔有時間是以分片的形式分配給給每一個不一樣線程的,從操做系統的角度來說,在不一樣線程之間切換的時候就是依賴程序計數器來記錄上一次線程所執行到具體的代碼的行數,在JVM中就是字節碼。算法

Java虛擬機棧

與程序計數器同樣,Java虛擬機棧也是線程私有的,用通俗的話將它就是咱們經常據說到堆棧中的那個「棧內存」。虛擬機棧描述的是Java方法執行的內存模型:每一個方法在執行的同時都會建立一個棧幀(Stack Frame)用於存儲局部變量表(局部變量表須要的內存在編譯期間就肯定了因此在方法運行期間不會改變大小),操做數棧,動態連接,方法出口等信息。每個方法從調用至出棧的過程,就對應着棧幀在虛擬機中從入棧到出棧的過程。p.s: 關於棧幀這裏咱們之後講虛擬機字節碼執行引擎的時候再來仔細分析。數組

本地方法棧

本地方法棧和Java虛擬機棧相似,只不過是爲JVM執行Native方法服務,這裏就不解釋了。性能優化

堆是用來存放對象的內存空間, 幾乎全部的對象都存儲在堆中。架構

堆的特色: 線程共享 整個Java虛擬機只有一個堆,全部的線程都訪問同一個堆。而程序計數器、Java虛擬機棧、本地方法棧都是一個線程對應一個的。 在虛擬機啓動時建立 垃圾回收的主要場所。 能夠進一步細分爲:新生代、老年代。 新生代又可被分爲:Eden、From Survior、To Survior。 不一樣的區域存放具備不一樣生命週期的對象。這樣能夠根據不一樣的區域使用不一樣的垃圾回收算法,從而更具備針對性,從而更高效。 堆的大小既能夠固定也能夠擴展,但主流的虛擬機堆的大小是可擴展的,所以當線程請求分配內存,但堆已滿,且內存已滿沒法再擴展時,就拋出OutOfMemoryError。併發

總結

Java虛擬機的內存模型中一共有兩個「棧」,分別是:Java虛擬機棧和本地方法棧。 兩個「棧」的功能相似,都是方法運行過程的內存模型。而且兩個「棧」內部構造相同,都是線程私有。 只不過Java虛擬機棧描述的是Java方法運行過程的內存模型,而本地方法棧是描述Java本地方法運行過程的內存模型。 Java虛擬機的內存模型中一共有兩個「堆」,一個是本來的堆,一個是方法區。方法區本質上是屬於堆的一個邏輯部分。堆中存放對象,方法區中存放類信息、常量、靜態變量、即時編譯器編譯的代碼。 堆是Java虛擬機中最大的一塊內存區域,也是垃圾收集器主要的工做區域。 程序計數器、Java虛擬機棧、本地方法棧是線程私有的,即每一個線程都擁有各自的程序計數器、Java虛擬機棧、本地方法區。而且他們的生命週期和所屬的線程同樣。 而堆、方法區是線程共享的,在Java虛擬機中只有一個堆、一個方法棧。並在JVM啓動的時候就建立,JVM中止才銷燬。eclipse

JVM面試問題

JVM 分爲堆區和棧區,還有方法區,初始化的對象放在堆裏面,引用放在棧裏面,class類信息常量池(static常量和static變量)等放在方法區new:方法區:主要是存儲類信息,常量池(static常量和static變量),編譯後的代碼(字節碼)等數據堆:初始化的對象,成員變量 (那種非static的變量),全部的對象實例和數組都要在堆上分配棧:棧的結構是棧幀組成的,調用一個方法就壓入一幀,幀上面存儲局部變量表,操做數棧,方法出口等信息,局部變量表存放的是8大基礎類型加上一個應用類型,因此仍是一個指向地址的指針本地方法棧:主要爲Native方法服務程序計數器:記錄當前線程執行的行號分佈式

二、GC的兩種斷定方法

引用計數法:指的是若是某個地方引用了這個對象就+1,若是失效了就-1,當爲0就會回收可是JVM沒有用這種方式,由於沒法斷定相互循環引用(A引用B,B引用A)的狀況微服務

引用鏈法: 經過一種GC ROOT的對象(方法區中靜態變量引用的對象等-static變量)來判斷,若是有一條鏈可以到達GC ROOT就說明,不能到達GC ROOT就說明能夠回收

三、 GC的三種收集方法:標記清除、標記整理、複製算法的原理與特色,分別用在什麼地方,若是讓你優化收集方法,有什麼思路?

先標記,標記完畢以後再清除,效率不高,會產生碎片

複製算法:分爲8:1的Eden區和survivor區,就是上面談到的YGC

標記整理:標記完畢以後,讓全部存活的對象向一端移動

四、幾種經常使用的內存調試工具:jmap、jstack、jconsole、jhat

jstack能夠看當前棧的狀況,jmap查看內存,jhat 進行dump堆的信息 mat(eclipse的也要了解一下)

五、類加載的幾個過程

加載、驗證、準備、解析、初始化。而後是使用和卸載了經過全限定名來加載生成class對象到內存中,而後進行驗證這個class文件,包括文件格式校驗、元數據驗證,字節碼校驗等。準備是對這個對象分配內存。解析是將符號引用轉化爲直接引用(指針引用),初始化就是開始執行構造器的代碼

六、雙親委派模型:Bootstrap ClassLoader、Extension ClassLoader、ApplicationClassLoader。

Bootstrap ClassLoader:啓動類加載器,負責將 Java_Home /lib/ext或者由系統變量 java.ext.dir指定位置中的類庫加載到內存中。ApplicationClassLoader:它負責將系統類路徑(CLASSPATH)中指定的類庫加載到內存中。開發者能夠直接使用系統類加載器雙親委派模型是某個特定的類加載器在接到加載類的請求時,首先將加載任務委託給父類加載器,依次遞歸,若是父類加載器能夠完成類加載任務,就成功返回;只有父類加載器沒法完成此加載任務時,才本身去加載。-----例如類java.lang.Object,它存在在rt.jar中,不管哪個類加載器要加載這個類,最終都是委派給處於模型最頂端的Bootstrap ClassLoader進行加載,所以Object類在程序的各類類加載器環境中都是同一個類。相反,若是沒有雙親委派模型而是由各個類加載器自行加載的話,若是用戶編寫了一個java.lang.Object的同名類並放在ClassPath中,那系統中將會出現多個不一樣的Object類,程序將混亂

七、SafePoint是什麼

好比GC的時候必需要等到Java線程都進入到safepoint的時候VMThread才能開始執行GC, 循環的末尾 (防止大循環的時候一直不進入safepoint,而其餘線程在等待它進入safepoint) 方法返回前 調用方法的call以後 拋出異常的位置

八、如和判斷一個對象是否存活?(或者GC對象的斷定方法)

判斷一個對象是否存活有兩種方法:

引用計數法 所謂引用計數法就是給每個對象設置一個引用計數器,每當有一個地方引用這個對象時,就將計數器加一,引用失效時,計數器就減一。當一個對象的引用計數器爲零時,說明此對象沒有被引用,也就是「死對象」,將會被垃圾回收. 引用計數法有一個缺陷就是沒法解決循環引用問題,也就是說當對象A引用對象B,對象B又引用者對象A,那麼此時A,B對象的引用計數器都不爲零,也就形成沒法完成垃圾回收,因此主流的虛擬機都沒有采用這種算法。

2.可達性算法(引用鏈法) 該算法的思想是:從一個被稱爲GC Roots的對象開始向下搜索,若是一個對象到GC Roots沒有任何引用鏈相連時,則說明此對象不可用。 在java中能夠做爲GC Roots的對象有如下幾種:

虛擬機棧中引用的對象

方法區類靜態屬性引用的對象

方法區常量池引用的對象

本地方法棧JNI引用的對象

雖然這些算法能夠斷定一個對象是否能被回收,可是當知足上述條件時,一個對象比不必定會被回收。當一個對象不可達GC Root時,這個對象並

不會立馬被回收,而是出於一個死緩的階段,若要被真正的回收須要經歷兩次標記

若是對象在可達性分析中沒有與GC

Root的引用鏈,那麼此時就會被第一次標記而且進行一次篩選,篩選的條件是是否有必要執行finalize()方法。當對象沒有覆蓋finalize()方法或者已被虛擬機調用過,那麼就認爲是不必的。

若是該對象有必要執行finalize()方法,那麼這個對象將會放在一個稱爲F-Queue的對隊列中,虛擬機會觸發一個Finalize()線程去執行,此線程是低優先級的,而且虛擬機不會承諾一直等待它運行完,這是由於若是finalize()執行緩慢或者發生了死鎖,那麼就會形成F-Queue隊列一直等待,形成了內存回收系統的崩潰。GC對處於F-Queue中的對象進行第二次被標記,這時,該對象將被移除」即將回收」集合,等待回收。

推薦一個交流學習羣:705127209 裏面會分享一些資深架構師錄製的視頻錄像:有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化這些成爲架構師必備的知識體系。還能領取免費的學習資源,目前受益良多:

相關文章
相關標籤/搜索