身邊有同學實習面試被問,JVM性能調優等問題,來總結一下JVM
什么是JVM

JAVA物件的組成
如物件組成中的分代年齡(4bit),跟GC的分代相關;

JVM如何執行.java檔案以及運行期資料區

基礎的概念不寫了,文章后面補上;
什么是程式計數器,作用?
什么是 Java 虛擬機堆疊,作用?
什么是 本地方法堆疊,作用?
什么是 堆,作用?
什么是方法區,作用?
說出java檔案運行的整個程序?
堆疊楨
java類檔案轉換為class檔案,在經過類裝載系統,到達運行時資料區運行(我們常常說到的記憶體管理就是針對這段空間進行管理(如何分配和回收記憶體空間));運行時資料區包括(程式計數器,虛擬機堆疊,本地方法堆疊,堆(執行緒共享),方法區(執行緒共享));
Java虛擬機堆疊存放著許多堆疊幀,每個方法執行的同時都會創建一個堆疊幀(stack Frame),方法執行完成,出堆疊;整個程序可以看成在虛擬機堆疊的入堆疊出堆疊程序;
幀結構:1. 區域變數表,2. 運算元堆疊,3.動態鏈接,4.方法出口

發現java檔案與對應位元組碼檔案的關系
public int compute() { #java檔案
int a = 0;
int b = 1;
int c = (a + b) * 10;
return c;
}
public int compute(); #對應的位元組碼
Code:
0: iconst_0 #
1: istore_1
2: iconst_1
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: bipush 10
9: imul
10: istore_3
11: iload_3
12: ireturn
GC初識
GC是什么,作業方式和區域等?
- 運行一個GCtest類()
public class GCtest {
//100kb
byte[] a = new byte[1024 * 100];
public static void main(String[] args) throws InterruptedException {
ArrayList<GCtest> gCtests = new ArrayList<>();
while (true) {
gCtests.add(new GCtest());
Thread.sleep(10);
}
}
}
- 使用JAVA自帶GC工具=>jvisualvm,命令列輸入jvisualvm啟動

- 點開GCtest類,查看GC運行情況

- 程式一直運行,堆記憶體不足,報錯(OOM)
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.gjf.GCtest.<init>(GCtest.java:13)
at com.gjf.GCtest.main(GCtest.java:17)
JVM調優
Arthas(阿爾薩斯)是阿里巴巴開源的 Java 診斷工具,深受開發者喜愛,
- 為什么要JVM調優?
減少GC,特別是FullGC
- 物件優先在堆的 Eden 區分配
- 大物件直接進入老年代
- 長期存活的物件將直接進入老年代. 當 Eden 區沒有足夠的空間進行分配時,虛擬機會執行一次 Minor GC.Minor Gc 通 常發生在新生代的 Eden 區,在這個區的物件生存期短,往往發生 Gc 的頻率較高, 回收速度比較快;Full Gc/Major GC 發生在老年代,一般情況下,觸發老年代 GC 的時候不會觸發 Minor GC,但是通過配置,可以在 Full GC 之前進行一次 Minor GC 這樣可以加快老年代的回收速度,
- GC 的兩種判定方法:
- 參考計數法:指的是如果某個地方參考了這個物件就+1,如果失效了就-1,當為 0 就 會回收但是 JVM 沒有用這種方式,因為無法判定相互回圈參考(A 參考 B,B 參考 A) 的情況
- 參考鏈法: 通過一種 GC ROOT 的物件(方法區中靜態變數參考的物件等-static 變 量)來判斷,如果有一條鏈能夠到達 GC ROOT 就說明,不能到達 GC ROOT 就說明 可以回收
JVM的垃圾回收器和記憶體分配
- 串行垃圾回收器
是指使用單執行緒進行垃圾回收的回收器,每次回收只有一個作業執行緒(對并行能力比較弱的電腦,運行性能較好)
注意:串行回收器運行時,所有
應用程式的執行緒都停止作業,屬于獨占式的垃圾回收方式;執行緒進行等待的現象稱為–>Stop-The-World,造成非常糟糕的用戶體驗;
- 并行垃圾回收器
多個執行緒同時進行垃圾回收,適合并行能力強的計算機;
- 新生代ParNew回收器,只是簡單將串行回收器多執行緒化,也是獨占式的回收器
- 還有其他的回收器,都
關注吞吐量,其中包括復制演算法,標記壓縮演算法等回收演算法注意:還是會造成執行緒等待現象–>Stop-The-World(STW),但是減少垃圾回收的停頓時間就會同時減小系統的吞吐量
- CMS回收器(jdk1.8以前)
CMS回收器主要關注
系統的停頓時間,并發標記清除,是一個基于標記清除演算法的回收器;CMS掉作業程序相對復雜,不是獨占式的回收器,作業程序中,應用程式仍然作業;
不會等到堆記憶體飽和后進行回收,而是到達一定閾值才開始垃圾回收
引數 說明 -XX:CMSInitiatingOccupancyFraction 默認堆老年代使用達到68%,執行CMS回收,如果在執行程序中記憶體不足,就會啟動串行回收器進行垃圾回收,應用程式將完全中斷; 根據此引數進行調優,增大閾值可以降低CMS的觸發,減少老年代的回收次數;如果記憶體使用增長很快,應該降低閾值; CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的垃圾收集器,從名字可以看出,CMS 是基于標記-清除演算法的,它的運作程序主要分為四個步驟:
- 初始標記(CMS initial mark):STW,標記GC Roots能直接關聯到的物件,速度很快,單執行緒
- 并發標記(CMS concurrent mark):從GC Roots的直接關聯物件開始遍歷整個物件圖的程序,耗時較長,不需要停頓用戶執行緒
- 重新標記(CMS remark):STW,修正并發標記期間,因用戶程式繼續運作而導致標記發生變動的那一部分物件的標記記錄(增量更新),時間稍長于初始標記,但遠低于并發標記
- 并發清除(CMS concurrent sweep):清除已死亡物件,因為不需要移動物件,所以與用戶執行緒是并發的關系
G1回收器(JDK1.7推出,1.9默認)
Garbages First(G1)垃圾回收器,作為CMS的長久替代方案,使用了全新的磁區演算法;
- 并行性:G1回收期間,多個GC執行緒可以同時作業
- 并發性:G1可以跟應用程式交替執行的能力,不會在回收期間完全阻塞應用程式
- 分代GC:G1依然是一個分代回收器,與之前回收器不同,G1兼顧年輕代和老年代,如CMS作業在老年代
- 空間整理:G1回收程序中,會適當進行物件移動,如CMS若干次GC后,CMS必須進行一次碎片整理,但是G1,每次回收都會有效復制物件,減少碎片空間;
- 可預見性:由于磁區原因,G1只對選取的部磁區域進行回收,可以很好的控制全域停頓;
新生代GC(主要回收eden區和survivor區,復制演算法)
eden區被占滿,新生代GC啟動,ednn區會被全部回收,至少存在一個survivor區,老年代區域增大;

G1的并發標記周期
- 初始化標記:標記從根節點直接可達的物件,發生一次新生代GC,產生全域停頓
- 根區域掃描:eden區已經清空,掃描標記由survivor區直接可達的老年代區域,無法與新生代GC同時執行
- 并發標記:與CMS類似,全域標記堆中存活的物件
- 重新標記:G1會使用(SATB)演算法,為存活物件創建快照,有助于加速重新標記速度
- 獨占清理:計算各個區域存活物件和GC回收比例,識別可供混合回收的區域
- 并發清理:會識別并清理空閑區域,并發清理不會引起停頓

混合回收
在并發標記后就知道哪個區域的垃圾較多,G1就會優先回收垃圾比例高的區域

FullGC
堆記憶體不足時,就會觸發FullGC,
對于并行回收器的FullGC之前,都會觸發一次新生代GC
使用system.gc()方法,觸發一次GC,在并行回收器中,FullGC之前會發生一個新生代GC,這樣可以縮短停頓時間(STW)
物件進入老年代
- 新物件在eden區
- JVM提供一個引數來控制新生代物件的年齡(MaxTenuringThreshold),默認初始值為15,新生代物件最多經歷15次GC就可以到老年代
- 新生代無法容納的大物件直接進入老年代,可以通過調節引數(PretenureSizeThreshold),設定物件晉升到老年代的閾值;
- 如使用的JVM測驗引數(使用開發工具可以自定義)
-Xmx32m -Xms32m -XX:+UseSerialGC -XX:+PrintGCDetails
G1和CMS的區別
G1具備如下特點:
- 并行與并發:G1能充分利用多CPU、多核環境下的硬體優勢,使用多個CPU來縮短Stop-the-world停頓的時間,部分其他收集器原來需要停頓Java執行緒執行的GC操作,G1收集器仍然可以通過并發的方式讓Java程式繼續運行,
- 分代收集
- 空間整合:與CMS的標記-清除演算法不同,G1從整體來看是基于標記-整理演算法實作的收集器,從區域(兩個Region之間)上來看是基于“復制”演算法實作的,但無論如何,這兩種演算法都意味著G1運作期間不會產生記憶體空間碎片,收集后能提供規整的可用記憶體,這種特性有利于程式長時間運行,分配大物件時不會因為無法找到連續記憶體空間而提前觸發下一次GC,
- 可預測的停頓:這是G1相對于CMS的一個優勢,降低停頓時間是G1和CMS共同的關注點,
CMS 用于老年代的回收,而 G1 用于新生代和老年代的回收,
G1 使用了 Region 方式對堆記憶體進行了劃分,且基于標記整理演算法實作,整體減少了垃圾碎片的產生,CMS使用“標記-清理”演算法會產生大量的空間碎片;

相關引數


實戰案例
- 擴大堆以提升系統性能
-Xmx512m -XX:MaxPermSize=32M -Xloggc:gc:gc.log -XX:+PrintGCDetails
監控和診斷性能問題
- Linux下性能監控命令和工具
- JConsole,Visual VM等工具使用
對癥下藥
記憶體溢位(OOM)
- 直接記憶體溢位
- 堆溢位
- 永久區(元資料區)溢位
- GC效率低引起OOM
待續,如何看問題做性能優化
補充JVM基礎知識
程式計數器
記憶體空間小,執行緒私有,位元組碼解釋器作業是就是通過改變這個計數器的值來選取下一條需要執行指令的位元組碼指令,分支、回圈、跳轉、例外處理、執行緒恢復等基礎功能都需要依賴計數器完成
如果執行緒正在執行一個 Java 方法,這個計數器記錄的是
正在執行的虛擬機位元組碼指令的地址;如果正在執行的是 Native 方法,這個計數器的值則為 (Undefined),此記憶體區域是唯一一個在 Java 虛擬機規范中沒有規定任何 OutOfMemoryError 情況的區域,
執行緒私有,生命周期和執行緒一致,描述的是 Java 方法執行的記憶體模型:每個方法在執行時都會床創建一個堆疊幀(Stack Frame)用于存盤區域變數表、運算元堆疊、動態鏈接、方法出口等資訊,每一個方法從呼叫直至執行結束,就對應著一個堆疊幀從虛擬機堆疊中入堆疊到出堆疊的程序,**區域變數表:**存放了編譯期可知的各種基本型別(boolean、byte、char、short、int、float、long、double)、物件參考(reference 型別)和 returnAddress 型別(指向了一條位元組碼指令的地址),第一位放的是0,代表this當前物件;
StackOverflowError:執行緒請求的堆疊深度大于虛擬機所允許的深度,
OutOfMemoryError:如果虛擬機堆疊可以動態擴展,而擴展時無法申請到足夠的內存,本地方法堆疊
區別于 Java 虛擬機堆疊的是,Java 虛擬機堆疊為虛擬機執行 Java 方法(也就是位元組碼)服務,而本地方法堆疊則使用 Native 方法,也會有 StackOverflowError 和 OutOfMemoryError 例外,
Java 堆
對于絕大多數應用來說,這塊區域是 JVM 所管理的記憶體中最大的一塊,執行緒共享,
主要是存放物件實體和陣列,內部會劃分出多個執行緒私有的分配緩沖區(Thread Local Allocation Buffer, TLAB),可以位于物理上不連續的空間,但是邏輯上要連續,(GC,G1主要作業區域)OutOfMemoryError:如果堆中沒有記憶體完成實體分配,并且堆也無法再擴展時,拋出該例外,
方法區
屬于
共享記憶體區域,存盤已被虛擬機加載的類資訊、常量、靜態變數、即時編譯器編譯后的代碼等資料,
一鍵三連
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/276176.html
標籤:java
上一篇:Spring5學習筆記
下一篇:Java運算子的知識點與代碼匯總

