日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

jvm(2)-java内存区域

發(fā)布時間:2023/12/3 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 jvm(2)-java内存区域 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
【0】README
0.1)本文轉(zhuǎn)自 深入理解jvm, 旨在學(xué)習(xí) java內(nèi)存區(qū)域 的基礎(chǔ)知識;

【1】運(yùn)行時數(shù)據(jù)區(qū)域
1)jvm 所管理的內(nèi)存將會包括以下幾個運(yùn)行時數(shù)據(jù)區(qū)域
1.1)
方法區(qū);(線程共享)
1.2)虛擬機(jī)棧;(線程私有)
1.3)本地方法棧;(線程私有)
1.4)java 堆;(線程共享)
1.5)程序計數(shù)器;(線程私有)
Attention) 除了程序計數(shù)器外,虛擬機(jī)內(nèi)存的其他幾個運(yùn)行時區(qū)域都有發(fā)生 OutOfMemoryError(內(nèi)存溢出) 異常的可能;

【1.1】程序計數(shù)器(線程私有)
1)程序計數(shù)器:
是一塊較小的內(nèi)存空間,可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器;(干貨——程序計數(shù)器==行號指示器)
2)其作用:
為了線程切換后能恢復(fù)到正確的執(zhí)行位置,每條線程都需要有一個獨(dú)立的程序計數(shù)器,各條線程之間計數(shù)器互不影響,獨(dú)立存儲,我們稱這類內(nèi)存區(qū)域為“線程私有”的內(nèi)存;
3)此內(nèi)存區(qū)域:是唯一一個在 jvm 規(guī)范中 沒有規(guī)定任何 OutOfMemoryError 情況的區(qū)域;

【1.2】java 虛擬機(jī)棧(線程私有,用于存儲局部變量表,操作數(shù)棧,動態(tài)鏈接,方法出口等信息)
1)虛擬機(jī)棧的作用:
描述的是 java 方法執(zhí)行的內(nèi)存模型,每個方法在執(zhí)行的時候都會創(chuàng)建一個棧幀,用于存儲局部變量表,操作數(shù)棧,動態(tài)鏈接,方法出口等信息;
2)局部變量表: 存放了編譯器可知的基本數(shù)據(jù)類型,對象引用 和 return address 類型(指向了一跳字節(jié)碼指令的地址);
3)局部變量空間(Slot): 其中64 位長度的long 和 double 類型數(shù)據(jù)都會占用兩個局部變量空間(slot),其余的數(shù)據(jù)類型占用1個;(干貨——引入slot==局部變量空間)
4)jvm 在該區(qū)域規(guī)定了兩種異常:?
Exception1)
如果線程請求的棧深度大于jvm 所允許的深度,將拋出 StackOverFlowError 異常;
Exception2)如果虛擬機(jī)棧可以動態(tài)擴(kuò)展,如果擴(kuò)展時無法申請到足夠的內(nèi)存,就會拋出 OutOfMemoryError 異常;

【1.3】本地方法棧(線程私有)
1)
本地方法棧的作用于 虛擬機(jī)棧的作用相似;
2)他們作用的區(qū)別在于: 虛擬機(jī)棧為虛擬機(jī)執(zhí)行 java(字節(jié)碼)服務(wù),而本地方法棧為 虛擬機(jī)使用到的 Native 方法(本地方法)服務(wù);

【1.4】java 堆(存放對象實例,線程共有)
1)java 堆的唯一作用:
存放對象實例,幾乎所有的對象實例都在這里分配內(nèi)存;
2)GC堆(垃圾收集堆): java 堆是垃圾收集器管理的主要區(qū)域,因此很多時候也被稱為 GC堆(Garbage Collected Heap);
3)從內(nèi)存回收的角度看:由于現(xiàn)在收集器基本都采用分代收集算法,所以 java 堆還可以分為: 新生代 和 老年代;(干貨——在java堆中引入新生代和老年代)
4)從內(nèi)存分配的角度來看:
線程共享的 java 堆可能劃分出多個線程私有的分配緩沖區(qū)(Thread Local Allocation Buffer, TLAB);
5)java堆 可以處理物理上不連續(xù)的內(nèi)存空間中, 只要邏輯上是連續(xù)的即可;
6) java 堆是可擴(kuò)展的,不過當(dāng)前主流的jvm 都是按照可擴(kuò)展來實現(xiàn)的(通過 Xmx 和 -Xms 控制), 如果在堆中沒有內(nèi)存完成實例分配,并且堆也無法再擴(kuò)展,將會拋出 OutOfMemoryError 異常;

【1.5】方法區(qū)(用于存放 class 的相關(guān)信息,線程共有)
1)方法區(qū)的作用:
用于存儲被虛擬機(jī)加載的類信息,常量,靜態(tài)變量,即時編譯器(JIT)編譯后的代碼等數(shù)據(jù);
2)方法區(qū)除了和 java 堆一樣不需要連續(xù)的內(nèi)存和可以選擇固定大小或者可擴(kuò)展外,還可以選擇不實現(xiàn)垃圾收集;
3) 方法區(qū)的內(nèi)存回收目標(biāo): 主要是針對常量池的回收和對類型的卸載;

【1.6】運(yùn)行時常量池
1)
運(yùn)行時常量池是方法區(qū)的一部分;
2)Class 文件中除了有類的版本,字段,方法,接口等描述信息外,還有一個信息——常量池;(干貨——引入常量池)
3)常量池的作用:
用于存放編譯器生成的各種字面量和符號引用,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)的運(yùn)行時常量池中存放;
4)運(yùn)行時常量池的一個重要特征:具備動態(tài)性;(干貨——運(yùn)行時常量池的具備動態(tài)性)
看個荔枝):
并非預(yù)置入 Class 文件中常量池的內(nèi)容才能進(jìn)入方法區(qū)運(yùn)行時常量池,運(yùn)行期間也可能將新的常量放入池中, 這種特性被開發(fā)人員利用的多的便是 String類的 intern 方法;

【1.7】直接內(nèi)存
0)直接內(nèi)存:
分配在本機(jī)直接內(nèi)存上,不是 jvm 運(yùn)行時數(shù)據(jù)區(qū)的一部分,但被頻繁地使用;
1)jdk1.4 中新加入了NIO(new input/output)類: 引入了一種基于通道與緩沖區(qū)(buffer)的IO方式,他可以使用 Native 函數(shù)庫直接分配堆外內(nèi)存,然后通過一個存儲在java 堆中的DirectByteBuffer 對象作為這塊內(nèi)存的引用進(jìn)行操作,這樣能在一些場景中提高性能,因為避免了在 java 堆 和 Native 堆中來回復(fù)制數(shù)據(jù);
2)顯然,本機(jī)直接內(nèi)存的分配不會受到 java 堆大小的限制;

【2】HotSpot 虛擬機(jī)對象探秘
1)對象的創(chuàng)建steps:
step1)是否執(zhí)行類加載過程:
虛擬機(jī)遇到一條new指令,首先去檢查這個指令的參數(shù)是否能在常量池中定位到一個類的符號引用,并且檢查這個符號引用代表的類是否已被加載,解析和初始化過。如果沒有,那必須執(zhí)行相應(yīng)的類加載過程;
step2)分配內(nèi)存: 接下來 虛擬機(jī)將為新生對象分配內(nèi)存。(內(nèi)存分配方式有: 指針碰撞 和 空閑列表)
problem):對象創(chuàng)建在虛擬機(jī)中是非常頻繁的行為,即使是僅僅修改一個指針指向的位置, 在并發(fā)情況下也并不是線程安全的。如,可能出現(xiàn)正在給對象A分配內(nèi)存,指針還未來得及修改,對象B 又同時使用了原來指針來分配內(nèi)存的情況;
solution1):對分配內(nèi)存空間的動作進(jìn)行同步處理——實際上采用 CAS 配上失敗重試的方式保證更新操作的原子性;
solution2):吧內(nèi)存分配的動作按照線程劃分在不同的空間中進(jìn)行,即每個線程在 java堆中預(yù)先分配一小塊內(nèi)存,稱為本地線程分配緩沖(Thread Local Allocation Buffer,TLAB),只有TLAB用完并分配新的TLAB時,才需要同步鎖定;(干貨——每個線程在 java堆中預(yù)先分配一小塊內(nèi)存,稱為本地線程分配緩沖)
step3)初始化為零:
內(nèi)存分配完成后,虛擬機(jī)需要將分配到 的內(nèi)存空間都初始化為零值;
step4)虛擬機(jī)對對象進(jìn)行必要的設(shè)置: 如這個對象的類是什么,如果才能找到類的元數(shù)據(jù)信息,對象的哈希碼,對象的GC 分代年齡等信息。這些信息存放在對象的對象頭中;(干貨——引入了對象頭)
step5)執(zhí)行 <init>方法:
執(zhí)行完new 執(zhí)行后,會執(zhí)行 init 方法,把對象按照程序員的意愿進(jìn)行初始化;

【2.1】對象的內(nèi)存布局
1)對象的內(nèi)存布局分為3個區(qū)域:
對象頭, 實例數(shù)據(jù) 和 對齊填充;
2)HotSpot 虛擬機(jī)的對象頭包括兩部分?jǐn)?shù)據(jù):
2.1)對象頭第一部分(Mark Word):
用于存儲對象自身的運(yùn)行時數(shù)據(jù),如哈希碼,GC分代年齡,鎖狀態(tài)標(biāo)志,線程持有的鎖,偏向線程ID,偏向時間戳,這部分?jǐn)?shù)據(jù)的長度在 32位 和 64 位的虛擬機(jī)(未開啟壓縮指針)中分別為 32bit 和 64 bit, 官方稱為 “Mark Word”;(干貨——引入對象頭的Mark Word);對象頭信息是與對象自身定義 的數(shù)據(jù)無關(guān)的額外存儲成本,考慮到虛擬機(jī)的空間效率,Mark Word被設(shè)計成 非固定的數(shù)據(jù)結(jié)構(gòu)以便在 極小的空間內(nèi)存儲盡量多的信息;(干貨——對象頭信息是與對象自身定義 的數(shù)據(jù)無關(guān)的額外存儲成本)
Complementary)
HotSpot 虛擬機(jī)對象頭 Mark Word:
2.2)對象頭第二部分(類型指針):類型指針, 即對象指向它的類元數(shù)據(jù)的指針,虛擬機(jī)通過這個指針來確定這個對象是哪個類的實例;
3)對象的實例數(shù)據(jù)區(qū)域:是對象真正存儲的有效信息,也是在程序代碼中所定義的各種類型的字段內(nèi)容;
4)對象的對齊填充區(qū)域: 沒有什么特別的含義,它僅僅起著占位符的作用, 換句話說,就是對象的大小必須是 8字節(jié) 的整數(shù)倍;

【2.3】對象的訪問定位
1)
java 程序需要通過 棧上的 引用數(shù)據(jù)來操作堆上的具體對象。
2)引用應(yīng)該通過何種方式去定位,訪問堆中的對象的具體位置,目前主流的訪問方式有 使用句柄 和 直接指針兩種;(干貨——棧上引用訪問堆上對象的主流訪問方式)
2.1)使用句柄訪問方式:
那么java 堆中將會劃分出一塊內(nèi)存來作為句柄池,引用數(shù)據(jù)中存儲的就是對象的句柄地址,而句柄地址包含了對象實例數(shù)據(jù)和類型數(shù)據(jù)各自的具體地址;

2.2)使用直接指針訪問(Sun HotSpot 使用這種方式訪問)

總結(jié)

以上是生活随笔為你收集整理的jvm(2)-java内存区域的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。