Java虛擬機篇(面試)

面試重點:Java虛擬機篇

1、Java引用的四種狀態:

強引用java

  用的最廣。咱們平時寫代碼時,new一個Object存放在堆內存,而後用一個引用指向它,這就是強引用。面試

  若是一個對象具備強引用,那垃圾回收器毫不會回收它。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具備強引用的對象來解決內存不足的問題。算法

軟引用緩存

  若是一個對象只具備軟引用,則內存空間足夠時,垃圾回收器就不會回收它;若是內存空間不足了,就會回收這些對象的內存。(備註:若是內存不足,隨時有可能被回收。)服務器

  只要垃圾回收器沒有回收它,該對象就能夠被程序使用。軟引用可用來實現內存敏感的高速緩存。數據結構

弱引用多線程

  弱引用與軟引用的區別在於:只具備弱引用的對象擁有更短暫的生命週期。併發

  每次執行GC的時候,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。不過,因爲垃圾回收器是一個優先級很低的線程,所以不必定會很快發現那些只具備弱引用的對象。jvm

虛引用性能

  「虛引用」顧名思義,就是形同虛設,與其餘幾種引用都不一樣,虛引用並不會決定對象的生命週期。若是一個對象僅持有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收器回收。

  虛引用主要用來跟蹤對象被垃圾回收器回收的活動。

2、Java中的內存劃分:

Java程序在運行時,須要在內存中的分配空間。爲了提升運算效率,就對數據進行了不一樣空間的劃分,由於每一片區域都有特定的處理數據方式和內存管理方式。

面試重點:Java虛擬機篇

一、程序計數器:(線程私有)

  • 每一個線程擁有一個程序計數器,在線程建立時建立,

  • 指向下一條指令的地址

  • 執行本地方法時,其值爲undefined

二、虛擬機棧:(線程私有)

每一個方法被調用的時候都會建立一個棧幀,用於存儲局部變量表、操做棧、動態連接、方法出口等信息。局部變量表存放的是:編譯期可知的基本數據類型、對象引用類型。

每一個方法被調用直到執行完成的過程,就對應着一個棧幀在虛擬機中從入棧到出棧的過程。

在Java虛擬機規範中,對這個區域規定了兩種異常狀況:

  (1)若是線程請求的棧深度太深,超出了虛擬機所容許的深度,就會出現StackOverFlowError(好比無限遞歸。由於每一層棧幀都佔用必定空間,而 Xss 規定了棧的最大空間,超出這個值就會報錯)

  (2)虛擬機棧能夠動態擴展,若是擴展到沒法申請足夠的內存空間,會出現OOM

三、本地方法棧:

(1)本地方法棧與java虛擬機棧做用很是相似,其區別是:java虛擬機棧是爲虛擬機執行java方法服務的,而本地方法棧則爲虛擬機執使用到的Native方法服務。

(2)Java虛擬機沒有對本地方法棧的使用和數據結構作強制規定,Sun HotSpot虛擬機就把java虛擬機棧和本地方法棧合二爲一。

(3)本地方法棧也會拋出StackOverFlowError和OutOfMemoryError。

四、堆:即堆內存(線程共享)

(1)堆是java虛擬機所管理的內存區域中最大的一塊,java堆是被全部線程共享的內存區域,在java虛擬機啓動時建立,堆內存的惟一目的就是存放對象實例幾乎全部的對象實例都在堆內存分配。

(2)堆是GC管理的主要區域,從垃圾回收的角度看,因爲如今的垃圾收集器都是採用的分代收集算法,所以java堆還能夠初步細分爲新生代和老年代。

(3)Java虛擬機規定,堆能夠處於物理上不連續的內存空間中,只要邏輯上連續的便可。在實現上既能夠是固定的,也能夠是可動態擴展的。若是在堆內存沒有完成實例分配,而且堆大小也沒法擴展,就會拋出OutOfMemoryError異常。

五、方法區:(線程共享)

(1)用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。

(2)Sun HotSpot虛擬機把方法區叫作永久代(Permanent Generation),方法區中最終要的部分是運行時常量池。

3、Java對象在內存中的狀態:

可達的/可觸及的:

  Java對象被建立後,若是被一個或多個變量引用,那就是可達的。即從根節點能夠觸及到這個對象。

  其實就是從根節點掃描,只要這個對象在引用鏈中,那就是可觸及的。

可恢復的:

  Java對象再也不被任何變量引用就進入了可恢復狀態。

  在回收該對象以前,該對象的finalize()方法進行資源清理。若是在finalize()方法中從新讓變量引用該對象,則該對象再次變爲可達狀態,不然該對象進入不可達狀態

不可達的:

  Java對象不被任何變量引用,且系統在調用對象的finalize()方法後依然沒有使該對象變成可達狀態(該對象依然沒有被變量引用),那麼該對象將變成不可達狀態。

  當Java對象處於不可達狀態時,系統纔會真正回收該對象所佔有的資源。

4、判斷對象死亡的兩種經常使用算法:

一、引用計數算法:

給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任什麼時候刻計數器爲0的對象就是不可能再被使用的。

可是,主流的java虛擬機並無選用引用計數算法來管理內存,其中最主要的緣由是:它很難解決對象之間相互循環引用的問題。

二、根搜索算法:(jvm採用的算法)

設立若干種根對象,當任何一個根對象(GC Root)到某一個對象均不可達時,則認爲這個對象是能夠被回收的。

5、垃圾回收算法

一、標記-清除算法:

標記階段:先經過根節點,標記全部從根節點開始的可達對象。所以,未被標記的對象就是未被引用的垃圾對象;

清除階段:清除全部未被標記的對象。

二、複製算法:(新生代的GC)

  將原有的內存空間分爲兩塊,每次只使用其中一塊,在垃圾回收時,將正在使用的內存中的存活對象複製到未使用的內存塊中,而後清除正在使用的內存塊中的全部對象。

三、標記-整理算法:(老年代的GC)

標記階段:先經過根節點,標記全部從根節點開始的可達對象。所以,未被標記的對象就是未被引用的垃圾對象

整理階段:將將全部的存活對象壓縮到內存的一端;以後,清理邊界外全部的空間

四、分代收集算法:

存活率低:少許對象存活,適合複製算法:在新生代中,每次GC時都發現有大批對象死去,只有少許存活(新生代中98%的對象都是「朝生夕死」),那就選用複製算法,只須要付出少許存活對象的複製成本就能夠完成GC。

存活率高:大量對象存活,適合用標記-清理/標記-整理:在老年代中,由於對象存活率高、沒有額外空間對他進行分配擔保,就必須使用「標記-清理」/「標記-整理」算法進行GC。

6、垃圾收集器

一、Serial收集器:(串行收集器)

這個收集器是一個單線程的收集器,但它的單線程的意義並不只僅說明它只會使用一個CPU或一條收集線程去完成垃圾收集工做,更重要的是在它進行垃圾收集時,必須暫停其餘全部的工做線程(Stop-The-World:將用戶正常工做的線程所有暫停掉),直到它收集結束。

二、ParNew收集器:Serial收集器的多線程版本(使用多條線程進行GC)

  ParNew收集器是Serial收集器的多線程版本。

  它是運行在server模式下的首選新生代收集器,除了Serial收集器外,目前只有它能與CMS收集器配合工做。CMS收集器是一個被認爲具備劃時代意義的併發收集器,所以若是有一個垃圾收集器能和它一塊兒搭配使用讓其更加完美,那這個收集器必然也是一個不可或缺的部分了。

三、ParNew Scanvenge收集器

  相似ParNew,但更加關注吞吐量。目標是:達到一個可控制吞吐量的收集器。

停頓時間和吞吐量不可能同時調優。咱們一方買但願停頓時間少,另一方面但願吞吐量高,其實這是矛盾的。由於:在GC的時候,垃圾回收的工做總量是不變的,若是將停頓時間減小,那頻率就會提升;既然頻率提升了,說明就會頻繁的進行GC,那吞吐量就會減小,性能就會下降。

吞吐量:CPU用於用戶代碼的時間/CPU總消耗時間的比值,即=運行用戶代碼的時間/(運行用戶代碼時間+垃圾收集時間)。好比,虛擬機總共運行了100分鐘,其中垃圾收集花掉1分鐘,那吞吐量就是99%。

四、G1收集器:

  是當今收集器發展的最前言成果之一,直到jdk1.7,sun公司才認爲它達到了足夠成熟的商用程度。

五、CMS收集器:(老年代收集器)

CMS收集器(Concurrent Mark Sweep:併發標記清除)是一種以獲取最短回收停頓時間爲目標的收集器。適合應用在互聯網站或者B/S系統的服務器上,這類應用尤爲重視服務器的響應速度,但願系統停頓時間最短。

7、Java堆內存劃分:

Java 中的堆是 JVM 所管理的最大的一塊內存空間,主要用於存放各類類的實例對象。

在 Java 中,堆被劃分紅兩個不一樣的區域:年輕代 ( Young )、老年代 ( Tenured)。年輕代 ( Young ) 又被劃分爲三個區域:Eden、From Survivor、To Survivor。 這樣劃分的目的是爲了使 JVM 可以更好的管理堆內存中的對象,包括內存的分配以及回收。

1.年輕代

年輕代用來存放新近建立的對象,尺寸隨堆大小的增大和減少而相應的變化,默認值是保持爲堆大小的1/15,能夠經過 -Xmn 參數設置年輕代爲固定大小,也能夠經過 -XX:NewRatio 來設置年輕代與年老代的大小比例,年青代的特色是對象更新速度快,在短期內產生大量的「死亡對象」。

年輕代的特色是產生大量的死亡對象,而且要是產生連續可用的空間, 因此使用複製清除算法和並行收集器進行垃圾回收.對年輕代的垃圾回收稱做初級回收 (minor gc)。

2.老年代

Full GC 是發生在老年代的垃圾收集動做,所採用的是標記-清除算法。

現實的生活中,老年代的人一般會比新生代的人 「早死」。堆內存中的老年代(Old)不一樣於這個,老年代裏面的對象幾乎個個都是在 Survivor 區域中熬過來的,它們是不會那麼容易就 「死掉」 了的。所以,Full GC 發生的次數不會有 Minor GC 那麼頻繁,而且作一次 Full GC 要比進行一次 Minor GC 的時間更長。 另外,標記-清除算法收集垃圾的時候會產生許多的內存碎片 ( 即不連續的內存空間 ),此後須要爲較大的對象分配內存空間時,若沒法找到足夠的連續的內存空間,就會提早觸發一次 GC 的收集動做。

3.永久代

永久代是Hotspot虛擬機特有的概念,是方法區的一種實現,別的JVM都沒有這個東西。在Java 8中,永久代被完全移除,取而代之的是另外一塊與堆不相連的本地內存——元空間。

永久代或者「Perm Gen」包含了JVM須要的應用元數據,這些元數據描述了在應用裏使用的類和方法。注意,永久代不是Java堆內存的一部分。永久代存放JVM運行時使用的類。永久代一樣包含了Java SE庫的類和方法。永久代的對象在full GC時進行垃圾收集。

8、類加載機制:

面試重點:Java虛擬機篇

虛擬機把描述類的數據從Class文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終造成能夠被虛擬機直接使用的Java類型,這就是虛擬機的類加載機制。

對應常見筆試題

注意:子類初始化問題:知足主動調用,即父類訪問子類中的靜態變量、方法,子類纔會初始化;不然僅父類初始化。

面試重點:Java虛擬機篇

面試重點:Java虛擬機篇

面試重點:Java虛擬機篇

面試重點:Java虛擬機篇

注意:訪問類或接口的靜態變量(特例:若是是用static final修飾的常量,那就不會對類進行顯式初始化。static final 修改的變量則會作顯式初始化)

面試重點:Java虛擬機篇

面試重點:Java虛擬機篇

面試重點:Java虛擬機篇

上面的運行效果顯示,因爲c是final static修飾的靜態常量,因此根本就沒有調用靜態代碼塊裏面的內容,也就是說,沒有對這個類進行顯式初始化

相關文章
相關標籤/搜索