面试必会系列 - 1.7 JVM 内存模型
本文已收錄至 Github(MD-Notes),若博客中圖片模糊或打不開,可以來我的 Github 倉庫,包含了完整圖文:https://github.com/HanquanHq/MD-Notes,涵蓋了互聯網大廠面試必問的知識點,講解透徹,長期更新中,歡迎一起學習探討 ~
更多內容,可以訪問:
面試必會系列專欄:https://blog.csdn.net/sinat_42483341/category_10300357.html
操作系統系列專欄:https://blog.csdn.net/sinat_42483341/category_10519484.html
JVM
查看 JVM 啟動默認參數:java -XX:+PrintCommandLineFlags -version
java -XX:+PrintCommandLineFlags -version -XX:InitialHeapSize=266536512 -XX:MaxHeapSize=4264584192 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC java version "1.8.0_251" Java(TM) SE Runtime Environment (build 1.8.0_251-b08) Java HotSpot(TM) 64-Bit Server VM (build 25.251-b08, mixed mode)對象內存布局
new Object() 對象在內存中占多少字節?16字節
8 字節(MarkWord,固定大小)
4 字節(開啟壓縮時的對象指針 ClassPointer,指向你的對象 TT.class)
0 字節 Instance data,要看你的對象有多少成員變量
4 字節 padding(對齊,要被 8 整除)
查看對象的內存布局工具:JOL = Java Object Layout
<dependencies><!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core --><dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.9</version></dependency> </dependencies> public class MyTest {public static void main(String[] args) {Object o = new Object(); // 不加鎖o.hashCode();System.out.println(ClassLayout.parseInstance(o).toPrintable());Object o1 = new Object();synchronized (o1) { // 加鎖System.out.println(ClassLayout.parseInstance(o1).toPrintable());}} }1、不加鎖時,對象內存布局如下:
(注意,jdk 11 需要指定 -XX:-UseBiasedLocking 參數,取消偏向鎖) 001 表示沒有鎖
2、添加 syncronized 之后,如下。下面 00 表示輕量級鎖,因為偏向鎖未啟動,直接升級為了輕量級鎖
3、添加參數,啟動偏向鎖:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0
對象頭包括什么?JDK 1.8 的實現如下
對象頭包括: MarkWord,classpointer,Instance data,padding
**MarkWord 包括:**鎖信息、HashCode、GC信息
在 hotspot 源碼 jdk8u: markOop.hpp 中,詳細的說明了 object header 的布局。理解即可,無需背過。
The markOop describes the header of an object. Bit-format of an object header (most significant first, big endian layout below):32 bits: -------- hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object) JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object) size:32 ------------------------------------------>| (CMS free block) PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)64 bits: -------- unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object) JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object) PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object) size:64 ----------------------------------------------------->| (CMS free block)unused:25 hash:31 -->| cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && normal object) JavaThread*:54 epoch:2 cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && biased object) narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->|(COOPs&&CMSpromotedobject) unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)總結 JDK8 中對象的 object header 布局如下(64 位,也就是 8 字節):
要看加的是什么鎖的話,先看 markword 的最低兩位,是 01 / 00 / 10 / 11
64位 markword 構成
為什么 GC 年齡默認為 15?因為分代年齡只有 4 bit,可以表示最大的數就是 15
對象分配過程?
見《Java垃圾回收機制.md》- 對象分配過程
對象怎么定位?
怎么通過 t 找到 new T()?
- 通過句柄池(間接法)
- 通過直接指針,效率高(HotSpot用的是這種方式),缺點是在GC需要移動對象時reference本身需要被修改
Java 程序是怎樣運行的?
1、首先,通過 Javac 編譯器將 .java 轉為 JVM 可加載的 .class 字節碼文件。
Javac 是由 Java 編寫的程序,編譯過程可以分為:
① 詞法解析,通過空格分割出單詞、操作符、控制符等信息,形成 token 信息流,傳遞給語法解析器。
② 語法解析,把 token 信息流按照 Java 語法規則組裝成語法樹。
③ 語義分析,檢查關鍵字使用是否合理、類型是否匹配、作用域是否正確等。
④ 字節碼生成,將前面各個步驟的信息轉換為字節碼。
字節碼必須通過類加載過程加載到 JVM 后才可以執行,執行有三種模式,解釋執行、JIT 編譯執行、JIT 編譯與解釋器混合執行(主流 JVM 默認執行的方式)。混合模式的優勢在于解釋器在啟動時先解釋執行,省去編譯時間。
2、之后,通過即時編譯器 JIT 把字節碼文件編譯成本地機器碼。
Java 程序最初都是通過解釋器進行解釋執行的,當虛擬機發現某個方法或代碼塊的運行特別頻繁,就會認定其為"熱點代碼",虛擬機即時編譯器會把它們編譯成本地機器碼。
3、還可以通過靜態的提前編譯器 AOT 直接把程序編譯成與目標機器指令集相關的二進制代碼。
JVM內存模型
class 的生命周期
Run-time data areas 運行時數據區的組成
我們常說的:棧放方法,堆存對象
堆里面存放的都是一些引用,而棧是我們真正用來執行程序的。可以把每一個方法看做對應一個棧幀。
動態鏈接:把符號引用(.class文件常量池中的引用)轉化為直接引用(指向堆中的對象)
每個線程有自己獨立的 PC,VMS,NMS
線程之間共享 Heap 以及 MethodArea
PC:程序計數器
MethodArea:方法區
類的所有字段和方法字節碼,以及一些特殊方法構造函數,接口代碼也在這里定義。簡單來說,所有定義方法的信息都保存在該區域,靜態變量+常量+類信息(構造方法/接口定義)+運行時常量池都存在方法區中
方法區只是對于虛擬機的規范。所有的虛擬機應該有方法區,不同虛擬機方法區的叫法不一樣
- jdk 1.8 之前,HotSpot 使用 PermSpace 永久代 實現方法區
- 字符串常量位于 Perm Space
- FGC 不會清理
- jdk 1.8 及之后:使用 Meta Space 元數據區
- 字符串常量位于 Heap
- FGC 會清理
Stacks:棧空間
- 棧中存放棧幀
- 局部變量表
- 操作數棧
- 動態鏈接
- 返回值地址
Heap:堆空間
虛擬機啟動時自動分配創建,用于存放對象的實例,幾乎所有對象都在堆上分配內存,當對象無法在該空間申請內存時,拋出OOM異常。也是垃圾收集器管理的主要區域。
- 類實例
- 為數組分配的空間
DirectMemory:直接內存
-
JVM可以直接訪問OS管理的內存,提高效率
- 零拷貝(不需要拷貝),NIO用到
Run-Time Constant Pool 運行時常量池
線程私有的:是交由 JVM 自動化管理的。我們做的 JVM 調優,是調的堆和方法區。
面試題:下面輸出 i 為多少?
package com.mashibing.jvm.c4_RuntimeDataAreaAndInstructionSet;public class TestIPulsPlus {public static void main(String[] args) {int i = 8;i = i++; // i = ++i;System.out.println(i);} } 超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的面试必会系列 - 1.7 JVM 内存模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 操作系统:第五章 磁盘管理 - I/O控
- 下一篇: 左神算法:猫狗队列(通过给不同实例盖时间