java之jvm

1.JVM內存模型java

線程獨佔:棧,本地方法棧,程序計數器
線程共享:堆,方法區
linux

回答以上問題是需回答兩個要點:
1. 各部分功能
2. 是不是線程共享程序員

2.JMM與內存可見性
JMM是定義程序中變量的訪問規則,線程對於變量的操做只能在本身的工做內存中進行,而不能直接對主內存操做.因爲指令重排序,讀寫的順序會被打亂,所以JMM須要提供原子性,可見性,有序性保證.算法

3.類加載與卸載編程

加載機制-雙親委派模式bootstrap

雙親委派模式,即加載器加載類時先把請求委託給本身的父類加載器執行,直到頂層的啓動類加載器.父類加載器可以完成加載則成功返回,不能則子類加載器才本身嘗試加載.
優勢:api

避免類的重複加載
避免Java的核心API被篡改緩存


分代回收
分代回收基於兩個事實:大部分對象很快就不使用了,還有一部分不會當即無用,但也不會持續很長時間.安全

年輕代->標記-複製
老年代->標記-清除網絡

回收算法
1.CMS算法
1.7前主流垃圾回收算法,爲標記-清楚算法,優勢是併發收集,停頓小.

2.G1算法
1.9後默認的垃圾回收算法,特色保持高回收率的同時減小停頓.採用每次只清理一部分,而不是清理所有的增量式清理,以保證停頓時間不會過長

3.ZGC
1.11中提供的高效垃圾回收算法,針對大堆內存設計,能夠處理TB級別的堆,能夠作到10ms如下的回收停頓時間.

考察點
深刻理解JVM內存模型
瞭解類加載機制
瞭解內存可見性
瞭解經常使用的GC算法實現和適用場景
能根據業務場景選擇合適JVM參數和GC算法

加分項
編譯器優化
問題排查經驗與思路
JVM調優經驗和調優思路
瞭解最新的技術趨勢(ZGC和Graalvm)

真題彙總
1.簡單描述一下JVM的內存模型
jvm內存模型:方法區、堆、程序計數器 、本地方法棧、虛擬機棧
這裏針對方法區、棧、堆、程序計數器作一個說明
這裏咱們來講個流程:
程序(非多線程)開始運行的時候,在系統中會自動分配一個棧,這個時候程序計數器就開始起到做用了,它會指示jvm對編譯以後的字節碼的執行方向,同時在執行一個方法的時候就會在棧中分配一個屬於方法一個棧幀,方法的局部變量都會存放在這個棧幀中,其生命週期隨着方法的結束而釋放,這裏強調一點的是先進後出的邏輯,堆中的數據當沒有對象引用的時候就成了孤立數據,此時就會被GC垃圾回收器對其進行內存釋放。
方法區包含了常量池:存放類信息、常量、靜態變量、即時編譯器編譯後的代碼等。其中靜態成員變量在類裝載的時候就進行了建立,在整個程序結束時按序銷燬。

靜態成員變量在類裝載的時候就進行了建立,在整個程序結束時按序銷燬。
實例變量在類實例化對象時候建立,在對象銷燬的時候銷燬。
局部變量在局部範圍使用時建立,跳出局部範圍銷燬。

這裏咱們說個題外話,在術語中常常會聽到編譯期和運行期
編譯期:就是將源碼編譯成二進制的.class字節碼文件,並將文件放到了磁盤中,編譯期至關於只是作了一個翻譯的過程
運行期:這塊就是咱們java解釋器將二進制.class字節碼解釋成程序能識別的程序(將磁盤中的代碼放到內存中就是類加載過程)


2.何時會觸發FullGC?

觸發MinorGC(Young GC)
虛擬機在進行minorGC以前會判斷老年代最大的可用連續空間是否大於新生代的全部對象總空間

一、若是大於的話,直接執行minorGC

二、若是小於,判斷是否開啓HandlerPromotionFailure,沒有開啓直接FullGC

三、若是開啓了HanlerPromotionFailure, JVM會判斷老年代的最大連續內存空間是否大於歷次晉升的大小,若是小於直接執行FullGC

四、若是大於的話,執行minorGC

觸發FullGC
老年代空間不足
若是建立一個大對象,Eden區域當中放不下這個大對象,會直接保存在老年代當中,若是老年代空間也不足,就會觸發Full GC。爲了不這種狀況,最好就是不要建立太大的對象。

持久代空間不足
若是有持久代空間的話,系統當中須要加載的類,調用的方法不少,同時持久代當中沒有足夠的空間,就出觸發一次Full GC

YGC出現promotion failure
promotion failure發生在Young GC, 若是Survivor區當中存活對象的年齡達到了設定值,會就將Survivor區當中的對象拷貝到老年代,若是老年代的空間不足,就會發生promotion failure, 接下去就會發生Full GC.

統計YGC發生時晉升到老年代的平均總大小大於老年代的空閒空間
在發生YGC是會判斷,是否安全,這裏的安全指的是,當前老年代空間能夠容納YGC晉升的對象的平均大小,若是不安全,就不會執行YGC,轉而執行Full GC。

顯示調用System.gc


3.Java類加載器有幾種,關係怎樣的?
JAVA類加載器包括幾種?
引導類加載器 bootstrap class loader
  啓動類加載器主要加載的是JVM自身須要的類,這個類加載使用C++語言實現的,是虛擬機自身的一部分,它負責將 /lib路徑下的核心類庫或-Xbootclasspath參數指定的路徑下的jar包加載到內存中,注意必因爲虛擬機是按照文件名識別加載jar包的,如rt.jar,若是文件名不被虛擬機識別,即便把jar包丟到lib目錄下也是沒有做用的(出於安全考慮,Bootstrap啓動類加載器只加載包名爲java、javax、sun等開頭的類

擴展類加載器 extensions class loader
  它負責加載JAVA_HOME/lib/ext目錄下或者由系統變量-Djava.ext.dir指定位路徑中的類庫,開發者能夠直接使用標準擴展類加載器。

應用程序類加載器 application class loader
  應用程序加載器是指 Sun公司實現的sun.misc.Launcher$AppClassLoader。它負責加載系統類路徑java -classpath或-D java.class.path 指定路徑下的類庫,也就是咱們常常用到的classpath路徑,開發者能夠直接使用系統類加載器,通常狀況下該類加載是程序中默認的類加載器,經過ClassLoader#getSystemClassLoader()方法能夠獲取到該類加載器。

自定義類加載器 java.lang.classloder
  就是自定義啦,經過繼承java.lang.ClassLoader類的方式

類加載器之間的關係
  啓動類加載器,由C++實現,沒有父類。
  拓展類加載器(ExtClassLoader),由Java語言實現,父類加載器爲null
  系統類加載器(AppClassLoader),由Java語言實現,父類加載器爲ExtClassLoader
  自定義類加載器,父類加載器確定爲AppClassLoader。

如何自定義一個類加載器?
  經過繼承ClassLoad定義一個類加載器。

應用場景
  如Tomcat容器,每一個WebApp有本身的ClassLoader,加載每一個WebApp的ClassPath路徑上的類,一旦遇到Tomcat自帶的Jar包就委託給CommonClassLoader加載。同包的隔離。另外成熟的開源框架,都有本身的classloade。


4.雙親委派機制的加載流程是怎樣的,有什麼好處?
雙親委派機制
  請注意雙親委派模式中的父子關係並不是一般所說的類繼承關係。
  其工做原理的是:若是一個類加載器收到了類加載請求,它並不會本身先去加載,而是把這個請求委託給父類的加載器去執行,若是父類加載器還存在其父類加載器,則進一步向上委託,依次遞歸,請求最終將到達頂層的啓動類加載器,若是父類加載器能夠完成類加載任務,就成功返回,假若父類加載器沒法完成此加載任務,子加載器纔會嘗試本身去加載,這就是雙親委派模式,即每一個兒子都很懶,每次有活就丟給父親去幹,直到父親說這件事我也幹不了時,兒子本身想辦法去完成。

雙親委派機制做用
  經過這種層級關能夠避免類的重複加載,當父親已經加載了該類時,就沒有必要子ClassLoader再加載一次。其次是考慮到安全因素,java核心api中定義類型不會被隨意替換,假設經過網絡傳遞一個名爲java.lang.Integer的類,經過雙親委託模式傳遞到啓動類加載器,而啓動類加載器在覈心Java API發現這個名字的類,發現該類已被加載,並不會從新加載網絡傳遞的過來的java.lang.Integer,而直接返回已加載過的Integer.class,這樣即可以防止核心API庫被隨意篡改

雙親委派模式的工做原理的是;若是一個類加載器收到了類加載請求,它並不會本身先去加載,而是把這個請求委託給父類的加載器去執行,若是父類加載器還存在其父類加載器,則進一步向上委託,依次遞歸,請求最終將到達頂層的啓動類加載器,若是父類加載器能夠完成類加載任務,就成功返回,假若父類加載器沒法完成此加載任務,子加載器纔會嘗試本身去加載,這就是雙親委派模式,即每一個兒子都不肯意幹活,每次有活就丟給父親去幹,直到父親說這件事我也幹不了時,兒子本身想辦法去完成,這不就是傳說中的雙親委派模式.那麼這種模式有什麼做用呢?

雙親委派模式優點

採用雙親委派模式的是好處是Java類隨着它的類加載器一塊兒具有了一種帶有優先級的層次關係,經過這種層級關能夠避免類的重複加載,當父親已經加載了該類時,就沒有必要子ClassLoader再加載一次。其次是考慮到安全因素,java核心api中定義類型不會被隨意替換,假設經過網絡傳遞一個名爲java.lang.Integer的類,經過雙親委託模式傳遞到啓動類加載器,而啓動類加載器在覈心Java API發現這個名字的類,發現該類已被加載,並不會從新加載網絡傳遞的過來的java.lang.Integer,而直接返回已加載過的Integer.class,這樣即可以防止核心API庫被隨意篡改。


5.1.8爲何用Metaspace替換掉PermGen?Meatspace保存在哪?
1. PermGen空間的情形
這部份內存空間徹底被移除 PermSize和MaxPermSize JVM參數被忽略掉,若是存在的話啓動時會發出警告。

2. Metaspace空間分配模型
1) 大部分類的metadata如今分配到本地內存。
2) 用來描述類的metadata的類已被刪除。

3. Metaspace容量
1) 默認狀況下,類的metadata的分配是受本機可用內存的限制(固然容量要取決於你使用的32位仍是64位的JVM以及操做系統可用的虛擬內存)。
2) 一個新的參數可用了(MaxMetaspaceSize),容許你限制本地用於類的metadata的內存容量。若是你沒有指定這個參數,Metaspace將會在運行時根據應用的需求自動調整大小。

4. Metaspace垃圾回收
1) 一旦類的metadata使用達到「MaxMetaspaceSize」,死的類和類加載器的垃圾回收器會被觸發。
2) 適當的Metaspace的監控&調優將明顯地被要求以限制此類垃圾回收的頻率或延遲。過分的Metaspace垃圾收集多是您的應用程序中類,類加載器內存泄漏或容量不夠的症狀。

5. Java 堆空間的影響
一些其餘數據已遷移到Java堆空間。這意味着下一個將來的JDK 8升級您可能會看到Java堆空間的增長。

6. Metaspace監控
1) Metaspace的使用狀況能夠從HotSpot 1.8的verbose GC log輸出。
2) jstat & jVisualVM


6.編譯器會對指令作哪些優化?(簡答描述編譯器的指令重排)

引言:在Java中看似順序的代碼在JVM中,可能會出現編譯器或者CPU對這些操做指令進行了從新排序;在特定狀況下,指令重排將會給咱們的程序帶來不肯定的結果..

1.  什麼是指令重排?

      在計算機執行指令的順序在通過程序編譯器編譯以後造成的指令序列,通常而言,這個指令序列是會輸出肯定的結果;以確保每一次的執行都有肯定的結果。可是,通常狀況下,CPU和編譯器爲了提高程序執行的效率,會按照必定的規則容許進行指令優化,在某些狀況下,這種優化會帶來一些執行的邏輯問題,主要的緣由是代碼邏輯之間是存在必定的前後順序,在併發執行狀況下,會發生二義性,即按照不一樣的執行邏輯,會獲得不一樣的結果信息。

 2.  數據依賴性

    主要指不一樣的程序指令之間的順序是不容許進行交互的,便可稱這些程序指令之間存在數據依賴性。

    主要的例子以下:

名稱 代碼示例 說明
寫後讀 a = 1;b = a; 寫一個變量以後,再讀這個位置。
寫後寫 a = 1;a = 2; 寫一個變量以後,再寫這個變量。
讀後寫 a = b;b = 1; 讀一個變量以後,再寫這個變量。
 進過度析,發現這裏每組指令中都有寫操做,這個寫操做的位置是不容許變化的,不然將帶來不同的執行結果。
  編譯器將不會對存在數據依賴性的程序指令進行重排,這裏的依賴性僅僅指單線程狀況下的數據依賴性;多線程併發狀況下,此規則將失效。

3.  as-if-serial語義

   無論怎麼重排序(編譯器和處理器爲了提升並行度),(單線程)程序的執行結果不能被改變。編譯器,runtime 和處理器都必須遵照as-if-serial語義。

   分析:  關鍵詞是單線程狀況下,必須遵照;其他的不遵照。

   代碼示例:

double pi = 3.14; //A
double r = 1.0; //B
double area = pi * r * r; //C
 分析代碼:   A->C  B->C;  A,B之間不存在依賴關係; 故在單線程狀況下, A與B的指令順序是能夠重排的,C不容許重排,必須在A和B以後。
結論性的總結:
   as-if-serial語義把單線程程序保護了起來,遵照as-if-serial語義的編譯器,runtime 和處理器共同爲編寫單線程程序的程序員建立了一個幻覺:單線程程序是按程序的順序來執行的。as-if-serial語義使單線程程序員無需擔憂重排序會干擾他們,也無需擔憂內存可見性問題。
   核心點仍是單線程,多線程狀況下不遵照此原則。

4. 在多線程下的指令重排
   首先咱們基於一段代碼的示例來分析,在多線程狀況下,重排是否有不一樣結果信息:

class ReorderExample {
int a = 0;
boolean flag = false;

public void writer() {
a = 1; //1
flag = true; //2
}

Public void reader() {
if (flag) { //3
int i = a * a; //4
……
}
}
}
上述的代碼,在單線程狀況下,執行結果是肯定的, flag=true將被reader的方法體中看到,並正確的設置結果。 可是在多線程狀況下,是否仍是隻有一個肯定的結果呢?
假設有A和B兩個線程同時來執行這個代碼片斷, 兩個可能的執行流程以下:

   可能的流程1, 因爲1和2語句之間沒有數據依賴關係,故二者能夠重排,在兩個線程之間的可能順序以下: 
   可能的流程2:, 在兩個線程之間的語句執行順序以下:
根據happens- before的程序順序規則,上面計算圓的面積的示例代碼存在三個happens- before關係:

A happens- before B;
B happens- before C;
A happens- before C;
這裏的第3個happens- before關係,是根據happens- before的傳遞性推導出來的 
    在程序中,操做3和操做4存在控制依賴關係。當代碼中存在控制依賴性時,會影響指令序列執行的並行度。爲此,編譯器和處理器會採用猜想(Speculation)執行來克服控制相關性對並行度的影響。以處理器的猜想執行爲例,執行線程B的處理器能夠提早讀取並計算a*a,而後把計算結果臨時保存到一個名爲重排序緩衝(reorder buffer ROB)的硬件緩存中。當接下來操做3的條件判斷爲真時,就把該計算結果寫入變量i中。從圖中咱們能夠看出,猜想執行實質上對操做3和4作了重排序。重排序在這裏破壞了多線程程序的語義。

     核心點是:兩個線程之間在執行同一段代碼之間的critical area,在不一樣的線程之間共享變量;因爲執行順序、CPU編譯器對於程序指令的優化等形成了不肯定的執行結果。

5.  指令重排的緣由分析
    主要仍是編譯器以及CPU爲了優化代碼或者執行的效率而執行的優化操做;應用條件是單線程場景下,對於併發多線程場景下,指令重排會產生不肯定的執行效果。

6.  如何防止指令重排
    volatile關鍵字能夠保證變量的可見性,由於對volatile的操做都在Main Memory中,而Main Memory是被全部線程所共享的,這裏的代價就是犧牲了性能,沒法利用寄存器或Cache,由於它們都不是全局的,沒法保證可見性,可能產生髒讀。
    volatile還有一個做用就是局部阻止重排序的發生,對volatile變量的操做指令都不會被重排序,由於若是重排序,又可能產生可見性問題。
    在保證可見性方面,鎖(包括顯式鎖、對象鎖)以及對原子變量的讀寫均可以確保變量的可見性。可是實現方式略有不一樣,例如同步鎖保證獲得鎖時從內存裏從新讀入數據刷新緩存,釋放鎖時將數據寫回內存以保數據可見,而volatile變量乾脆都是讀寫內存。

7.  可見性
    這裏提到的可見性是指前一條程序指令的執行結果,能夠被後一條指令讀到或者看到,稱之爲可見性。反之爲不可見性。這裏主要描述的是在多線程環境下,指令語句之間對於結果信息的讀取即時性。


7.簡單描述一下volatile能夠解決什麼問題?如何作到的?

 

8.簡單描述一下GC的分代回收?

爲何要進行分代回收?
JVM使用分代回收測試,是由於:不一樣的對象,生命週期是不同的。所以不一樣生命週期的對象採用不一樣的收集方式。
能夠提升垃圾回收的效率。
Java程序運行過程當中,會產生大量的對象,其中有些對象是與業務相關的。好比Http請求的Session對象,線程,Socket
鏈接等。可是還有一些對象,主要是程序運行過程當中生成的臨時變量(好比方法中的局部變量),這些對象生命週期會比較短,
好比:String對象,因爲其不變類的特性,系統會產生大量的這些對象,有些對象甚至只用一次便可回收。

試想,在不進行對象存活時間區分的狀況下,每次垃圾回收都是對整個堆空間進行回收,花費時間相對會
長,同時,由於每次回收都須要遍歷全部存活對象,但實際上,對於生命週期長的對象而言,這種遍歷是沒有
效果的,由於可能進行了不少次遍歷,可是他們依舊存在。所以,分代垃圾回收採用分而治之的思想,進行代的劃
分,把不一樣生命週期的對象放在不一樣代上,不一樣代上採用最適合它的垃圾回收方式進行回收。

如何分代


以下圖所示
虛擬機中的共劃分爲三個代:年輕代(Young Generation)、年老點(Old Generation)和持久代
(Permanent Generation)。其中持久代主要存放的是Java類的類信息,與垃圾收集要收集的Java對象關係
不大。年輕代和年老代的劃分是對垃圾收集影響比較大的。

年輕代
全部新生成的對象首先都是放在年輕代的。年輕代的目標就是儘量快速的收集掉那些生命週期短的對象。
年輕代分三個區。一個Eden區,兩個Survivor區(通常而言)。大部分對象在Eden區中生成。當Eden區滿時,還
存活的對象將被複制到Survivor區(兩個中的一個),當這個Survivor區滿時,此區的存活對象將被複制到另外
一個Survivor區,當這個Survivor去也滿了的時候,從第一個Survivor區複製過來的而且此時還存活的對象,將
被複制「年老區(Tenured)」。須要注意,Survivor的兩個區是對稱的,沒前後關係,因此同一個區中可能同時
存在從Eden複製過來 對象,和從前一個Survivor複製過來的對象,而複製到年老區的只有從第一個Survivor去
過來的對象。並且,Survivor區總有一個是空的。同時,根據程序須要,Survivor區是能夠配置爲多個的(多於
兩個),這樣能夠增長對象在年輕代中的存在時間,減小被放到年老代的可能。

老年代
在年輕代中經歷了N次垃圾回收後仍然存活的對象,就會被放到年老代中。所以,能夠認爲年老代中存放的
都是一些生命週期較長的對象。

持久代
用於存放靜態文件,現在Java類、方法等。持久代對垃圾回收沒有顯著影響,可是有些應用可能動態生成或
者調用一些class,例如Hibernate等,在這種時候須要設置一個比較大的持久代空間來存放這些運行過程當中新
增的類。持久代大小經過-XX:MaxPermSize=進行設置。

什麼狀況下觸發垃圾回收
因爲對象進行了分代處理,所以垃圾回收區域、時間也不同。GC有兩種類型:Scavenge GC和Full GC。

Scavenge GC
通常狀況下,當新對象生成,而且在Eden申請空間失敗時,就會觸發Scavenge GC,對Eden區域進行GC,
清除非存活對象,而且把尚且存活的對象移動到Survivor區。而後整理Survivor的兩個區。這種方式的GC是對
年輕代的Eden區進行,不會影響到年老代。由於大部分對象都是從Eden區開始的,同時Eden區不會分配的很
大,因此Eden區的GC會頻繁進行。於是,通常在這裏須要使用速度快、效率高的算法,使Eden去能儘快空閒
出來。

Full GC
對整個堆進行整理,包括Young、Tenured和Perm。Full GC由於須要對整個對進行回收,因此比Scavenge
GC要慢,所以應該儘量減小Full GC的次數。在對JVM調優的過程當中,很大一部分工做就是對於FullGC的調
節。有以下緣由可能致使Full GC:
· 年老代(Tenured)被寫滿
· 持久代(Perm)被寫滿
· System.gc()被顯示調用


9.G1與CMS的區別?
一、CMS收集器

  CMS收集器是一種以獲取最短回收停頓時間爲目標的收集器。基於「標記-清除」算法實現,它的運做過程以下:


1)初始標記  2)併發標記 3)從新標記  4)併發清除


  初始標記、重新標記這兩個步驟仍然須要「stop the world」,初始標記僅僅只是標記一下GC Roots能直接關聯到的對象,熟讀很快,併發標記階段就是進行GC Roots Tracing,而從新標記階段則是爲了修正併發標記期間因用戶程序繼續運做而致使標記產生表動的那一部分對象的標記記錄,這個階段的停頓時間通常會比初始標記階段稍長點,但遠比並發標記的時間短。


優勢:併發收集、低停頓。

缺點:

1)CMS收集器對CPU資源很是敏感。在併發階段,它雖然不會致使用戶線程停頓,可是會由於佔用了一部分線程而致使應用程序變慢,總吞吐量會下降。

2)CMS收集器沒法處理浮動垃圾,可能會出現「Concurrent Mode Failure(併發模式故障)」失敗而致使Full GC產生。

3)CMS是一款「標記--清除」算法實現的收集器,容易出現大量空間碎片。當空間碎片過多,將會給大對象分配帶來很大的麻煩,每每會出現老年代還有很大空間剩餘,可是沒法找到足夠大的連續空間來分配當前對象,不得不提早觸發一次Full GC。

二、G1收集器

G1是一款面向服務端應用的垃圾收集器。G1具有以下特色:

一、並行於併發:G1能充分利用CPU、多核環境下的硬件優點,使用多個CPU(CPU或者CPU核心)來縮短stop-The-World停頓時間。部分其餘收集器本來須要停頓Java線程執行的GC動做,G1收集器仍然能夠經過併發的方式讓java程序繼續執行。

二、分代收集:雖然G1能夠不須要其餘收集器配合就能獨立管理整個GC堆,可是仍是保留了分代的概念。它可以採用不一樣的方式去處理新建立的對象和已經存活了一段時間,熬過屢次GC的舊對象以獲取更好的收集效果。

三、空間整合:與CMS的「標記--清理」算法不一樣,G1從總體來看是基於「標記整理」算法實現的收集器;從局部上來看是基於「複製」算法實現的。

四、可預測的停頓:這是G1相對於CMS的另外一個大優點,下降停頓時間是G1和CMS共同的關注點,但G1除了追求低停頓外,還能創建可預測的停頓時間模型,能讓使用者明確指定在一個長度爲M毫秒的時間片斷內,


五、G1運做步驟:
一、初始標記;二、併發標記;三、最終標記;四、篩選回收

上面幾個步驟的運做過程和CMS有不少類似之處。

初始標記階段僅僅只是標記一下GC Roots能直接關聯到的對象,而且修改TAMS的值,讓下一個階段用戶程序併發運行時,能在正確可用的Region中建立新對象,這一階段須要停頓線程,可是耗時很短,

併發標記階段是從GC Root開始對堆中對象進行可達性分析,找出存活的對象,這階段時耗時較長,但可與用戶程序併發執行。

最終標記階段則是爲了修正在併發標記期間因用戶程序繼續運做而致使標記產生變更的那一部分標記記錄,虛擬機將這段時間對象變化記錄在線程Remenbered Set Logs裏面,最終標記階段須要把Remembered Set Logs的數據合併到Remembered Set Logs裏面,最終標記階段須要把Remembered Set Logs的數據合併到Remembered Set中,這一階段須要停頓線程,可是可並行執行。

最後在篩選回收階段首先對各個Region的回收價值和成本進行排序,根據用戶所指望的GC停頓時間來制定回收計劃。


10.對象引用有哪幾種,有什麼特色?
在jdk1.2以前,java中引用的定義:若是引用類型的數據類型中存儲的數值表明的是一塊內存的起始地址,就稱這塊內存表明一個引用。在jdk1.2以後,java引入了4種對象引用類型,級別由高到低分別是:強引用、軟引用、弱引用和虛引用。

爲何須要不一樣的引用

對於須要長期運行的應用程序來講,若是無用的對象所佔的內存空間沒法及時釋放的話,那麼在一個局部的時間段裏就會造成事實上的內存泄露,若是要及時地釋放內存,在java中最穩妥的方式就是在使用完對象事後 當即執行「object = null」,固然這這是理想狀態。

經過這4種引用類型 強行調用垃圾回收方法 System.gc() 來解決內存泄露的問題,不一樣的引用方式會有不一樣的做用,下面咱們來一一講解。

強引用(StrongReference):直接引用對象,內存不足時也不會回收
軟引用(SoftReference):內存不足時,回收引用相關聯的對象
弱引用 (WeakReference):不管內存是否充足,都回收引用相關聯的對象
虛引用(PhantomReference):任什麼時候候均可以被垃圾回收器回收
強引用
強引用是平常編程中最經常使用的一種引用類型,它的特色是隻要引用存在,就永遠不會調用垃圾回收方法對其進行釋放內存,java虛擬機寧肯出現OutOfMemoryError錯誤中止運行,也會保存內存空間。只用當這個對象的引用被釋放掉,對象纔會被釋放。
正是由於強引用具有這樣的特色,因此咱們的開發原則是儘可能少實例化對象。
強引用是形成java內存泄露的主要緣由之一。

軟引用
軟引用是指非必須引用,在內存足夠時是不會對其進行回收,而在內存不足的時候垃圾回收器會將其回收並釋放內存。java中軟引用對應着SoftReference類,若是想要一個對象被軟引用,只須要將其做爲參數傳入SoftReference類的構造方法中就好了。

軟引用主要用於用戶實現相似緩存的功能,在內存足夠的狀況下直接經過軟引用取值,無需從繁忙的真實來源查詢數據,提高速度;當內存不足時,自動刪除這部分緩存數據,從真實來源查詢這些數據。軟引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是軟引用所引用的對象被垃圾回收器回收,java虛擬機就會把這個軟引用加入到與之關聯的隊列引用中。

弱引用
弱引用相對於軟引用,被弱引用的對象擁有更短的內存時間(生命週期)。垃圾回收器一旦發現了被弱引用的對象,就會回收它的內存,無論當前內存空間是否是足夠。不過,因爲垃圾回收器是一個優先級很低的線程,所以不必定很快發現那些弱引用的對象。對應java中的WeakReference類,使用方法與軟引用基本相同,弱引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是弱引用所引用的對象被垃圾回收器回收,java虛擬機就會把這個軟引用加入到與之關聯的隊列引用中。

弱引用主要應用於監控對象是否被垃圾回收器標記爲即將回收的垃圾,能夠經過弱引用的isEnQueued方法返回對象是否被垃圾回收器標記

虛引用
顧名思義:形同虛設,虛引用並不會決定對象的生命週期,若是一個對象僅有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收器回收。程序也不能經過虛引用訪問被引用的對象。
虛引用主要用來跟蹤垃圾回收器回收的活動,虛引用與弱應用,軟引用的區別在於:虛引用必須和引用隊列(ReferenceQueue)聯合使用,當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在回收對象內存以前,把這個虛引用加入到與之關聯的隊列中。


11.使用過哪些JVM調試工具,主要分析哪些內容?

監視JVM
jps
jstat
jstatd
Jmc

故障排除
jcmd
jinfo
jhat
jmap
jsadebugd
jstack

jps (JavaVirtual Machine Process Status Tool): 虛擬機進程情況工具
命令格式:jps [options] [hostid]
-q : 抑制類名的輸出,JAR文件名和傳遞給main方法的參數,僅生成本地JVM標識符列表。
-m: 顯示傳遞給該main方法的參數。輸出多是null嵌入式JVM。
-l : 顯示應用程序main類的完整包名或應用程序的JAR文件的完整路徑名。
-v : 顯示傳遞給JVM的參數。
-V : 抑制類名的輸出,JAR文件名和傳遞給main方法的參數,僅生成本地JVM標識符的列表。
-Joption : 傳遞option給JVM,其中的選項是optionsJava應用程序啓動器的參考頁面中描述的選項之一。例如,-J-Xms48m將啓動內存設置爲48 MB。
樣例:

jstat(Java Virtual Machine (JVM) statistics):監視Java虛擬機(JVM)統計信息。
命令格式:jstat [ option vmid [interval[s|ms] [count] ]
Interval:間隔時間 count:次數
class:顯示類加載器行爲的統計信息。
compiler:顯示有關Java HotSpot VM即時編譯器行爲的統計信息。
gc:顯示垃圾回收堆行爲的統計信息。
gccapacity:顯示有關世代及其對應空間容量的統計數據。
gccause:顯示有關垃圾回收統計信息(相同-gcutil)的摘要,其中包含最後和當前(適用時)垃圾收集事件的緣由。
gcnew:顯示新一代行爲的統計信息。
gcnewcapacity:顯示有關新一代及其相應空間大小的統計信息。
gcold:顯示舊版本和Metaspace統計信息的統計信息。
gcoldcapacity:顯示有關舊一代大小的統計信息。
gcmetacapacity:顯示有關元空間大小的統計信息。
gcutil:顯示有關垃圾收集統計信息的摘要。
printcompilation:顯示Java HotSpot VM編譯方法統計信息。

jinfo(Configuration Info for Java):生成配置信息。
命令格式:jinfo [ option ] pid
-flag 名稱 : 打印指定命令行標誌的名稱和值。
-flag [+ | - ]名稱 : 啓用或禁用指定的布爾命令行標誌。
-flag name = value : 將指定的命令行標誌設置爲指定的值。
-flags : 打印傳遞給JVM的命令行標誌。
-sysprops : 將Java系統屬性打印爲名。

jmap(Memory Map for Java):內存映射工具[生成堆轉儲快照]
命令格式:jinfo [ option ] vmid
-dump:[live,] format = b,file = filename : 以hprof二進制格式轉儲Java堆filename。live子選項說明是否之dump出存活的對象。
-finalizerinfo : 打印有關正在等待最終肯定的對象的信息(linux)。
-heap :顯示java堆詳細信息,如使用哪一種回收器、參數配置、分代情況等(linux)。
-histo [:live] : 顯示堆中對象統計信息,包括類、實例數量、合計容量。
-clstats : 打印Java堆的類加載器智能統計。對於每一個類加載器,它的名稱,它的活動程度,地址,父類加載器以及它加載的類的數量和大小。
-F : -dump或 -histo選項不響應時,該選項強制生成dump快照(不支持live)。

jhat(JVM Heap Analysis Tool):虛擬機堆轉儲快照分析工具
命令格式:jhat [ options ] 堆轉儲文件

jstack(Stack Trace for Java):Java堆棧跟蹤工具命令格式:jstack [ options ] pid-F : jstack[ -l] pid不響應時強制堆棧轉儲。-l : 打印有關鎖的其餘信息,例如所java.util.concurrent擁有的同步器列表。-m : 打印混合模式堆棧跟蹤,其中包含Java和本機C / C ++框架。

相關文章
相關標籤/搜索