面试必会系列 - 1.7 JVM 内存模型
本文已收錄至 Github(MD-Notes),若博客中圖片模糊或打不開,可以來我的 Github 倉(cāng)庫(kù),包含了完整圖文:https://github.com/HanquanHq/MD-Notes,涵蓋了互聯(lián)網(wǎng)大廠面試必問的知識(shí)點(diǎn),講解透徹,長(zhǎng)期更新中,歡迎一起學(xué)習(xí)探討 ~
更多內(nèi)容,可以訪問:
面試必會(huì)系列專欄:https://blog.csdn.net/sinat_42483341/category_10300357.html
操作系統(tǒng)系列專欄:https://blog.csdn.net/sinat_42483341/category_10519484.html
JVM
查看 JVM 啟動(dòng)默認(rèn)參數(shù):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)對(duì)象內(nèi)存布局
new Object() 對(duì)象在內(nèi)存中占多少字節(jié)?16字節(jié)
8 字節(jié)(MarkWord,固定大小)
4 字節(jié)(開啟壓縮時(shí)的對(duì)象指針 ClassPointer,指向你的對(duì)象 TT.class)
0 字節(jié) Instance data,要看你的對(duì)象有多少成員變量
4 字節(jié) padding(對(duì)齊,要被 8 整除)
查看對(duì)象的內(nèi)存布局工具: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、不加鎖時(shí),對(duì)象內(nèi)存布局如下:
(注意,jdk 11 需要指定 -XX:-UseBiasedLocking 參數(shù),取消偏向鎖) 001 表示沒有鎖
2、添加 syncronized 之后,如下。下面 00 表示輕量級(jí)鎖,因?yàn)槠蜴i未啟動(dòng),直接升級(jí)為了輕量級(jí)鎖
3、添加參數(shù),啟動(dòng)偏向鎖:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0
對(duì)象頭包括什么?JDK 1.8 的實(shí)現(xiàn)如下
對(duì)象頭包括: MarkWord,classpointer,Instance data,padding
**MarkWord 包括:**鎖信息、HashCode、GC信息
在 hotspot 源碼 jdk8u: markOop.hpp 中,詳細(xì)的說明了 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)總結(jié) JDK8 中對(duì)象的 object header 布局如下(64 位,也就是 8 字節(jié)):
要看加的是什么鎖的話,先看 markword 的最低兩位,是 01 / 00 / 10 / 11
64位 markword 構(gòu)成
為什么 GC 年齡默認(rèn)為 15?因?yàn)榉执挲g只有 4 bit,可以表示最大的數(shù)就是 15
對(duì)象分配過程?
見《Java垃圾回收機(jī)制.md》- 對(duì)象分配過程
對(duì)象怎么定位?
怎么通過 t 找到 new T()?
- 通過句柄池(間接法)
- 通過直接指針,效率高(HotSpot用的是這種方式),缺點(diǎn)是在GC需要移動(dòng)對(duì)象時(shí)reference本身需要被修改
Java 程序是怎樣運(yùn)行的?
1、首先,通過 Javac 編譯器將 .java 轉(zhuǎn)為 JVM 可加載的 .class 字節(jié)碼文件。
Javac 是由 Java 編寫的程序,編譯過程可以分為:
① 詞法解析,通過空格分割出單詞、操作符、控制符等信息,形成 token 信息流,傳遞給語(yǔ)法解析器。
② 語(yǔ)法解析,把 token 信息流按照 Java 語(yǔ)法規(guī)則組裝成語(yǔ)法樹。
③ 語(yǔ)義分析,檢查關(guān)鍵字使用是否合理、類型是否匹配、作用域是否正確等。
④ 字節(jié)碼生成,將前面各個(gè)步驟的信息轉(zhuǎn)換為字節(jié)碼。
字節(jié)碼必須通過類加載過程加載到 JVM 后才可以執(zhí)行,執(zhí)行有三種模式,解釋執(zhí)行、JIT 編譯執(zhí)行、JIT 編譯與解釋器混合執(zhí)行(主流 JVM 默認(rèn)執(zhí)行的方式)。混合模式的優(yōu)勢(shì)在于解釋器在啟動(dòng)時(shí)先解釋執(zhí)行,省去編譯時(shí)間。
2、之后,通過即時(shí)編譯器 JIT 把字節(jié)碼文件編譯成本地機(jī)器碼。
Java 程序最初都是通過解釋器進(jìn)行解釋執(zhí)行的,當(dāng)虛擬機(jī)發(fā)現(xiàn)某個(gè)方法或代碼塊的運(yùn)行特別頻繁,就會(huì)認(rèn)定其為"熱點(diǎn)代碼",虛擬機(jī)即時(shí)編譯器會(huì)把它們編譯成本地機(jī)器碼。
3、還可以通過靜態(tài)的提前編譯器 AOT 直接把程序編譯成與目標(biāo)機(jī)器指令集相關(guān)的二進(jìn)制代碼。
JVM內(nèi)存模型
class 的生命周期
Run-time data areas 運(yùn)行時(shí)數(shù)據(jù)區(qū)的組成
我們常說的:棧放方法,堆存對(duì)象
堆里面存放的都是一些引用,而棧是我們真正用來執(zhí)行程序的??梢园衙恳粋€(gè)方法看做對(duì)應(yīng)一個(gè)棧幀。
動(dòng)態(tài)鏈接:把符號(hào)引用(.class文件常量池中的引用)轉(zhuǎn)化為直接引用(指向堆中的對(duì)象)
每個(gè)線程有自己獨(dú)立的 PC,VMS,NMS
線程之間共享 Heap 以及 MethodArea
PC:程序計(jì)數(shù)器
MethodArea:方法區(qū)
類的所有字段和方法字節(jié)碼,以及一些特殊方法構(gòu)造函數(shù),接口代碼也在這里定義。簡(jiǎn)單來說,所有定義方法的信息都保存在該區(qū)域,靜態(tài)變量+常量+類信息(構(gòu)造方法/接口定義)+運(yùn)行時(shí)常量池都存在方法區(qū)中
方法區(qū)只是對(duì)于虛擬機(jī)的規(guī)范。所有的虛擬機(jī)應(yīng)該有方法區(qū),不同虛擬機(jī)方法區(qū)的叫法不一樣
- jdk 1.8 之前,HotSpot 使用 PermSpace 永久代 實(shí)現(xiàn)方法區(qū)
- 字符串常量位于 Perm Space
- FGC 不會(huì)清理
- jdk 1.8 及之后:使用 Meta Space 元數(shù)據(jù)區(qū)
- 字符串常量位于 Heap
- FGC 會(huì)清理
Stacks:??臻g
- 棧中存放棧幀
- 局部變量表
- 操作數(shù)棧
- 動(dòng)態(tài)鏈接
- 返回值地址
Heap:堆空間
虛擬機(jī)啟動(dòng)時(shí)自動(dòng)分配創(chuàng)建,用于存放對(duì)象的實(shí)例,幾乎所有對(duì)象都在堆上分配內(nèi)存,當(dāng)對(duì)象無法在該空間申請(qǐng)內(nèi)存時(shí),拋出OOM異常。也是垃圾收集器管理的主要區(qū)域。
- 類實(shí)例
- 為數(shù)組分配的空間
DirectMemory:直接內(nèi)存
-
JVM可以直接訪問OS管理的內(nèi)存,提高效率
- 零拷貝(不需要拷貝),NIO用到
Run-Time Constant Pool 運(yùn)行時(shí)常量池
線程私有的:是交由 JVM 自動(dòng)化管理的。我們做的 JVM 調(diào)優(yōu),是調(diào)的堆和方法區(qū)。
面試題:下面輸出 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);} } 超強(qiáng)干貨來襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生總結(jié)
以上是生活随笔為你收集整理的面试必会系列 - 1.7 JVM 内存模型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 操作系统:第五章 磁盘管理 - I/O控
- 下一篇: 左神算法:用递归函数和栈逆序一个栈(Ja