深入理解 Java 虚拟机 学习笔记
生活随笔
收集整理的這篇文章主要介紹了
深入理解 Java 虚拟机 学习笔记
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
第二章 Java 內存區域與內存溢出異常
內存區域
?
?
?
-- from 姜志明
對象創建
- 若已經在內存中則跳過。
- 類加載完以后就可以確定對象所需的空間大小?// TODO why?
- 根據 GC 回收算法的不同,分配方式略有區別。
- 標記整理算法,使用空閑列表
- 帶壓縮的算法,使用指針碰撞(已分配和未分配內存間由指針分隔)
對象的內存布局
- MarkWord 占用一個?字?的大小,其中分為兩部分:
- 對象自身運行時元數據。例如,哈希碼、GC 分代年齡、鎖狀態標志等等
- 類型指針。指向其類的元數據。
- 若對象是數組則還需要保存數組的長度。
- 域的存儲順序:
- 基本類型優先,長度長的優先。
- 父類域優先。子類較短域可插入父類域空隙。
- 受虛擬機分配策略參數和域定義順序的影響。
對象訪問
兩種方式:
內存溢出異常
常用 JVM 參數 (Java HotSpot VM)
| -verbose:class | 顯示每一個被加載的類的信息 | ? |
| -verbose:gc | 顯示每一個 GC 事件的信息 | ? |
| -Xmnsize | 年輕代最大容量 | -Xmn256m |
| -Xmssize | 堆的初始大小。1024 的整數倍并且要大于 1MB | -Xms6m |
| -Xmxsize | 堆的最大容量。1024 的整數倍并且要大于 2MB | -Xmx80m |
| -Xsssize | 線程棧容量。平臺不同默認值不同,詳情參考文檔。Linux/x64 (64-bit): 1024 KB | -Xss1m |
| -XX:MaxDirectMemorySize=size | 直接內存的最大容量,默認與堆容量相同 | -XX:MaxDirectMemorySize=1m |
| -XX:+HeapDumpOnOutOfMemory | 當拋出 OOM 時,使用 HPROF 將堆的快照保存到當前目錄 | ? |
| -XX:HeapDumpPath=path | 設置快照輸出路徑 | -XX:HeapDumpPath=/var/log/java/java_heapdump.hprof |
| -XX:+PrintGCDetails | 開啟在 GC 時打印詳細信息 | ? |
| -XX:SurvivorRatio=ratio | 新生代中 eden 與 survivor 的大小比例,默認為 8 | -XX:SurvivorRatio=4 |
?
參考:?Java HotSpot VM 參數
常見異常及可能原因
- 堆區
- OutOfMemoryException。使用工具對快照進行分析,看是否發生了內存泄露(內存中有不再使用的但無法回收的對象或資源)。若是,則通過分析引用鏈找到根源,解決問題;若不是檢查虛擬機堆參數,看是否能夠調大。再檢查代碼中是否有生命周期很長的大對象。
- 虛擬機棧和本地方法棧
- OutOfMemoryException。棧容量 * 線程數量 = 固定值。當線程數量過多時會引發,可以適當減小棧容量。
- StackOverflowException。按異常查根源。
- 方法區和運行時常量池
- 直接內存溢出
- 不正確的使用 NIO。
String 與字符串常量
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class StringTest {public static void main(String[] args) {String m = "hello";String n = "hello";String u = new String(m);String v = new String("hello");System.out.println("m == n: " + (m == n));System.out.println("m == u: " + (m == u));System.out.println("m == v: " + (m == v));System.out.println("u == v: " + (u == v));} }output: m == n: true m == u: false m == v: false u == v: false |
參考:?初探Java字符串
第三章 垃圾收集器與內存分配策略
判斷對象是否存活
- 棧中引用的對象
- 方法區常量引用的對象
- 方法區靜態域引用的對象
- 本地方法中 JNI 引用的對象(不太懂)
- 新生代的回收效率可達到 70% - 95%,而永久代則低的多(性價比太低)
- 在大量使用反射、動態代理、CGLib 等 ByteCode 框架、動態生成 JSP 以及 OSGi 這類頻繁自定義 ClassLoader 的場景都需要虛擬機有卸載類的能力。
垃圾收集算法
- 掃描一遍,標記出需要回收的對象,再掃描將其清除
- 標記/清除兩階段時間效率都不高,且回收后空間較零碎。
- 將內存分為兩塊,當一塊中內存不足時,將其中所有存活對象復制到另一塊中,回收當前一整塊。
- 目前商用虛擬機大都使用這一算法回收新生代。將內存劃分為一個較大的 Eden 區和兩塊較小的 Survivor. Eden:Survivor = 8:1
- 標記出須清理的對象,然后其余對象移動到一端
- 新生代使用復制算法
- 永久代使用其他兩種算法
HotSpot 算法實現
- 安全點(safepoint):指令序列復用的位置。例如方法調用、循環結構、異常跳轉等位置。
- OopMaps:一種特殊的數據結構,用于枚舉 GC root
- 安全區域(safe region):引用不發生改變的代碼片段
垃圾收集器
- 并發(concurrent) vs 并行(parallel)
- 并行是同時進行(多 CPU)
- 并發可交替
- Minor GC vs Major GC vs Full GC
- Minor GC:只回收新生代
- Major GC:只回收永久代
- Full GC: 回收整個堆。相當于 Minor GC + Major GC
- 吞吐量 = 用戶代碼執行時間 / (用戶代碼執行時間 + 垃圾回收時間)
- 自適應調節:虛擬機根據但前系統的運行情況,自動調節虛擬機各參數以確保最大吞吐量。
內存分配與回收策略
TLAB(Thread local allocate buffer)線程私有分配緩沖區,每個線程一個
第六章 類文件結構
第七章 虛擬機類加載機制
類加載的過程
- 為了確保加載的字節流時符合規范的,不會危害到虛擬機自身的安全。主要包括
- 為類變量分配內存并進行初步初始化(0/null) // 不應該是在類加載階段完成的么?
- 將符號引用替換為直接引用
- static fields and block init
類加載器
- 雙親委派:當需要加載一個類時先使用父類加載器(其實這個地方不是很準確,父子關系是通過復合來實現的),若失敗了,再使用當前的加載器。如果自己寫一個?Object?類,編譯可通過但是由于雙親委派,它永遠都不會被加載。
第十章 早期(編譯器)優化
// TODO: 因本章含有相當多的編譯原理相關概念,所以第十、十一章學習延后(預計第 8-9 周)
前端編譯過程(*.java --> *.class)
- Token: 是編譯過程中的最小元素。例如關鍵字、變量名、運算符等等
參考
?
作者:c-rainstorm
GitHub:https://github.com/c-rainstorm
https://www.javazhiyin.com/26438.html?
總結
以上是生活随笔為你收集整理的深入理解 Java 虚拟机 学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《深入理解Java虚拟机》笔记6——高效
- 下一篇: JDK1.8 十大新特性详解