【JVM 知識體系框架】(不斷更新)

JVM 內存分佈

  • 線程共享數據區:

方法區->類信息,靜態變量
堆->數組對象java

  • 線程隔離區

虛擬機棧-> 方法
本地方法棧->本地方法庫 native面試

  • 堆、程序計數器
  • JVM 運行數據

程序計數器

線程隔離 ,比較小的內存空間,當前線程所執行的字節碼的行號
線程是一個獨立的執行單元,由 CPU執行
惟一沒有 OOM 的地方,由虛擬機維護,因此不會出現 OOM算法

虛擬機棧

執行的是Java方法segmentfault

方法的調用就是棧幀入虛擬機棧的過程
棧幀:局部變量表(變量) 、操做數棧(存放a+b的結果 )、 動態連接(對對象引用的地址),方法出口(return的值)
線程請求的棧深度大於虛擬機所容許的深度StackOverflowError數組

本地方法棧

執行的是 native 方法的一塊 java內存區域,同樣有 棧幀
hotspot將 Java 虛擬機棧和本地方法棧合二爲一
jvm標準是 java 虛擬機棧和本地方法棧分開安全

java內存中存放對象實例的區域,幾乎全部的對象實例都在這裏分配
全部線程共享
新生代、老年代
jmap -heap pid;多線程

方法區

各個線程共享的內存區域
存儲已被虛擬機加載的類的信息、常量、靜態變量、即時編譯器編譯後的代碼等數據
Hotspot用永久代實現方法區(讓垃圾回收器能夠管理方法區),對常量池的回收和卸載
方法區會拋出 OOM,當他沒法知足內存分配需求時併發

運行時常量池

運行時常量池是方法區的一部分,Class 中除了字段、方法、接口的 常量池,存放編譯器生成的字面量和符號引用,這部份內容由類加載後進入方法區的運行時常量池中存放。app

StringTable是HashSet結構
方法區的一部分,受到方法區的限制,依然會 OOMjvm

Java 對象建立過程


<init> -> static方法 static代碼塊

  1. new 指令,判斷在常量池中有沒符號引用,有則已被加載過
  2. 判斷類是否被加載、解析、初始化
  3. 爲新生對象在java堆裏分配內存空間

1) 指針碰撞(內存比較整齊)

步驟:1. 分配內存 2. 移動指針,非原子步驟可能出現併發問題,Java虛擬機採用 CAS 配上失敗重試的方式保證更新操做的原子性
2)空閒列表(內存比較亂)
存儲堆內存空閒地址
步驟:1.分配內存 2. 修改空閒列表地址 非原子步驟可能出現併發問題,Java虛擬機採用 CAS 配上失敗重試的方式保證更新操做的原子性

  1. 將分配到內存空間都初始化零值
  2. 設置對象頭相關信息 (GC分代年齡、對象的HashCode、元數據信息)
  3. 執行<init>方法

Java 對象內存佈局

對象屬性的值->實例數據
對象頭 64 位機器存 64 位,32 位機器存 32 位,8 的倍數

Java 對象的訪問

  1. 直接指針訪問

  1. 句柄訪問


對比:

  1. 訪問效率:直接指針訪問效率高(hotspot採用這種方式)
  2. 垃圾回收:句柄訪問效率高,垃圾回收只用更新句柄池,而直接指針訪問方式則要更新 reference地址

垃圾回收算法

引用計數器


當對象實例分配給一個變量時,該變量計數設置爲 1,當任何其餘變量被賦值爲這個對象的引用的時,計數+1 (a =b,則b的引用對象實例計數器+1),當一個對象實例的某個引用超過了生命週期(方法執行完)或者被設置爲一個新值,則該對象的實例引用計數器 -1
沒法解決循環引用
可達性分析
GC Root (虛擬機棧中的引用的對象、本地方法棧中引用的對象、方法區靜態屬性引用的對象、方法區常量引用的對象)

標記-清除


標記須要回收的對象,在標記完成後統一回收
不足:
1.效率問題,標記清除 2 個過程效率都不高
2.空間問題,標記清除後產生大量不連續的內存碎片,碎片過多當程序須要分配較大的對象時,沒法找到足夠的連續內存而不得不提早觸發一次垃圾回收動做

標記-複製


內存塊 A存活的對象複製到內存塊 B (Survivor to)裏,而後將內存塊A (Eden + Survivor from)清空,
只有少部分對象移動,更多的對象是要被回收的
Eden:Survivor from:Survivor to=8:1:1
98%對象「朝生夕亡」,新生代可用內存容量 90%(80%+10%),98%的對象可回收是通常狀況,當小於 90%的對象被回收的時候(10%以上的對象存活時),則 Survivor to 空間不夠,則須要依賴老年代進行分配擔保

標記-整理


老年代不適合複製算法

  1. 複製操做增多 2. 額外 50%空間浪費 3. 常常須要額外的空間分配擔保 4.可能老年代中對象 100% 存活

步驟:

  1. 標記
  2. 整理 將存活的對象移動到一端(左上方),從不規整變成規整,而後直接清理掉邊界之外的內存

垃圾收集器

Serial 收集器


單線程垃圾回收器,用戶線程到安全點先暫定,而後 GC 線程單線程串行進行,等 GC 線程回收完,而後用戶線程再繼續
特色:Stop the world
場景:桌面應用 (gc時間短)
用於新生代,client 端

ParNew 收集器

Serial收集器的多線程版本

用於新生代,惟一能和CMS 收集器配合工做,運行在 server 模式下
-XX:ParallelGCThreads 限制垃圾收集器線程數 = CPU 核數(過多會致使上下文切換消耗)
並行:多條垃圾收集線程並行工做,用戶線程仍然處於等待狀態
併發:用戶線程與垃圾收集器同時執行,用戶線程和垃圾線程在不一樣 CPU 上執行

Parallel Scavenge 收集器

新生代收集器,複製算法,並行的多線程收集器
關注吞吐量優先的收集器(吞吐量 = CPU 運行用戶代碼執行時間/CPU 執行總時間 ,好比: 99%時間執行用戶線程,1%時間回收垃圾,這時吞吐量爲 99%)高吞吐量能夠高效率利用 CPU 時間,儘快完成程序的運算任務,適合在後臺運算而不須要太多的交互任務
CMS 關注縮短垃圾回收停頓時間,適合與用戶交互的程序,良好的響應速度能提高用戶體驗
-XX:MaxGCPauseMillis 參數 GC 停頓時間,參數太小會頻繁 GC
-XX:GCTimeRatio 參數,默認 99%(用戶線程時間佔 CPU 總時間的 99%)

Serial Old 收集器

是Serial 收集器的老年代版本
單線程老年代收集器,採用「標記-整理」算法

Parallel Old 收集器

是 Parallel Scavenge收集器的老年代版本
多線程老年代收集器,採用「標記-整理」算法

CMS 收集器

獲取最短回收停頓時間爲目標的收集器,採用「標記-清除」算法,用於互聯網、B/S 系統重視響應的系統

步驟:

  1. 初始標記(不和用戶線程一塊兒運行,耗時短)—— 標記一下 GC Roots 能直接關聯到的對象,速度很快
  2. 併發標記(和用戶線程一塊兒運行,耗時長) —— 併發標記階段就是進行 GC RootsTracing,尋找 GC 引用鏈
  3. 從新標記(不和用戶線程一塊兒運行,耗時短)—— 爲了修正併發標記期間因用戶線程致使標記產生變更的標記記錄
  4. 併發清除(和用戶線程一塊兒運行,耗時長)—— 掃描整個內存區域

缺點 :

  1. 對 CPU 資源很是敏感(併發標記階段時間長,佔用用戶線程 CPU 時間)
  2. 沒法處理浮動垃圾(程序在進行併發清除時,用戶線程所產生的新垃圾)
  3. 標記-清除產生空間碎片

G1 收集器

面向服務端應用的垃圾收集器

Region->Remembered Set (解決 循環引用 )
檢查 Reference (程序對reference類型寫操做,檢查 reference 引用類型)
步驟:

  1. 初始標記 —— 標記 GC Roots 能直接關聯到的對象
  2. 併發標記 —— 從 GC Root 開始對堆中對象進行可達性分析,找出存活對象 ,這一階段耗時較長,但可與用戶程序併發執行
  3. 最終標記(Remembered Set Logs->Remembered Set)—— 修正在併發標記期間因用戶程序繼續運做而致使標記產生變更的那一部分標記記錄,虛擬機將這段時間對象變化記錄在線程 Remembered Set Logs裏面,最終標記階段須要把 Remembered Set Logs的數據合併到 Remembered Set中
  4. 篩選回收(Live Data Counting and Evacuation)—— 只須要掃描 Remembered Set

優點:

  1. 基於「標記-整理」 爲主和 Region 之間採用複製算法實現
  2. 可預測停頓,下降停頓時間,但G1 除了追求低停頓外,還能創建可預測的停頓時間模型
  3. G1 直接對 Java 堆中的 Region 進行回收(新生代、老年代再也不物理隔離,他們都是一部分 Region)
  4. 可預測的停頓時間模型,G1 跟蹤各個 Regions 裏面的垃圾堆積的價值大小(回收所得到的空間大小以及回收所需時間的經驗值),在後臺維護一個優先列表,每次根據容許的收集時間,優先回收價值最大的 Region

堆內存分配

Java 堆分佈圖

對象分配的規則:

  1. 對象主要分配在新生代的 Eden 區 ( Eden區,From 區存活對象複製到 To區,Eden區,From區被回收,而後 To區對象拷貝到From區,再進行下一次垃圾回收 )
  2. 若是啓動了本地線程分配緩衝,將按線程優先在 TLAB 上分配
  3. 少數狀況下也可能直接分配到老年代 (放不下From和To區的都直接放到老年代)

大對象分配

大對象是指須要大量連續內存空間的 Java 對象,最典型的大對象是是那種很長的字符串以及數組
-XX:PretenureSizeThreshold 設置大於該值的對象直接分配在老年代,避免在 Eden 區以及 2 個Survivior區之間發生大量的內存複製

逃逸分析和棧上分配

逃逸分析:分析對象動態做用域,當一個對象在方法中被定義後,它可能被外部方法所引用,稱爲方法逃逸。甚至還有可能被外部線程訪問到,好比賦值給類變量或其餘線程中訪問的實例變量,稱爲線程逃逸。
棧上分配:把方法中的變量和對象直接分配到棧上,方法執行完後自動銷燬,不須要垃圾回收介入,從而提升系統性能
-XX:+DoEscapeAnalysis 開啓逃逸分析(jdk1.8默認開啓 )
-XX:-DoEscapeAnalysis 關閉逃逸分析

虛擬機調優命令

  1. ps -ef | grep java
  2. jps -m(啓動參數) -l(類名) -v (JVM 參數)
  3. jstat -gc 27660 250 20 監視虛擬機各類運行狀態信息

  1. jinfo 27660 查看和調整進程虛擬機(未被顯示指定的)參數信息
  2. jmap 生成堆轉儲快照 -XX:+HeapDumpOnOutOfMemoryError

jmap -heap 9366;
jmap -histo 9366 | more; 顯示堆中對象統計
jmap -dump:format=b,file=/Users/mousycoder/Desktop/a.bin 9366 生成dump文件
-Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/mousycoder/Desktop/
jhat /Users/mousycoder/Desktop/java_pid9783.hprof 圖形分析Heap
select s.toString() from java.lang.String s where (s.value != null && s.value.length > 1000 )

  1. jstack 線程快照(虛擬機內每一條線程正在執行的方法堆棧的集合,主要用於定位線程問題)

shutdownHook 在關閉以前執行的任務
jstack -l -F pid 強制輸出

線程狀態

  1. NEW
  2. RUNNABLE
  3. BLOCKED 一個正在阻塞等待一個監視器的線程處於這個狀態(Entry Set)被動的阻塞
  4. WAITING 一個正在無限期等待另外一個線程執行一個特別的動做的線程處於這一狀態 (Wait Set)主動顯式申請的阻塞
  5. TIMED_WAITING 一個正在限時等待另外一個線程執行一個動做的線程處於這一狀態
  6. TERMINATED 線程完成一個excution

JConsole

基於 JMX 的可視化監視、管理工具
開啓 JMX 端口
nohup java -Xms800m -Xmx800m -Djava.rmi.server.hostname=192.168.1.250 -Dcom.sun.management.jmx
remote.port=1111 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -jar hc-charging-
server.jar &

互聯網開發流程

Jconsole 內存分析思考過程

FullGC

Minor GC:當 Eden 區滿,觸發 Minor GC
FullGC:

  1. 調用System.gc() 建議虛擬機進行 Full GC,可經過 -XX:+DisableExplicitGC 來進制 RMI 調用System.gc()
  2. 老年代空間不足 大對象直接進入老年代,長期存活的對象進入老年代,當執行 Full GC後空間仍然不足,則拋出 OutOfMemoryError,爲了不上面緣由引發 Full GC,調優時儘可能作到讓對象在 Minor GC 階段被回收,讓對象在新生代多存活一段時間以及不要建立過大的對象和數組
  3. 空間分配擔保失敗 使用複製算法的 Minor GC 須要老年代的內存空間做爲擔保,若是出現了 HandlePromotionFailure 擔保失敗,則會觸發 Full GC

建議:

  1. 減小-Xmx大小,縮短 GC 時間(堆內存設置越大,Full GC 時間越長,停頓時間也會越長)
  2. 集羣部署

互聯網問題

  1. 白名單問題

解決方法:list.contain->set.contain->布隆過濾器(用戶量大和用戶量小系統解決方案不同)

  1. 死鎖

解決方法:jstack 以及 new thread帶上名稱

  1. 堆內存泄露

FullGC 出現正常頻率爲一天 1~2 次
解決方案:jmap , heap dump on oom + jhat

  1. 堆外內存泄露

heap堆使用率很低,可是有 OOM 以及 Full GC
解決方法:btrace ,directMemory增大

  1. 不對等數據

解決方案: MQ

Class 文件

  • class 文件是一組以 8 位字節爲基礎單位的二進制流,各個數據項目嚴格按照順序緊湊的排列在class文件中 ,中間沒有添加任何分隔符
  • 當遇到 8 位字節以上的空間數據項時,則會按照高位在前的方式分隔成若干個 8 位字節進行存儲
  • class 文件中有 2 種數據類型,無符號數和表

結構:
1.魔數
2.class 文件版本
3.常量池
4.訪問標誌
5.類索引、父類索引、接口索引集合
6.字段表集合
7.方法表集合
8.屬性表集合

魔數

CAFEBABE

常量池



訪問標誌

類索引

字段表集合


方法表


屬性表

字節碼指令


加載存儲指令

運算指令

類型轉換指令

對象建立與訪問指令

操做數棧管理指令

控制轉移指令


方法調用和返回指令


異常處理指令

同步指令


類加載機制

類加載時機

初始化

不被初始化狀況

類加載過程

加載

加載源

驗證

準備

解析

類、接口解析

字段的解析

類方法解析

接口方法解析

初始化




類加載器

分類

自定義類加載器優點

雙親委派模型


運行時棧幀結構



局部變量表

操做數棧


動態鏈接

棧幀指向該棧所屬方法引用

方法的返回地址和附加信息

方法返回地址

附加信息

方法調用

解析


invokestatic、invokespecial、invokevirtual、invokeinterface、invokedynamic

靜態分派調用

編譯期間決定,主要是重載,支持最優選擇重載

動態分派調用

針對重寫

## 動態語言支持

happens-before

重排序


多線程中重排序用同步來保證,否則會出現莫名其妙問題

鎖內存語義


final


JVM調優

  • newRadio = 2/ 新生代老年代比例
  • Xss5m 設置最大調用深度

GC ROOT


學習祕籍

  1. 知識體系
  2. 面試前看下知識體系導圖
  3. 堅持就勝利
相關文章
相關標籤/搜索