簡介
Jvm 系列一:Java類的加載機(jī)制
Jvm系列二:JVM內(nèi)存結(jié)構(gòu) --內(nèi)存泄漏與內(nèi)存溢出
Jvm系列三:GC算法 垃圾收集器
Jvm系列四:jvm調(diào)優(yōu)-命令篇
Jvm系列五:java GC分析
Java技術(shù)體系主要由 class文件格式 + jvm + javaApi + java框架 構(gòu)成
不了解虛擬機(jī)運(yùn)行的特性原理,就無法寫出適合虛擬機(jī)運(yùn)行和自優(yōu)化的代碼
虛擬機(jī)特性和調(diào)優(yōu)方法
。。。
一、 Java類的加載機(jī)制
類加載機(jī)制是指:
1.將類的.class文件中的二進(jìn)制數(shù)據(jù)讀入到內(nèi)存中,將其放在運(yùn)行時數(shù)據(jù)模型的方法區(qū)中,=>保存類的數(shù)據(jù)結(jié)構(gòu)
2.然后在堆區(qū)創(chuàng)建一個** java.lang.Class對象** => 作為對方法區(qū)中這些數(shù)據(jù)的訪問入口。
3.并且向Java程序員提供了訪問方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)的接口。
加載階段完成后,虛擬機(jī)外部的二進(jìn)制字節(jié)流就按照虛擬機(jī)所需的格式存儲在方法區(qū)之中,
而且在Java堆中也創(chuàng)建一個 java.lang.Class類的對象,
這樣便可以通過該對象訪問方法區(qū)中的這些數(shù)據(jù)。
當(dāng)類被加載之后,系統(tǒng)為之生成一個對應(yīng)的Class對象
接著將會進(jìn)入連接階段,連接階段負(fù)責(zé)把類的二進(jìn)制數(shù)據(jù)合并到JRE中
================
加載.class文件的方式
本地、
網(wǎng)絡(luò)、
專有數(shù)據(jù)庫提取.class文件、
將Java源文件動態(tài)編譯為.class文件
從zip,jar等歸檔文件中加載.class文件
參考鏈接 :
https://zhuanlan.zhihu.com/p/60684596
https://zhuanlan.zhihu.com/p/44670213
https://zhuanlan.zhihu.com/p/61775388 內(nèi)存
https://blog.csdn.net/m0_38075425/article/details/81627349 比較詳細(xì)
二、JVM內(nèi)存結(jié)構(gòu)
運(yùn)行時數(shù)據(jù)區(qū)域:了解創(chuàng)建和銷毀時機(jī),有的區(qū)域隨著虛擬機(jī)進(jìn)程啟動而存在,有的區(qū)域隨著用戶線程的啟動和結(jié)束而創(chuàng)建和銷毀
JVM內(nèi)存結(jié)構(gòu)主要有三大塊: 堆內(nèi)存、方法區(qū)和棧
堆內(nèi)存:
a. 堆內(nèi)存是JVM中最大的一塊由年輕代和老年代組成1:2
b. 年輕代內(nèi)存又被分成三部分(8:1:1): Eden空間、From Survivor空間、To Survivor空間
堆內(nèi)存模型+GC回收
<<<<<<<<<<<<<>>>>>>>>>>>>>>>
方法區(qū):
方法區(qū)存儲類信息、常量、靜態(tài)變量等數(shù)據(jù),是線程共享等區(qū)域->Non-Heap(非堆)
棧:
java虛擬機(jī)棧和本地方法棧,主要用于方法的執(zhí)行
<這里應(yīng)該有張圖 JVM和系統(tǒng)調(diào)用之間的關(guān)系>
共享區(qū)
Java堆(Heap)
- Java Heap 是Java虛擬機(jī)管理內(nèi)存中最大的一塊
- Java堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動時創(chuàng)建
- 此內(nèi)存區(qū)域的唯一目的: 存放對象實例 ,幾乎所有的對象實例都在這里分配內(nèi)存
- Java堆是垃圾收集器管理的主要區(qū)域,也被稱為“GC堆” -- 分代收集算法
- 如果在堆中沒有內(nèi)存完成實例分配,并且堆也無法再擴(kuò)展,將會拋出OOM Error
方法區(qū)(Method Area)
- 方法區(qū)與Java堆一樣,是各個線程共享的內(nèi)存區(qū)域
- 它用于儲存已被虛擬機(jī)加載的類信息,常量,靜態(tài)變量,即時編譯器編譯后的代碼數(shù)據(jù)
- 除了和Java堆一樣不需要連續(xù)的內(nèi)存和可以選擇固定大小或者可擴(kuò)展外,還可以選擇不實現(xiàn)垃圾收集
- 方法區(qū)有時被稱為持久代(PermGen)
運(yùn)行時常量池:類信息 + 常量池-》 編譯期生成的各種字面量和符號引用
參考鏈接:https://www.cnblogs.com/natian-ws/p/10749164.html
高版本有變化
方法的執(zhí)行都伴隨著線程的。
原始類型的本地變量以及引用都存放在線程棧中。而引用關(guān)聯(lián)的對象比如String,都存在在堆中
線程私有
程序計數(shù)器(Program Counter Register)
程序計數(shù)器(Program Counter Register)是一塊較小的內(nèi)存空間
它的作用可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器---> 執(zhí)行哪一行字節(jié)碼
字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令
分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個計數(shù)器來完成
java虛擬機(jī)的多線程是通過線程輪流切換并分配處理器執(zhí)行時間的方式來實現(xiàn)
1. 在任何一個確定的時刻,一個處理器(對于多核處理器來說是一個內(nèi)核)只會執(zhí)行一條線程指令 -->某個線程執(zhí)行完后
2. 線程切換后能恢復(fù)到正確的執(zhí)行位置,每一條線程都需要一個獨立的程序計數(shù)器
3. 各條線程之間的計數(shù)器互不影響,獨立儲存
4. 這類內(nèi)存區(qū)域為“線程私有”的內(nèi)存
5. 線程執(zhí)行java方法,計數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址;
6. 如果正在執(zhí)行的是Native方法,這個計數(shù)器值則為空(Undefined)
此內(nèi)存區(qū)域是唯一一個在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域
JVM棧(JVM Stacks)
與程序計數(shù)器一樣,Java虛擬機(jī)棧(Java Virtual Machine Stacks)也是線程私有
每一個方法從調(diào)用開始到執(zhí)行完成的過程,就對應(yīng)著一個棧幀在虛擬機(jī)棧里面從入棧到出棧的過程。
1. 它的生命周期與線程相同。
2. 虛擬機(jī)棧描述的是Java**方法執(zhí)行**的內(nèi)存模型
3. 每個方法執(zhí)行的時候都會同時創(chuàng)建一個棧幀(Stack Frame)用于儲存
局部變量表:
1. 存放了編譯器可知的各種基本數(shù)據(jù)類型,在棧中分配多少內(nèi)存空間,在編譯期就已經(jīng)確定了,索引訪問
stackoverflowError
outofmemoryError3
2. 對象引用
3. returnAddress類型
4. 系統(tǒng)不會為局部變量賦予初始值(實例變量和類變量都會被賦予初始值)。也就是說不存在類變量那樣的準(zhǔn)備階段。
操作數(shù)棧:
1. Java虛擬機(jī)的解釋執(zhí)行引擎被稱為"基于棧的執(zhí)行引擎",其中所指的棧就是指-操作數(shù)棧。
動態(tài)鏈接:
1. 由于棧幀是用于方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu)。所以每個棧幀都包含一個指向運(yùn)行時常量池中該棧幀所屬方法的引用
2. 在class文件的常量池中有大量的符號引號。這些符號引用有一部分在類加載階段或者第一次使用的時候就直接轉(zhuǎn)化為直接引用(靜態(tài)解析)。有的則在每一次運(yùn)行期間轉(zhuǎn)化為直接引用(動態(tài)連接)。
方法出口等信息
4. 每一個方法被調(diào)用直至執(zhí)行完成的過程,就對應(yīng)一個棧幀在虛擬機(jī)出入棧的過程
本地方法棧(Native Method Stacks)
虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù)
本地方法棧是為虛擬機(jī)使用到Native方法服務(wù)。
直接內(nèi)存
使用Native函數(shù)直接直接分配堆外內(nèi)存 由堆中DirectByteBuffer操縱
避免在堆內(nèi)存和Native內(nèi)存來回復(fù)制
JAVA 垃圾回收
- 垃圾回收算法
標(biāo)記清除
年老代
復(fù)制算法
年輕代
標(biāo)記壓縮
分代收集算法
Eden => from區(qū) => to區(qū) => 年老區(qū)
(新生代進(jìn)行回收,Minor GC,不影響老年代)
(年老滿了就要進(jìn)行FULL GC,也叫Major GC 包括新生代和年老代)
應(yīng)該盡量減少FULL GC,因為慢 卡
導(dǎo)致的原因:年老代被寫滿,永久代被寫滿,顯示調(diào)用System.gc()
- 虛擬機(jī)遇到一條New指令時的操作
檢查這個指令的參數(shù),能否在常量池中一個類的符號引用
檢查這個符合引用代表的類是否已經(jīng)被加載、解析、初始化
如果沒有執(zhí)行類加載過程
加載之后,分配內(nèi)存,內(nèi)存大小在編譯時就已確定
- 根據(jù)異常信息快速判斷內(nèi)存溢出區(qū)域
-XX:+HeapDumpOutOfMemoryError
如果不存在內(nèi)存泄漏:GcRoot引用鏈 - > 查看內(nèi)存是否必須活著 - > 查看參數(shù)是否調(diào)大
集群同步時,導(dǎo)致內(nèi)存溢出
使用直接內(nèi)存,導(dǎo)致內(nèi)存溢出
外部命令,導(dǎo)致內(nèi)存溢出
不恰當(dāng)數(shù)據(jù)結(jié)構(gòu),導(dǎo)致內(nèi)存溢出
- 數(shù)據(jù)分析
運(yùn)行日志、異常堆棧、GC日志、線程快照、堆轉(zhuǎn)儲快照(heapdump/prof)
cpu 寄存器 高速緩存 工作內(nèi)存 主內(nèi)存
內(nèi)核 用戶
參考鏈接:
https://zhuanlan.zhihu.com/p/34426768
https://www.zhihu.com/question/293352546/answer/48523571 FUllGC YGC導(dǎo)致的問題
參考書籍:《深入理解Java虛擬機(jī):JVM高級特性與最佳事件》-周志明