主頁 > 後端開發 > 連肝三個通宵,JVM77道高頻面試題詳細分析,就這?

連肝三個通宵,JVM77道高頻面試題詳細分析,就這?

2020-10-22 02:21:32 後端開發

為方便大家記憶,記得收藏加關注哦 ,需要下載PDF版本請在公眾號【程式員空間】回復“資料”即可獲取下載方式,你也可以 點在文末微信掃描二維碼關注!

1、java 中會存在記憶體泄漏嗎,請簡單描述,

會,自己實作堆載的資料結構時有可能會出現記憶體泄露,

2、64 位 JVM 中,int 的長度是多數?

Java 中,int 型別變數的長度是一個固定值,與平臺無關,都是 32 位,意思就是說,在 32 位 和 64 位 的 Java 虛擬機中,int 型別的長度是相同的,

3、Serial 與 Parallel GC 之間的不同之處?

Serial 與 Parallel 在 GC 執行的時候都會引起 stop-the-world,它們之間主要不同 serial 收集器是默認的復制收集器,執行 GC 的時候只有一個執行緒,而 parallel 收集器使用多個 GC 執行緒來執行,

4、32 位和 64 位的 JVM,int 型別變數的長度是多數?

32 位和 64 位的 JVM 中,int 型別變數的長度是相同的,都是 32 位或者 4 個位元組,

5、Java 中 WeakReference 與 SoftReference 的區別?

雖然 WeakReference 與 SoftReference 都有利于提高 GC 和 記憶體的效率,但是 WeakReference ,一旦失去最后一個強參考,就會被 GC 回收,而軟參考雖然不能阻止被回收,但是可以延遲到 JVM 記憶體不足的時候,

6、JVM 選項 -XX:+UseCompressedOops 有什么作用?為什么要使用

當你將你的應用從 32 位的 JVM 遷移到 64 位的 JVM 時,由于物件的指標從 32 位增加到了 64 位,因此堆記憶體會突然增加,差不多要翻倍,這也會對 CPU 快取(容量比記憶體小很多)的資料產生不利的影響,因為,遷移到 64 位的 JVM 主要動機在于可以指定最大堆大小,通過壓縮 OOP 可以節省一定的記憶體,通過-XX:+UseCompressedOops 選項,JVM 會使用 32 位的 OOP,而不是 64 位的 OOP,

7、怎樣通過 Java 程式來判斷 JVM 是 32 位 還是 64 位?

你可以檢查某些系統屬性如 sun.arch.data.model 或 os.arch 來獲取該資訊,

8、32 位 JVM 和 64 位 JVM 的最大堆記憶體分別是多數?

理論上說上 32 位的 JVM 堆記憶體可以到達 2^32,即 4GB,但實際上會比這個小很多,不同作業系統之間不同,如 Windows 系統大約 1.5GB,Solaris 大約 3GB,64 位 JVM 允許指定最大的堆記憶體,理論上可以達到 2^64,這是一個非常大的數字,實際上你可以指定堆記憶體大小到 100GB,甚至有的 JVM,如 Azul,堆記憶體到 1000G 都是可能的,

9、JRE、JDK、JVM 及 JIT 之間有什么不同?

JRE 代表 Java 運行時(Java run-time),是運行 Java 參考所必須的,JDK 代表 Java 開發工具(Java development kit),是 Java 程式打開發工具,如 Java 編譯器,它也包含 JRE,JVM 代表 Java 虛擬機(Java virtual machine),它的責任是運行 Java 應用,JIT 代表即時編譯(Just In Time compilation),當代碼執行的次數超過一定的閾值時,會將 Java 位元組碼轉換為本地代碼,如,主要的熱點代碼會被準換為本地代碼,這樣有利大幅度提高 Java 應用的性能,

10、解釋 Java 堆空間及 GC?

當通過 Java 命令啟動 Java 行程的時候,會為它分配記憶體,記憶體的一部分用于創建堆空間,當程式中創建物件的時候,就從對空間中分配記憶體,GC 是 JVM 內部的一個行程,回收無效物件的記憶體用于將來的分配,

11、JVM 記憶體區域

JVM 記憶體區域主要分為執行緒私有區域【程式計數器、虛擬機堆疊、本地方法區】、執行緒共享區域【JAVA 堆、方法區】、直接記憶體,

執行緒私有資料區域生命周期與執行緒相同, 依賴用戶執行緒的啟動/結束 而 創建/銷毀(在 Hotspot VM 內, 每個執行緒都與作業系統的本地執行緒直接映射, 因此這部分記憶體區域的存/否跟隨本地執行緒的生/死對應),

執行緒共享區域隨虛擬機的啟動/關閉而創建/銷毀,

直接記憶體并不是 JVM 運行時資料區的一部分, 但也會被頻繁的使用: 在 JDK 1.4 引入的 NIO 提供了基于 Channel 與 Buffer 的 IO 方式, 它可以使用 Native 函式庫直接分配堆外記憶體, 然后使用 DirectByteBuffer 物件作為這塊記憶體的參考進行操作(詳見: Java I/O 擴展), 這樣就避免了在 Java 堆和 Native 堆中來回復制資料, 因此在一些場景中可以顯著提高性能,

12、程式計數器(執行緒私有)

一塊較小的記憶體空間, 是當前執行緒所執行的位元組碼的行號指示器,每條執行緒都要有一個獨立的程式計數器,這類記憶體也稱為“執行緒私有” 的記憶體,

正在執行 java 方法的話,計數器記錄的是虛擬機位元組碼指令的地址(當前指令的地址) ,如果還是 Native 方法,則為空,

這個記憶體區域是唯一一個在虛擬機中沒有規定任何 OutOfMemoryError 情況的區域,

13、虛擬機堆疊(執行緒私有)

是描述 java 方法執行的記憶體模型,每個方法在執行的同時都會創建一個堆疊幀(Stack Frame)用于存盤區域變數表、運算元堆疊、動態鏈接、方法出口等資訊,每一個方法從呼叫直至執行完成的程序,就對應著一個堆疊幀在虛擬機堆疊中入堆疊到出堆疊的程序,

堆疊幀( Frame)是用來存盤資料和部分程序結果的資料結構,同時也被用來處理動態鏈接(Dynamic Linking)、 方法回傳值和例外分派(Dispatch Exception),堆疊幀隨著方法呼叫而創建,隨著方法結束而銷毀——無論方法是正常完成還是例外完成(拋出了在方法內未被捕獲的例外)都算作方法結束,

14、本地方法區(執行緒私有)

本地方法區和 Java Stack 作用類似, 區別是虛擬機堆疊為執行 Java 方法服務, 而本地方法堆疊則為 Native 方法服務, 如果一個 VM 實作使用 C-linkage 模型來支持 Native 呼叫, 那么該堆疊將會是一個 C 堆疊,但 HotSpot VM 直接就把本地方法堆疊和虛擬機堆疊合二為一 ,

15、你能保證 GC 執行嗎?

不能,雖然你可以呼叫 System.gc() 或者 Runtime.gc(),但是沒有辦法保證 GC 的執行,

16、怎么獲取 Java 程式使用的記憶體?堆使用的百分比?

可以通過 java.lang.Runtime 類中與記憶體相關方法來獲取剩余的記憶體,總記憶體及最大堆記憶體,通過這些方法你也可以獲取到堆使用的百分比及堆記憶體的剩余空間,Runtime.freeMemory() 方法回傳剩余空間的位元組數,Runtime.totalMemory()方法總記憶體的位元組數,Runtime.maxMemory() 回傳最大記憶體的位元組數,

17、Java 中堆和堆疊有什么區別?

JVM 中堆和堆疊屬于不同的記憶體區域,使用目的也不同,堆疊常用于保存方法幀和區域變數,而物件總是在堆上分配,堆疊通常都比堆小,也不會在多個執行緒之間共享,而堆被整個 JVM 的所有執行緒共享,

18、描述一下 JVM 加載 class 檔案的原理機制

JVM 中類的裝載是由類加載器(ClassLoader)和它的子類來實作的,Java 中各類加載器是一個重要的 Java 運行時系統組件,它負責在運行時查找和裝入類檔案中的類,

由于 Java 的跨平臺性,經過編譯的 Java 源程式并不是一個可執行程式,而是一個或多個類檔案,當 Java 程式需要使用某個類時,JVM 會確保這個類已經被加載、連接(驗證、準備和決議)和初始化,類的加載是指把類的.class 檔案中的資料讀入到記憶體中,通常是創建一個位元組陣列讀入.class 檔案,然后產生與所加載類對應的 Class 物件,

加載完成后,Class 物件還不完整,所以此時的類還不可用,當類被加載后就進入連接階段,這一階段包括驗證、準備(為靜態變數分配記憶體并設定默認的初始值)和決議(將符號參考替換為直接參考)三個步驟,最后 JVM 對類進行初始化,包括:1)如果類存在直接的父類并且這個類還沒有被初始化,那么就先初始化父類;2)如果類中存在初始化陳述句,就依次執行這些初始化陳述句,

類的加載是由類加載器完成的,類加載器包括:根加載器(BootStrap)、擴展加載器(Extension)、系統加載器(System)和用戶自定義類加載器(java.lang.ClassLoader 的子類),

從 Java 2(JDK 1.2)開始,類加載程序采取了父親委托機制(PDM),PDM 更好的保證了 Java 平臺的安全性,在該機制中,JVM 自帶的 Bootstrap 是根加載器,其他的加載器都有且僅有一個父類加載器,類的加載首先請求父類加載器加載,父類加載器無能為力時才由其子類加載器自行加載,JVM 不會向 Java 程式提供對 Bootstrap 的參考,下面是關于幾個類

加載器的說明:

(1)Bootstrap:一般用本地代碼實作,負責加載 JVM 基礎核心類別庫(rt.jar);

(2)Extension:從 java.ext.dirs 系統屬性所指定的目錄中加載類別庫,它的父加載器是 Bootstrap;

(3)System:又叫應用類加載器,其父類是 Extension,它是應用最廣泛的類加載器,它從環境變數 classpath 或者系統屬性

java.class.path 所指定的目錄中記載類,是用戶自定義加載器的默認父加載器,

19、GC 是什么?為什么要有 GC?

GC 是垃 圾收 集的 意思 ,記憶體 處理 是編 程人 員容 易出 現問 題的 地方 ,忘記 或者 錯誤的內 存回 識訓 導致 程式 或系 統的 不穩 定甚 至崩 潰, Java 提供 的 GC 功能 可以 自動監測 物件 是否 超過 作用 域從 而達 到自 動回 收內 存的 目的 ,Java 語言 沒有 提供 釋放已分配記憶體的 顯示 操作 方法 ,Java 程式 員不 用擔 心內 存管 理, 因為 垃圾 收集 器會自動 進行 管理 ,要 請求 垃圾 收集 ,可 以調 用下 面的 方法 之一 :System.gc() 或 Runtime.getRuntime().gc() ,但 JVM 可以 屏蔽 掉線 示的 垃圾 回收 呼叫 ,

垃圾回收可以有效的防止記憶體泄露,有效的使用可以使用的記憶體,垃圾回收器通常是作為一個單獨的低優先級的執行緒運行,不可預知的情況下對記憶體堆中已經死亡的或者長時間沒有使用的物件進行清除和回收,程式員不能實時的呼叫垃圾回收器對某個物件或所有物件進行垃圾回收,在 Java 誕生初期,垃圾回收是 Java 最大的亮點之一,因為服務器端的編程需要有效的防止記憶體泄露問題,然而時過境遷,如今 Java 的垃圾回識訓制已經成為被詬病的東,移動智能終端用戶通常覺得 iOS 的系統比 Android 系統有更好的用戶體驗,其中一個深層次的原因就在于 Android 系統中垃圾回收的不可預知性,

20、堆(Heap-執行緒共享) -運行時資料區

是被執行緒共享的一塊記憶體區域, 創建的物件和陣列都保存在 Java 堆記憶體中,也是垃圾收集器進行垃圾收集的最重要的記憶體區域,由于現代 VM 采用分代收集演算法, 因此 Java 堆從 GC 的角度還可以細分為: 新生代(Eden 區、 From Survivor 區和 To Survivor 區)和老年代,

21、方法區/永久代(執行緒共享)

即我們常說的永久代(Permanent Generation), 用于存盤被 JVM 加載的類資訊、常量、靜態變數即、時編譯器編譯后的代碼等資料.HotSpot VM 把 GC 分代收集擴展至方法區, 即使用 Java 堆的永久代來實作方法區, 這樣 HotSpot 的垃圾收集器就可以像管理 Java 堆一樣管理這部分記憶體,而不必為方法區開發專門的記憶體管理器(永久帶的記憶體回收的主要目標是針對常量池的回收和型別的卸載, 因此收益一般很小) ,

運行時常量池(Runtime Constant Pool)是方法區的一部分,Class 檔案中除了有類的版本、欄位、方法、介面等描述等資訊外,還有一項資訊是常量池 (Constant Pool Table),用于存放編譯期生成的各種字面量和符號參考,這部分內容將在類加載后存放到方法區的運行時常量池中,Java 虛擬機對 Class 檔案的每一部分(自然也包括常量池)的格式都有嚴格的規定,每一個位元組用于存盤哪種資料都必須符合規范上的要求,這樣才會被虛擬機認可、裝載和執行,

22、JVM 運行時記憶體

Java 堆從 GC 的角度還可以細分為: 新生代(Eden 區、 From Survivor 區和 To Survivor 區)和老年代,

23、新生代

是用來存放新生的物件,一般占據堆的 1/3 空間,由于頻繁創建物件,所以新生代會頻繁觸發 MinorGC 進行垃圾回收,新生代又分為 Eden 區、 ServivorFrom、 ServivorTo 三個區,

Eden 區

Java 新物件的出生地(如果新創建的物件占用記憶體很大,則直接分配到老年代),當 Eden 區記憶體不夠的時候就會觸發 MinorGC,對新生代區進行一次垃圾回收,

ServivorFrom

上一次 GC 的幸存者,作為這一次 GC 的被掃描者,

ServivorTo

保留了一次 MinorGC 程序中的幸存者,

MinorGC 的程序(復制->清空->互換)

MinorGC 采用復制演算法,

(1) eden、 servicorFrom 復制到 ServicorTo,年齡+1

首先,把 Eden 和 ServivorFrom 區域中存活的物件復制到 ServicorTo 區域(如果有物件的年齡以及達到了老年的標準,則賦值到老年代區),同時把這些物件的年齡+1(如果 ServicorTo 不夠位置了就放到老年區);

(2)清空 eden、 servicorFrom

然后,清空 Eden 和 ServicorFrom 中的物件;

(3) ServicorTo 和 ServicorFrom 互換

最后, ServicorTo 和 ServicorFrom 互換,原 ServicorTo 成為下一次 GC 時的 ServicorFrom 區,

24、老年代

主要存放應用程式中生命周期長的記憶體物件,

老年代的物件比較穩定,所以 MajorGC 不會頻繁執行,在進行 MajorGC 前一般都先進行了一次 MinorGC,使得有新生代的物件晉身入老年代,導致空間不夠用時才觸發,當無法找到足夠大的連續空間分配給新創建的較大物件時也會提前觸發一次 MajorGC 進行垃圾回收騰出空間,

MajorGC 采用標記清除演算法:首先掃描一次所有老年代,標記出存活的物件,然后回收沒有標記的物件,ajorGC 的耗時比較長,因為要掃描再回收,MajorGC 會產生記憶體碎片,為了減少記憶體損耗,我們一般需要進行合并或者標記出來方便下次直接分配,當老年代也滿了裝不下的時候,就會拋出 OOM(Out of Memory)例外,

25、永久代

指記憶體的永久保存區域,主要存放 Class 和 Meta(元資料)的資訊,Class 在被加載的時候被放入永久區域, 它和和存放實體的區域不同,GC 不會在主程式運行期對永久區域進行清理,所以這也導致了永久代的區域會隨著加載的 Class 的增多而脹滿,最終拋出 OOM 例外,

26、JAVA8 與元資料

在 Java8 中, 永久代已經被移除,被一個稱為“元資料區”(元空間)的區域所取代,元空間的本質和永久代類似,元空間與永久代之間最大的區別在于:元空間并不在虛擬機中,而是使用本地記憶體,因此,默認情況下,元空間的大小僅受本地記憶體限制,類的元資料放入 nativememory, 字串池和類的靜態變數放入 java 堆中, 這樣可以加載多少類的元資料就不再由 MaxPermSize 控制, 而由系統的實際可用空間來控制,

27、參考計數法

在 Java 中,參考和物件是有關聯的,如果要操作物件則必須用參考進行,因此,很顯然一個簡單的辦法是通過參考計數來判斷一個物件是否可以回收,簡單說,即一個物件如果沒有任何與之關聯的參考, 即他們的參考計數都不為 0, 則說明物件不太可能再被用到,那么這個物件就是可回收物件,

28、可達性分析

為了解決參考計數法的回圈參考問題, Java 使用了可達性分析的方法,通過一系列的“GC roots”物件作為起點搜索,如果在“GC roots”和一個物件之間沒有可達路徑,則稱該物件是不可達的,要注意的是,不可達物件不等價于可回收物件, 不可達物件變為可回收物件至少要經過兩次標記程序,兩次標記后仍然是可回收物件,則將面臨回收,

29、標記清除演算法( Mark-Sweep)

最基礎的垃圾回收演算法,分為兩個階段,標注和清除,標記階段標記出所有需要回收的物件,清除階段回收被標記的物件所占用的空間,如圖

從圖中我們就可以發現,該演算法最大的問題是記憶體碎片化嚴重,后續可能發生大物件不能找到可利用空間的問題,

30、復制演算法(copying)

為了解決 Mark-Sweep 演算法記憶體碎片化的缺陷而被提出的演算法,按記憶體容量將記憶體劃分為等大小的兩塊,每次只使用其中一塊,當這一塊記憶體滿后將尚存活的物件復制到另一塊上去,把已使用的記憶體清掉,如圖:

這種演算法雖然實作簡單,記憶體效率高,不易產生碎片,但是最大的問題是可用記憶體被壓縮到了原本的一半,且存活物件增多的話, Copying 演算法的效率會大大降低,

31、標記整理演算法(Mark-Compact)

結合了以上兩個演算法,為了避免缺陷而提出,標記階段和 Mark-Sweep 演算法相同, 標記后不是清理物件,而是將存活物件移向記憶體的一端,然后清除端邊界外的物件,如圖:

32、分代收集演算法

分代收集法是目前大部分 JVM 所采用的方法,其核心思想是根據物件存活的不同生命周期將記憶體劃分為不同的域,一般情況下將 GC 堆劃分為老生代(Tenured/Old Generation)和新生代(YoungGeneration),老生代的特點是每次垃圾回收時只有少量物件需要被回收,新生代的特點是每次垃圾回收時都有大量垃圾需要被回收,因此可以根據不同區域選擇不同的演算法,

33、新生代與復制演算法

目前大部分 JVM 的 GC 對于新生代都采取 Copying 演算法,因為新生代中每次垃圾回收都要回收大部分物件,即要復制的操作比較少,但通常并不是按照 1:1 來劃分新生代,一般將新生代劃分為一塊較大的 Eden 空間和兩個較小的 Survivor 空間(From Space, To Space),每次使用 Eden 空間和其中的一塊 Survivor 空間,當進行回收時,將該兩塊空間中還存活的物件復制到另一塊 Survivor 空間中,

34、老年代與標記復制演算法

而老年代因為每次只回收少量物件,因而采用 Mark-Compact 演算法,

(1)JAVA 虛擬機提到過的處于方法區的永生代(Permanet Generation), 它用來存盤 class 類,常量,方法描述等,對永生代的回收主要包括廢棄常量和無用的類,

(2)物件的記憶體分配主要在新生代的 Eden Space 和 Survivor Space 的 From Space(Survivor 目前存放物件的那一塊),少數情況會直接分配到老生代,

(3)當新生代的 Eden Space 和 From Space 空間不足時就會發生一次 GC,進行 GC 后, EdenSpace 和 From Space 區的存活物件會被挪到 To Space,然后將 Eden Space 和 FromSpace 進行清理,

(4)如果 To Space 無法足夠存盤某個物件,則將這個物件存盤到老生代,

(5)在進行 GC 后,使用的便是 Eden Space 和 To Space 了,如此反復回圈,

(6)當物件在 Survivor 去躲過一次 GC 后,其年齡就會+1,默認情況下年齡到達 15 的物件會被移到老生代中,

35、JAVA 強參考

在 Java 中最常見的就是強參考, 把一個物件賦給一個參考變數,這個參考變數就是一個強參考,當一個物件被強參考變數參考時,它處于可達狀態,它是不可能被垃圾回識訓制回收的,即使該物件以后永遠都不會被用到 JVM 也不會回收,因此強參考是造成 Java 記憶體泄漏的主要原因之一,

36、JAVA 軟參考

軟參考需要用 SoftReference 類來實作,對于只有軟參考的物件來說,當系統記憶體足夠時它不會被回收,當系統記憶體空間不足時它會被回收,軟參考通常用在對記憶體敏感的程式中,

37、JAVA 弱參考

弱參考需要用 WeakReference 類來實作,它比軟參考的生存期更短,對于只有弱參考的物件來說,只要垃圾回識訓制一運行,不管 JVM 的記憶體空間是否足夠,總會回收該物件占用的記憶體,

38、JAVA 虛參考

虛參考需要 PhantomReference 類來實作,它不能單獨使用,必須和參考佇列聯合使用,虛參考的主要作用是跟蹤物件被垃圾回收的狀態,

39、分代收集演算法

當前主流 VM 垃圾收集都采用”分代收集” (Generational Collection)演算法, 這種演算法會根據物件存活周期的不同將記憶體劃分為幾塊, 如 JVM 中的新生代、老年代、永久代, 這樣就可以根據各年代特點分別采用最適當的 GC 演算法

40、在新生代-復制演算法

每次垃圾收集都能發現大批物件已死, 只有少量存活. 因此選用復制演算法, 只需要付出少量存活物件的復制成本就可以完成收集

41、在老年代-標記整理演算法

因為物件存活率高、沒有額外空間對它進行分配擔保, 就必須采用“標記—清理”或“標記—整理” 演算法來進行回收, 不必進行記憶體復制, 且直接騰出空閑記憶體,

42、磁區收集演算法

磁區演算法則將整個堆空間劃分為連續的不同小區間, 每個小區間獨立使用, 獨立回收. 這樣做的好處是可以控制一次回收多少個小區間 , 根據目標停頓時間, 每次合理地回收若干個小區間(而不是整個堆), 從而減少一次 GC 所產生的停頓,

43、GC 垃圾收集器

Java 堆記憶體被劃分為新生代和年老代兩部分,新生代主要使用復制和標記-清除垃圾回收演算法;年老代主要使用標記-整理垃圾回收演算法,因此 java 虛擬中針對新生代和年老代分別提供了多種不同的垃圾收集器, JDK1.6 中 Sun HotSpot 虛擬機的垃圾收集器如下:

44、Serial 垃圾收集器(單執行緒、 復制演算法)

Serial(英文連續) 是最基本垃圾收集器,使用復制演算法,曾經是 JDK1.3.1 之前新生代唯一的垃圾收集器,Serial 是一個單執行緒的收集器,它不但只會使用一個 CPU 或一條執行緒去完成垃圾收集作業,并且在進行垃圾收集的同時,必須暫停其他所有的作業執行緒,直到垃圾收集結束,

Serial 垃圾收集器雖然在收集垃圾程序中需要暫停所有其他的作業執行緒,但是它簡單高效,對于限定單個 CPU 環境來說,沒有執行緒互動的開銷,可以獲得最高的單執行緒垃圾收集效率,因此 Serial 垃圾收集器依然是 java 虛擬機運行在 Client 模式下默認的新生代垃圾收集器,

45、ParNew 垃圾收集器(Serial+多執行緒)

ParNew 垃圾收集器其實是 Serial 收集器的多執行緒版本,也使用復制演算法,除了使用多執行緒進行垃圾收集之外,其余的行為和 Serial 收集器完全一樣, ParNew 垃圾收集器在垃圾收集程序中同樣也要暫停所有其他的作業執行緒,

ParNew 收集器默認開啟和 CPU 數目相同的執行緒數,可以通過-XX:ParallelGCThreads 引數來限制垃圾收集器的執行緒數,【Parallel:平行的】

ParNew 雖然是除了多執行緒外和 Serial 收集器幾乎完全一樣,但是 ParNew 垃圾收集器是很多 java 虛擬機運行在 Server 模式下新生代的默認垃圾收集器,

46、Parallel Scavenge 收集器(多執行緒復制演算法、高效)

Parallel Scavenge 收集器也是一個新生代垃圾收集器,同樣使用復制演算法,也是一個多執行緒的垃圾收集器, 它重點關注的是程式達到一個可控制的吞吐量(Thoughput, CPU 用于運行用戶代碼的時間/CPU 總消耗時間,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)),高吞吐量可以最高效率地利用 CPU 時間,盡快地完成程式的運算任務,主要適用于在后臺運算而不需要太多互動的任務,自適應調節策略也是 ParallelScavenge 收集器與 ParNew 收集器的一個重要區別,

47、Serial Old 收集器(單執行緒標記整理演算法 )

Serial Old 是 Serial 垃圾收集器年老代版本,它同樣是個單執行緒的收集器,使用標記-整理演算法,這個收集器也主要是運行在 Client 默認的

java 虛擬機默認的年老代垃圾收集器,在 Server 模式下,主要有兩個用途:

(1)在 JDK1.5 之前版本中與新生代的 Parallel Scavenge 收集器搭配使用,

(2)作為年老代中使用 CMS 收集器的后備垃圾收集方案,新生代 Serial 與年老代 Serial Old 搭配垃圾收集程序圖:

新生代 Parallel Scavenge 收集器與 ParNew 收集器作業原理類似,都是多執行緒的收集器,都使用的是復制演算法,在垃圾收集程序中都需要暫停所有的作業執行緒,新生代 ParallelScavenge/ParNew 與年老代 Serial Old 搭配垃圾收集程序圖:

48、Parallel Old 收集器(多執行緒標記整理演算法)

Parallel Old 收集器是 Parallel Scavenge 的年老代版本,使用多執行緒的標記-整理演算法,在 JDK1.6 才開始提供,

在 JDK1.6 之前,新生代使用 ParallelScavenge 收集器只能搭配年老代的 Serial Old 收集器,只能保證新生代的吞吐量優先,無法保證整體的吞吐量, Parallel Old 正是為了在年老代同樣提供吞吐量優先的垃圾收集器, 如果系統對吞吐量要求比較高,可以優先考慮新生代 Parallel Scavenge 和年老代 Parallel Old 收集器的搭配策略,

新生代 Parallel Scavenge 和年老代 Parallel Old 收集器搭配運行程序圖

49、CMS 收集器(多執行緒標記清除演算法)

Concurrent mark sweep(CMS)收集器是一種年老代垃圾收集器,其最主要目標是獲取最短垃圾回收停頓時間, 和其他年老代使用標記-整理演算法不同,它使用多執行緒的標記-清除演算法,最短的垃圾收集停頓時間可以為互動比較高的程式提高用戶體驗,CMS 作業機制相比其他的垃圾收集器來說更復雜,整個程序分為以下 4 個階段:

初始標記

只是標記一下 GC Roots 能直接關聯的物件,速度很快,仍然需要暫停所有的作業執行緒,

并發標記

進行 GC Roots 跟蹤的程序,和用戶執行緒一起作業,不需要暫停作業執行緒,

重新標記

為了修正在并發標記期間,因用戶程式繼續運行而導致標記產生變動的那一部分物件的標記記錄,仍然需要暫停所有的作業執行緒,

并發清除

清除 GC Roots 不可達物件,和用戶執行緒一起作業,不需要暫停作業執行緒,由于耗時最長的并發標記和并發清除程序中,垃圾收集執行緒可以和用戶現在一起并發作業, 所以總體上來看 CMS 收集器的記憶體回收和用戶執行緒是一起并發地執行,CMS 收集器作業程序

50、G1 收集器

Garbage first 垃圾收集器是目前垃圾收集器理論發展的最前沿成果,相比與 CMS 收集器, G1 收集器兩個最突出的改進是:

(1)基于標記-整理演算法,不產生記憶體碎片,

(2)可以非常精確控制停頓時間,在不犧牲吞吐量前提下,實作低停頓垃圾回收,G1 收集器避免全區域垃圾收集,它把堆記憶體劃分為大小固定的幾個獨立區域,并且跟蹤這些區域的垃圾收集進度,同時在后臺維護一個優先級串列,每次根據所允許的收集時間, 優先回收垃圾最多的區域,區域劃分和優先級區域回識訓制,確保 G1 收集器可以在有限時間獲得最高的垃圾收集效率

51、JVM 類加載機制

JVM 類加載機制分為五個部分:加載,驗證,準備,決議,初始化,下面我們就分別來看一下這五個程序,

加載

加載是類加載程序中的一個階段, 這個階段會在記憶體中生成一個代表這個類的 java.lang.Class 物件, 作為方法區這個類的各種資料的入口,注意這里不一定非得要從一個 Class 檔案獲取,這里既可以從 ZIP 包中讀取(比如從 jar 包和 war 包中讀取),也可以在運行時計算生成(動態代理),也可以由其它檔案生成(比如將 JSP 檔案轉換成對應的 Class 類),

驗證

這一階段的主要目的是為了確保 Class 檔案的位元組流中包含的資訊是否符合當前虛擬機的要求,并且不會危害虛擬機自身的安全,

準備

準備階段是正式為類變數分配記憶體并設定類變數的初始值階段,即在方法區中分配這些變數所使用的記憶體空間,注意這里所說的初始值概念,比如一個類變數定義為:

實際上變數 v 在準備階段過后的初始值為 0 而不是 8080, 將 v 賦值為 8080 的 put static 指令是程式被編譯后, 存放于類構造器方法之中,

但是注意如果宣告為:

public static final int v = 8080;

在編譯階段會為 v 生成 ConstantValue 屬性,在準備階段虛擬機會根據 ConstantValue 屬性將 v 賦值為 8080,

決議

決議階段是指虛擬機將常量池中的符號參考替換為直接參考的程序,符號參考就是 class 檔案中的

public static int v = 8080;

實際上變數 v 在準備階段過后的初始值為 0 而不是 8080, 將 v 賦值為 8080 的 put static 指令是程式被編譯后, 存放于類構造器方法之中,但是注意如果宣告為:

在編譯階段會為 v 生成 ConstantValue 屬性,在準備階段虛擬機會根據 ConstantValue 屬性將 v 賦值為 8080,

決議

決議階段是指虛擬機將常量池中的符號參考替換為直接參考的程序,符號參考就是 class 檔案中的

public static final int v = 8080;

在編譯階段會為 v 生成 ConstantValue 屬性,在準備階段虛擬機會根據 ConstantValue 屬性將 v 賦值為 8080,

決議

決議階段是指虛擬機將常量池中的符號參考替換為直接參考的程序,符號參考就是 class 檔案中的:

(1) CONSTANT_Class_info

(2)CONSTANT_Field_info

(3)CONSTANT_Method_info

等型別的常量,

符號參考

符號參考與虛擬機實作的布局無關, 參考的目標并不一定要已經加載到記憶體中,各種虛擬機實作的記憶體布局可以各不相同,但是它們能接受的符號參考必須是一致的,因為符號參考的字面量形式明確定義在 Java 虛擬機規范的 Class 檔案格式中,

直接參考

直接參考可以是指向目標的指標,相對偏移量或是一個能間接定位到目標的句柄,如果有了直接參考,那參考的目標必定已經在記憶體中存在,

初始化

初始化階段是類加載最后一個階段,前面的類加載階段之后,除了在加載階段可以自定義類加載器以外,其它操作都由 JVM 主導,到了初始階段,才開始真正執行類中定義的 Java 程式代碼,

類構造器

初始化階段是執行類構造器方法的程序,方法是由編譯器自動收集類中的類變數的賦值操作和靜態陳述句塊中的陳述句合并而成的,虛擬機會保證子方法執行之前,父類的方法已經執行完畢, 如果一個類中沒有對靜態變數賦值也沒有靜態陳述句塊,那么編譯器可以不為這個類生成()方法,注意以下幾種情況不會執行類初始化:

(1)通過子類參考父類的靜態欄位,只會觸發父類的初始化,而不會觸發子類的初始化,

(2)定義物件陣列,不會觸發該類的初始化,

(3)常量在編譯期間會存入呼叫類的常量池中,本質上并沒有直接參考定義常量的類,不會觸發定義常量所在的類,

(4)通過類名獲取 Class 物件,不會觸發類的初始化,

(5)通過 Class.forName 加載指定類時,如果指定引數 initialize 為 false 時,也不會觸發類初始化,其實這個引數是告訴虛擬機,是否要對類進行初始化,

(6)通過 ClassLoader 默認的 loadClass 方法,也不會觸發初始化動作,

52、類加載器

虛擬機設計團隊把加載動作放到 JVM 外部實作,以便讓應用程式決定如何獲取所需的類, JVM 提供了 3 種類加載器:

啟動類加載器(Bootstrap ClassLoader)

負責加載 JAVA_HOME\lib 目錄中的, 或通過-Xbootclasspath 引數指定路徑中的, 且被虛擬機認可(按檔案名識別, 如 rt.jar) 的類,

擴展類加載器(Extension ClassLoader)

負責加載 JAVA_HOME\lib\ext 目錄中的,或通過 java.ext.dirs 系統變數指定路徑中的類別庫,

應用程式類加載器(Application ClassLoader):

負責加載用戶路徑(classpath)上的類別庫,JVM 通過雙親委派模型進行類的加載, 當然我們也可以通過繼承 java.lang.ClassLoader 實作自定義的類加載器,

53、雙親委派

當一個類收到了類加載請求,他首先不會嘗試自己去加載這個類,而是把這個請求委派給父類去完成,每一個層次類加載器都是如此,因此所有的加載請求都應該傳送到啟動類加載其中,只有當父類加載器反饋自己無法完成這個請求的時候(在它的加載路徑下沒有找到所需加載的 Class), 子類加載器才會嘗試自己去加載,

采用雙親委派的一個好處是比如加載位于 rt.jar 包中的類 java.lang.Object,不管是哪個加載器加載這個類,最終都是委托給頂層的啟動類加載器進行加載,這樣就保證了使用不同的類加載器最終得到的都是同樣一個 Object 物件

54、OSGI( 動態模型系統)

OSGi(Open Service Gateway Initiative),是面向 Java 的動態模型系統,是 Java 動態化模塊化系統的一系列規范,

55、動態改變構造

OSGi 服務平臺提供在多種網路設備上無需重啟的動態改變構造的功能,為了最小化耦合度和促使這些耦合度可管理, OSGi 技術提供一種面向服務的架構,它能使這些組件動態地發現對方,

56、模塊化編程與熱插拔

OSGi 旨在為實作 Java 程式的模塊化編程提供基礎條件,基于 OSGi 的程式很可能可以實作模塊級的熱插拔功能,當程式升級更新時,可以只停用、重新安裝然后啟動程式的其中一部分,這對企業級程式開發來說是非常具有傭訓力的特性,

OSGi 描繪了一個很美好的模塊化開發目標,而且定義了實作這個目標的所需要服務與架構,同時也有成熟的框架進行實作支持,但并非所有的應用都適合采用 OSGi 作為基礎架構,它在提供強大功能同時,也引入了額外的復雜度,因為它不遵守了類加載的雙親委托模型,

57、JVM 記憶體模型

執行緒獨占:堆疊,本地方法堆疊,程式計數器

執行緒共享:堆,方法區

58、堆疊

又稱方法堆疊,執行緒私有的,執行緒執行方法是都會創建一個堆疊陣,用來存盤區域變數表,操作堆疊,動態鏈接,方法出口等資訊.呼叫方法時執行入堆疊,方法回傳式執行出堆疊.

59、本地方法堆疊

與堆疊類似,也是用來保存執行方法的資訊.執行 Java 方法是使用堆疊,執行 Native 方法時使用本地方法堆疊.

60、程式計數器

保存著當前執行緒執行的位元組碼位置,每個執行緒作業時都有獨立的計數器,只為執行 Java 方法服務,執行 Native 方法時,程式計數器為空.

61、堆

JVM 記憶體管理最大的一塊,對被執行緒共享,目的是存放物件的實體,幾乎所欲的物件實體都會放在這里,當堆沒有可用空間時,會拋出 OOM 例外.根據物件的存活周期不同,JVM 把物件進行分代管理,由垃圾回收器進行垃圾的回收管理

62、方法區

又稱非堆區,用于存盤已被虛擬機加載的類資訊,常量,靜態變數,即時編譯器優化后的代碼等資料.1.7 的永久代和 1.8 的元空間都是方法區的一種實作,

63、分代回收

分代回識訓于兩個事實:大部分物件很快就不使用了,還有一部分不會立即無用,但也不會持續很長時間

年輕代->標記-復制

老年代->標記-清除

64、堆和堆疊的區別

堆疊是運行時單位,代表著邏輯,內含基本資料型別和堆中物件參考,所在區域連續,沒有碎片;堆是存盤單位,代表著資料,可被多個堆疊共享(包括成員中基本資料型別、參考和參考物件),所在區域不連續,會有碎片,

(1)功能不同

堆疊記憶體用來存盤區域變數和方法呼叫,而堆記憶體用來存盤 Java 中的物件,無論是成員變數,區域變數,還是類變數,它們指向的物件都存盤在堆記憶體中,

(2)共享性不同

堆疊記憶體是執行緒私有的,

堆記憶體是所有執行緒共有的,

(3)例外錯誤不同

如果堆疊記憶體或者堆記憶體不足都會拋出例外,

堆疊空間不足:java.lang.StackOverFlowError,

堆空間不足:java.lang.OutOfMemoryError,

(4)空間大小

堆疊的空間大小遠遠小于堆的

65、什么時候會觸發 FullGC

除直接呼叫 System.gc 外,觸發 Full GC 執行的情況有如下四種,

(1)舊生代空間不足

舊生代空間只有在新生代物件轉入及創建為大物件、大陣列時才會出現不足的現象,當執行 Full GC 后空間仍然不足,則拋出如下錯

誤:

java.lang.OutOfMemoryError: Java heap space

為避免以上兩種狀況引起的 FullGC,調優時應盡量做到讓物件在 Minor GC 階段被回收、讓物件在新生代多存活一段時間及不要創建過大的物件及陣列,

(2) Permanet Generation 空間滿

PermanetGeneration 中存放的為一些 class 的資訊等,當系統中要加載的類、反射的類和呼叫的方法較多時,Permanet Generation 可能會被占滿,在未配置為采用 CMS GC 的情況下會執行 Full GC,如果經過 Full GC 仍然回收不了,那么 JVM 會拋出如下錯誤資訊:

java.lang.OutOfMemoryError: PermGen space

為避免 Perm Gen 占滿造成 Full GC 現象,可采用的方法為增大 Perm Gen 空間或轉為使用 CMS GC,

(3)CMS GC 時出現 promotion failed 和 concurrent mode failure

對于采用 CMS 進行舊生代 GC 的程式而言,尤其要注意 GC 日志中是否有 promotion failed 和 concurrent mode failure 兩種狀況,當這兩種狀況出現時可能會觸發 Full GC,

promotionfailed 是在進行 Minor GC 時,survivor space 放不下、物件只能放入舊生代,而此時舊生代也放不下造成的;concurrentmode failure 是在執行 CMS GC 的程序中同時有物件要放入舊生代,而此時舊生代空間不足造成的,

應對措施為:增大 survivorspace、舊生代空間或調低觸發并發 GC 的比率,但在 JDK 5.0+、6.0+的版本中有可能會由于 JDK 的 bug29 導致 CMS 在 remark 完畢后很久才觸發 sweeping 動作,對于這種狀況,可通過設定-XX:CMSMaxAbortablePrecleanTime=5(單位為 ms)來避免,

(4)統計得到的 Minor GC 晉升到舊生代的平均大小大于舊生代的剩余空間

這是一個較為復雜的觸發情況,Hotspot 為了避免由于新生代物件晉升到舊生代導致舊生代空間不足的現象,在進行 Minor GC 時,做了一個判斷,如果之前統計所得到的 Minor GC 晉升到舊生代的平均大小大于舊生代的剩余空間,那么就直接觸發 Full GC,

例如程式第一次觸發 MinorGC 后,有 6MB 的物件晉升到舊生代,那么當下一次 Minor GC 發生時,首先檢查舊生代的剩余空間是否大于 6MB,如果小于 6MB,則執行 Full GC,

當新生代采用 PSGC 時,方式稍有不同,PS GC 是在 Minor GC 后也會檢查,例如上面的例子中第一次 Minor GC 后,PS GC 會檢查此時舊生代的剩余空間是否大于 6MB,如小于,則觸發對舊生代的回收,除了以上 4 種狀況外,對于使用 RMI 來進行 RPC 或管理的 Sun JDK 應用而言,默認情況下會一小時執行一次 Full GC,可通過在啟動時通過- java-Dsun.rmi.dgc.client.gcInterval=3600000 來設定 Full GC 執行的間隔時間或通過-XX:+ DisableExplicitGC 來禁止 RMI 呼叫 System.gc

66、什么是 Java 虛擬機?為什么 Java 被稱作是“平臺無關的編程語言”?

Java 虛擬機是一個可以執行 Java 位元組碼的虛擬機行程,Java 源檔案被編譯成能被 Java 虛擬機執行的位元組碼檔案,Java 被設計成允許應用程式可以運行在任意的平臺,而不需要程式員為每一個平臺單獨重寫或者是重新編譯,Java 虛擬機讓這個變為可能,因為它知道底層硬體平臺的指令長度和其他特性,

67、物件分配規則

(1)物件優先分配在 Eden 區,如果 Eden 區沒有足夠的空間時,虛擬機執行一次 Minor GC,

(2)大物件直接進入老年代(大物件是指需要大量連續記憶體空間的物件),這樣做的目的是避免在 Eden 區和兩個 Survivor 區之間發生大量的記憶體拷貝(新生代采用復制演算法收集記憶體),

(3)長期存活的物件進入老年代,虛擬機為每個物件定義了一個年齡計數器,如果物件經過了 1 次 Minor GC 那么物件會進入 Survivor 區,之后每經過一次 Minor GC 那么物件的年齡加 1,知道達到閥值物件進入老年區,

(4) 動態判斷物件的年齡,如果 Survivor 區中相同年齡的所有物件大小的總和大于 Survivor 空間的一半,年齡大于或等于該年齡的物件可以直接進入老年代,

(5) 空間分配擔保,每次進行 Minor GC 時,JVM 會計算 Survivor 區移至老年區的物件的平均大小,如果這個值大于老年區的剩余值大小則進行一次 Full GC,如果小于檢查 HandlePromotionFailure 設定,如果 true 則只進行 Monitor GC,如果 false 則進行 Full GC

68、描述一下 JVM 加載 class 檔案的原理機制?

JVM 中類的裝載是由類加載器(ClassLoader)和它的子類來實作的,Java 中的類加載器是一個重要的 Java 運行時系統組件,它負責在運行時查找和裝入類檔案中的類,

由于 Java 的跨平臺性,經過編譯的 Java 源程式并不是一個可執行程式,而是一個或多個類檔案,當 Java 程式需要使用某個類時,JVM 會確保這個類已經被加載、連接(驗證、準備和決議)和初始化,

類的加載是指把類的.class 檔案中的資料讀入到記憶體中,通常是創建一個位元組陣列讀入.class 檔案,然后產生與所加載類對應的 Class 物件,加載完成后,Class 物件還不完整,所以此時的類還不可用,

當類被加載后就進入連接階段,這一階段包括驗證、準備(為靜態變數分配記憶體并設定默認的初始值)和決議(將符號參考替換為直接參考)三個步驟,最后 JVM 對類進行初始化,

包括:

(1)如果類存在直接的父類并且這個類還沒有被初始化,那么就先初始化父類;

(2)如果類中存在初始化陳述句,就依次執行這些初始化陳述句,類的加載是由類加載器完成的,類加載器包括:根加載器(BootStrap)、擴展加載器(Extension)、系統加載器(System)和用戶自定義類加載器(java.lang.ClassLoader 的子類),

從 Java 2(JDK 1.2)開始,類加載程序采取了父親委托機制(PDM),PDM 更好的保證了 Java 平臺的安全性,在該機制中,JVM 自帶的 Bootstrap 是根加載器,其他的加載器都有且僅有一個父類加載器,類的加載首先請求父類加載器加載,父類加載器無能為力時才由其子類加載器自行加載,JVM 不會向 Java 程式提供對 Bootstrap 的參考,下面是關于幾個類加載器的說明

Bootstrap:一般用本地代碼實作,負責加載 JVM 基礎核心類別庫(rt.jar);

Extension:從 java.ext.dirs 系統屬性所指定的目錄中加載類別庫,它的父加載器是 Bootstrap;

System:又叫應用類加載器,其父類是 Extension,它是應用最廣泛的類加載器,它從環境變數 classpath 或者系統屬性 java.class.path 所指定的目錄中記載類,是用戶自定義加載器的默認父加載器,

69、Java 物件創建程序

(1)JVM 遇到一條新建物件的指令時首先去檢查這個指令的引數是否能在常量池中定義到一個類的符號參考,然后加載這個類(類加載程序在后邊講)

(2)為物件分配記憶體,一種辦法“指標碰撞”、一種辦法“空閑串列”,最終常用的辦法“本地執行緒緩沖分配(TLAB)”

(3)將除物件頭外的物件記憶體空間初始化為 0

(4)對物件頭進行必要設定

70、簡述 Java 的物件結構

Java 物件由三個部分組成:物件頭、實體資料、對齊填充,

物件頭由兩部分組成,第一部分存盤物件自身的運行時資料:哈希碼、GC 分代年齡、鎖標識狀態、執行緒持有的鎖、偏向執行緒 ID(一般占 32/64 bit),第二部分是指標型別,指向物件的類元資料型別(即物件代表哪個類),如果是陣列物件,則物件頭中還有一部分用來記錄陣列長度,

實體資料用來存盤物件真正的有效資訊(包括父類繼承下來的和自己定義的)

對齊填充:JVM 要求物件起始地址必須是 8 位元組的整數倍(8 位元組對齊 )

71、如何判斷物件可以被回收

判斷物件是否存活一般有兩種方式:

參考計數:每個物件有一個參考計數屬性,新增一個參考時計數加 1,參考釋放時計數減 1,計數為 0 時可以回收,此方法簡單,無法解決物件相互回圈參考的問題,

可達性分析(Reachability Analysis):從 GC Roots 開始向下搜索,搜索所走過的路徑稱為參考鏈,當一個物件到 GC Roots 沒有任何參考鏈相連時,則證明此物件是不可用的,不可達物件,

72、JVM 的永久代中會發生垃圾回收么

垃圾回收不會發生在永久代,如果永久代滿了或者是超過了臨界值,會觸發完全垃圾回收(Full GC),如果你仔細查看垃圾收集器的輸出資訊,就會發現永久代也是被回收的,這就是為什么正確的永久代大小對避免 Full GC 是非常重要的原因,請參考下 Java8:從永久代到元資料區 (注:Java8 中已經移除了永久代,新加了一個叫做元資料區的 native 記憶體區)

73、垃圾收集演算法

GC 最基礎的演算法有三種:標記 -清除演算法、復制演算法、標記-壓縮演算法,我們常用的垃圾回收器一般都采用分代收集演算法,

標記 -清除演算法

“標記-清除”(Mark-Sweep)演算法,如它的名字一樣,演算法分為“標記”和“清除”兩個階段:首先標記出所有需要回收的物件,在標記完成后統一回收掉所有被標記的物件,

復制演算法

“復制”(Copying)的收集演算法,它將可用記憶體按容量劃分為大小相等的兩塊,每次只使用其中的一塊,當這一塊的記憶體用完了,就將還存活著的物件復制到另外一塊上面,然后再把已使用過的記憶體空間一次清理掉,

標記-壓縮演算法

標記程序仍然與“標記-清除”演算法一樣,但后續步驟不是直接對可回收物件進行清理,而是讓所有存活的物件都向一端移動,然后直接清理掉端邊界以外的記憶體

分代收集演算法

“分代收集”(Generational Collection)演算法,把 Java 堆分為新生代和老年代,這樣就可以根據各個年代的特點采用最適當的收集演算法

74、調優命令有哪些?

Sun JDK 監控和故障處理命令有 jps jstat jmap jhat jstack jinfo

(1)jps,JVM Process Status Tool,顯示指定系統內所有的 HotSpot 虛擬機行程,

(1)jstat,JVM statistics Monitoring 是用于監視虛擬機運行時狀態資訊的命令,它可以顯示出虛擬機行程中的類裝載、記憶體、垃圾收集、JIT 編譯等運行資料,

(3)jmap,JVM Memory Map 命令用于生成 heap dump 檔案

(4) jhat,JVM Heap Analysis Tool 命令是與 jmap 搭配使用,用來分析 jmap 生成的 dump,jhat 內置了一個微型的 HTTP/HTML 服務器,生成 dump 的分析結果后,可以在瀏覽器中查看

(5)jstack,用于生成 java 虛擬機當前時刻的執行緒快照,

(6) jinfo,JVM Configuration info 這個命令作用是實時查看和調整虛擬機運行引數

75、調優工具

常用調優工具分為兩類,jdk 自帶監控工具:jconsole 和 jvisualvm,第三方有:MAT(Memory AnalyzerTool)、GChisto,

(1) jconsole,Java Monitoring and Management Console 是從 java5 開始,在 JDK 中自帶的 java 監控和管理控制臺,用于對 JVM 中記憶體,執行緒和類等的監控

(2)jvisualvm,jdk 自帶全能工具,可以分析記憶體快照、執行緒快照;監控記憶體變化、GC 變化等,

(3)MAT,Memory Analyzer Tool,一個基于 Eclipse 的記憶體分析工具,是一個快速、功能豐富的 Javaheap 分析工具,它可以幫助我們查找記憶體泄漏和減少記憶體消耗

(4)GChisto,一款專業分析 gc 日志的工具

76、Minor GC 與 Full GC 分別在什么時候發生?

新生代記憶體不夠用時候發生 MGC 也叫 YGC,JVM 記憶體不夠的時候發生 FGC

77、你知道哪些 JVM 性能調優

設定堆記憶體大小

-Xmx:堆記憶體最大限制,

設定新生代大小,新生代不宜太小,否則會有大量物件涌入老年代

-XX:NewSize:新生代大小

-XX:NewRatio 新生代和老生代占比

-XX:SurvivorRatio:伊甸園空間和幸存者空間的占比

設定垃圾回收器 年輕代用 -XX:+UseParNewGC 年老代用-XX:+UseConcMarkSweepGC

在這里插入圖片描述

活頁筆記本簡單掃碼關注二維碼.png

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/184908.html

標籤:Java

上一篇:單例模式

下一篇:Druid 的整合

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more