JVM學習文檔
引言
1.什麼是JVM?
2.學習JVM有什麼用呢?
3.常見的JVM有哪些?
4.學習線路
JVM介紹
定義:Java Virtual Machine = java 程序的運行環境 ( java二進制字節碼的運行環境 )。 JVM是一套規範,由不同的廠商實現。
好處:
1.使得java程序可以一次編寫,到處運行,Java虛擬機從軟件層面屏蔽了不同操作系統之間的底層差異。
2.提供了自動內存管理機制,垃圾回收功能。
3.數組下標越界檢查。
4.多態機制的實現。
比較:
面試題:jvm、jdk、jre 三者之間的關係? 包含關係。
jdk = jvm + 基礎類庫+編譯工具
jre = jvm + 基礎類庫
學習JVM有什麼用?
1.面試。
2.理解底層的實現原理。(更好理解:自動拆裝箱、動態代理的原理。)
3.中高級程序員的必備既能!
常見的JVM:
此次學習以HotSpot vm 爲準。
學習線路圖
一、JVM內存結構
1.程序計數器
1.1 作用
記住下一條JVM指令的執行地址。
1.2 特點
1.是線程私有的。每一個線程都會有一個程序計數器。
2.唯一一個不會存在內存溢出結構。
2.虛擬機棧
2.1 定義
每一條線程運行時需要的總內存空間,稱爲虛擬機棧。也稱爲 線程棧。
2.2 特點
一個線程棧由多個【棧幀】組成。當需要調用方法時,線程棧會爲該方法開闢一塊棧幀空間,方法執行完後棧幀彈棧並釋放棧幀內存。
棧幀:每個方法運行時需要的內存。
活動棧幀:指的就是正在執行的那個方法。
2.3 問題辨析
1.垃圾回收是否涉及棧內存?
不涉及,線程棧內存僅涉及到方法的一次次的調用與釋放。
2.棧內存分配越大越好嗎?
棧內存分配越大,線程數就會變少。
3.方法內的局部變量是否線程安全?
如果方法內的局部變量沒有逃離方法的作用範圍,是線程安全的。
如果是局部變量引用了對象,並逃離方法的作用範圍,需要考慮線程安全問題。
2.4 棧內存溢出問題
棧內存溢出異常:java.lang.StackOverflowError
原因:
1.棧幀過多導致棧內存溢出。如:遞歸調用。
2.棧幀過大導致棧內存溢出。
2.5 線程運行診斷
案例1: CPU佔用過多
定位:(Linux環境下)
1.用top定位那個進程對cpu的佔用過高
2.ps H -eo pid,tid,%cpu | grep +進程id (用ps命令進一步定位是那個線程引起的cpu佔用過高)
3.使用jdk自帶工具 jstack + 進程id
可以根據線程id找到有問題的線程,進一步定位到問題代碼的源代碼行號。
案例2: 遲遲得不到結果。 考慮 線程死鎖
3.本地方法棧
3.1 定義
jvm在調用本地方法時爲本地方法提供的內存空間。
指的就是那些不是由Java代碼編寫的方法。底層與操作系統打交道的是 c/c++ 如:clone()、notify()方法等。
4.堆內存
4.1 定義
通過new關鍵字創建的對象,都會使用堆內存。
4.2 特點
1.是線程共享的內存區域,堆中的對象都要考慮線程安全的問題。
2.有垃圾回收機制。
4.3 堆內存溢出
java.lang.OutOfMemoryError Java heap space
4.4 堆內存診斷
1.jps 工具
查看當前系統中存在哪些Java進程
2.jmap 工具 使用:jmap + -heap + 進程id
查看當前線程堆內存佔用情況。
3.jconsole 工具
圖形界面的,多功能的檢測工具,可以連續監測。
案例:
垃圾回收後,內存佔用仍然很高,怎麼查看並排除?【有實際價值】
解決方法:
使用更實用的jdk官方工具: jvirsualvm 內存佔用可視化工具--使用 堆dump 將佔用情況抓取下來分析。
5.方法區
5.1 定義
方法區是線程共享的一塊區域,存儲的是與類結構相關的如常量池等,還包括在類、實例、接口初始化時用到的特殊方法。在虛擬機啓動時被創建,邏輯上是屬於堆內存的一部分,但是在具體實現上不同虛擬機的廠商不一定遵從jvm邏輯規範,實現方式有所不同。
方法區的內存結構變化圖
5.2 特點
1.線程共享
2.方法區中存儲的是與類相關的一些信息,如:成員變量,成員方法,構造方法、運行時常量池等。
3.由虛擬機啓動時創建,邏輯上是堆內存的一部分。
4.jdk 1.8之前 被稱爲永久代,屬於堆內存的一部分。1.8之後,被稱爲元空間,使用的是本地內存(使用操作系統的內存)。
5.3 方法區的內存溢出
實際場景:框架中使用Cglib來動態生成並加載代理類,很容易產生永久代內存溢出。如: spring、 Mybatis
5.4 常量池
5.4.1 定義
常量池是 *.class中的概念,本質就是一張表,虛擬機指令根據符號地址從這張常量表找到要執行的類名、方法名、參數類型、字面值常量等信息。
5.4.2 作用
給JVM指令提供一些常量符號,虛擬機執行引擎中的解釋器會根據符號找到對應的信息。
5.4.3 常量池與StringTable的關係
在程序運行時,常量池中的信息,都會被加載到運行時常量池中,字符串對象在未使用時僅僅是常量池中的一個符號,當執行到 String s = "a" 時,纔會創建字符串對象,此時,會把 a 作爲 key值從 StringTable 查找,如果有則直接用,如果沒有則把 符號 "a"變爲字符串對象,放入StringTable中。
5.5 運行時常量池
5.5.1定義
當類被加載進內存時,他的常量池信息就會被放入到運行時常量池(在方法區中),並將常量池中的符號地址變爲真實的內存地址。
5.5.2 StringTable的內存位置
5.5.3 StringTable特性
1. 常量池中的字符創僅是符號,第一次使用時纔會變成對象。
2. 利用串池的機制,可以避免重複創建字符串對象。
3. 字符串變量拼接的底層原理是使用StringBuiler對象拼接。(jdk1.8)
4. 字符串常量拼接的原理是編譯期的優化。
5. 可以使用intern方法,主動將串池中還沒有的字符串對象放入串池。
* intern()方法在jdk1.6和jdk1.8 底層實現的區別?
jdk1.8中,將內存中的字符串對象嘗試放入串池,如果串池中有則不會放入,如果沒有則放入串池,並把放入串池中的對象返回。
jdk1.6中,將內存中的字符串對象嘗試放入串池,如果串池中有則不會放入,如果沒有則【會把該字符串對象複製一份】放入串池,並把放入串池中的對象返回。
案例1
案例2
5.5.4 StringTable的性能調優
pass
6.直接內存
6.1 特點
1.屬於操作系統內存。
2.常見於NIO操作時,用於數據緩衝區。
3.分配回收成本較高,但讀寫性能高。
4.不受JVM內存回收管理。
- Java文件讀寫過程之BIO
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-NDfzW3E1-1591013381172)(.\images\Java文件讀寫過程_BIO_01.png)]
- Java文件讀寫過程之NIO 提高了文件讀寫的效率。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QZpb9Anx-1591013381173)(.\images\Java文件讀寫過程_NIO_02.png)]
6.2 直接內存分配和回收的原理
1.使用了Java最底層的一個 Unsafe 對象完成了直接內存的分配回收,並且通過主動調用 freeMemory 方法來回收直接內存。
2.ByteBuffer 的實現類內部,使用了Cleaner(虛引用) 來監測ByteBuffer對象,一旦 ByteBuffer對象被垃圾回收,那麼就會由ReferenceHandler線程
3.通過Cleaner的clean()方法調用 freeMemory來釋放直接內存。
二、垃圾回收機制
內容介紹
1.如何判斷對象可以回收
2.垃圾回收算法
3.分代垃圾回收算法
4.垃圾回收器
5.垃圾回收調優
1. 如何判斷對象可以回收?
1.1 兩個對象回收算法
引用計數法:指一個對象被其他變量引用,就讓引用計數加1,當被兩個對象引用時就加2,如果沒有變量引用時,計數爲 0.
【缺點】:
1.循環引用問題。 A對象引用B,B引用A,AB均無其他對象引用。由於AB對象的引用計數均爲1,
所以AB對象不會被當作垃圾回收,從而造成了內存泄漏問題。
2.早期Python虛擬機在進行垃圾回收時就採用了引用計數算法。
可達性分析算法(JAVA虛擬機採用)
根對象:肯定不能當成垃圾被回收的對象就是根對象。(例子:一串葡萄,掉在盤子裏的就可被回收。)
特點:
1. java虛擬機中的垃圾回收機器採用可達性分析來探索所有存活的對象。
2. 掃描堆中的對象,看是否能夠沿着GC Root對象爲起點的引用鏈找到該對象,找不到,表示可以回收。
問:哪些對象可以作爲GC Root?
a.虛擬機棧(棧楨中的本地變量表)中的引用的對象
b.方法區中的類靜態屬性引用的對象
c.方法區中的常量引用的對象
d.本地方法棧中JNI的引用的對象
1.2對象的四種引用
面試題: 請你說一下Java虛擬中的四種引用?
常見的是五種,分別是強引用、弱引用、虛引用、軟飲用、終結器引用。
1.2.1 強引用
定義:
我們平常用的對象都是強引用。如 Student s = new Student();通過【賦值號】s就【強引用】了學生對象。
特點:
* 只要沿着GC Root根對象的引用鏈能夠找到該對象,就不會被回收。只有所有的GC Root對象都不通過【強引用】引用該對象,該對象才能被垃圾回收。
1.2.2軟引用
特點:
* 只有沒有被直接的強引用而引用,當垃圾回收發生後,內存仍然不足時,就會回收軟引用對象。
* 可以配合引用隊列來釋放軟引用自身。
1.2.3 弱引用
特點:
* 只要沒有被直接的強引用而引用,只要垃圾回收動作發生,不管內存是否充足,都會回收弱引用對象。
* 可以配合引用隊列來釋放弱引用自身。
1.2.4 虛引用
特點:
* 虛引用必須配合引用隊列使用,主要配合 ByteBuffer使用,被引用對象回收時,會將虛引用入隊,由
Reference Handler線程調用虛引用相關方法釋放直接內存。
1.2.5終結器引用
特點:
無需手動編碼,但是內部配合引用隊列使用,在垃圾回收時,終結器引用入隊(被引用對象暫時沒有被回收),再由
Finalizer 線程通過終結器引用找到被引用的對象並調用它的finalize()方法,第二次GC 時才能被回收。
2.垃圾回收算法
2.1 標記清除算法
2.2 標記整理算法
2.3 複製算法
3.分代垃圾回收機制
3.1 垃圾回收執行流程
3.2 與GC 相關 vm參數
4. 垃圾回收器的分類
4.1.串行
特點:
* 單線程
* 適用於 堆內存較小,適合個人電腦。
4.2.吞吐量優先
特點:
* 多線程
* 堆內存較大,多核cpu
* 讓單位時間內,STW的時間最短
吞吐量: 垃圾回收時間佔程序運行時間的比重,垃圾回收時間越短,吞吐量越大。
4.3.響應時間優先
特點:
* 多線程
* 堆內存較大,多核cpu
* 儘可能讓單次STW的時間最短
4. 4G1 垃圾回收器
4.4.1 定義:
Garbage First
4.4.2 適用場景/特點
1、同時注重吞吐量和低延遲、默認的暫停目標是 200ms.
2、超大堆內存,會將堆內存劃分爲多個大小相等的Region.
注意:每個區域都可以獨立作爲伊甸園區、倖存區、和老年代區。
3、整體上是標記+清除算法,兩個區域之間採用的是複製算法.
相關的jvm參數:
-xx:+UseG1Gc
-xx:G1HeapRegionSize = size
-xx:MaxGCPauseMillis = time
4.4.3 G1垃圾回收階段
4.4.4 Young COllection 新生代垃圾回收
4.4.5 Young Collection + CM 垃圾回收階段
4.4.6 Mixed Collection垃圾回收階段
垃圾回收階段
未完待續…