深入解剖JVM内存区域
不詩意的女程序媛不是好廚師~
轉載請注明出處,From李詩雨—[https://blog.csdn.net/cjm2484836553/article/details/103528907]
深入解剖JVM內存區域
- 前言:Jvm是啥
- 1.運行時數據區
- 2.站在線程角度來看
- 2.1 線程私有
- 2.1.1程序計數器
- 穿插提一下→棧
- 2.1.2虛擬機棧
- (1) 局部變量表
- (2)操作數棧
- (3)返回地址
- (4)動態鏈接
- 2.1.3本地方法棧
- 2.2線程共享
- 2.2.1方法區
- 2.2.2Java堆
- 3.直接內存(了解即可)
- 4.深入辨析堆和棧
- 5.深入理解對象
- 5.1虛擬機角度來看,一個對象的誕生
- 1.檢查加載
- 2.分配內存
- 3.內存空間初始化
- 4.設置
- 5.對象初始化
- 5.2對象的內存布局
- 5.3對象的訪問定位
- 6.虛擬機優化技術(后期)——逃逸分析
- 棧上分配:
- 7.面試會問那哪些呢?
前言:Jvm是啥
圖可以大致理解為:
- JDK - 類庫 = JRE
- JRE - API --> JVM(翻譯)
【JVM是啥】
- 其實 JVM 就是 翻譯 .
? 字節碼 --> JVM(翻譯) —> 機器碼(讓電腦的CPU可以直接讀取)
- JVM不單單只支持Java語言,也支持其他語言(Scala、Kotlin、Groovy等等)
【虛擬機歷史】
-
目前使用范圍最廣的Java虛擬機—HotSpot VM
Hotspot什么意思:熱點代碼探測技術,及時編譯器(發現最有價值的代碼,如果代碼用得非常多,就會把這些代碼編譯成本地代碼)。
-
谷歌(谷歌主要開發語言也是Java):Google Android Dalivk VM
1.運行時數據區
一個Class文件經過類加載了,然后它就會被放到運行時數據區里面。
-
那運行時數據區 是什么呢?
Java虛擬機在執行Java程序的過程中會把它所管理的內存劃分為若干個不同的數據區域
注意:JVM所有的東西都是放在內存的
-
運行時數據區 按照線程私有 和 線程共享 進行劃分(如下圖所示)。
? 線程私有—即:你單獨起一個線程這里就會單獨有一塊東西
? 線程共享—即:無論你起多少個線程,這個區域就一份
下面來分別講講線程私有的東西:
2.站在線程角度來看
2.1 線程私有
2.1.1程序計數器
【定義】:指向當前線程正在執行的字節碼指令(Class)的地址(行號)
注意:jvm是運行class的,不是運行java的
【舉個栗子】:
比如大家都有上過公開課的經歷,
老師在上課我們可以看做線程1;
老師有時候會與我們互動,解決我們的疑問,我們看做線程2.
那老師暫停講課,和我們互動的時候,他要記住自己講到哪里了,以便后面繼續講課,而不是從頭再來講。
? 而程序計數器就是起到了記錄的作用,以保證2個線程在切換過程中,程序仍然能正常執行。
-
程序計數器占用的內存非常非常的小,因為它就記一個數。
是當前線程執行的字節碼的行號指示器;
各線程之間獨立存儲,互不影響
-
(面試)為什么需要程序計數器
? Java是多線程的,意味著線程切換
? 確保多線程情況下的程序正常執行
在說虛擬機棧之前,我們先來提一下棧~
穿插提一下→棧
-
棧(Stack):數據結構
入口和出口只有一個
入棧
出棧
-
特點
先進后出(FIL0)
-
為什么JVM要使用棧?
非常符合JAVA中方法間的調用
來看個例子你就明白了:
輸出結果:
先入棧的方法,最后出來。
好下面我們來看看虛擬機棧:
2.1.2虛擬機棧
-
虛擬機棧:
每個線程私有的,線程在運行時,在執行每個方法的時候都會打包成一個棧幀,
棧幀中 存儲了局部變量表,操作數棧,動態鏈接,方法出口等信息,然后放入棧。
每個時刻正在執行的當前方法就是虛擬機棧頂的棧楨。
方法的執行就對應著棧幀在虛擬機棧中入棧和出棧的過程。
-
棧的大小缺省為1M,
-
改虛擬棧的大小:可用參數 –Xss調整大小,例如-Xss256k
下面我們來一一講解棧幀的內部:
(1) 局部變量表
-
定義:顧名思義就是局部變量的表,用于存放我們的局部變量的。
首先它是一個32位的長度,主要存放我們的Java的八大基礎數據類型,一般32位就可以存放下,如果是64位的就使用高低位占用兩個也可以存放下,如果是局部的一些對象,比如我們的Object對象,我們只需要存放它的一個引用地址即可。
局部變量表的第0個位置始終放的都是this這個對象,放的是引用。
(2)操作數棧
-
定義:存放我們方法執行的操作數的,它就是一個棧,先進后出的棧結構。
操作數棧,就是用來操作的,操作的的元素可以是任意的java數據類型。
我們知道一個方法剛剛開始的時候,這個方法的操作數棧就是空的,操作數棧運行方法是會一直進行入棧/出棧的操作
(3)返回地址
正常返回(調用程序計數器中的地址作為返回)、異常的話(通過異常處理器表<非棧幀中的>來確定)
(4)動態鏈接
動態鏈接: Java語言特性多態(需要類加載、運行時才能確定具體的方法),動態特性(Groovy、JS、動態代理)
2.1.3本地方法棧
-
本地方法棧保存的是native方法的信息
-
各虛擬機自由實現。
-
當一個JVM創建的線程調用native方法后,JVM不再為其在虛擬機棧中創建棧幀,JVM只是簡單地動態鏈接并直接調用native方法
2.2線程共享
2.2.1方法區
方法區中都存儲了哪些信息呢?
-
類信息 (即class)
-
常量 (“lsy”,"123"等)
-
靜態變量 (static變量)
-
即時編譯期編譯后的代碼
這個指的是動態即使編譯,我在運行的時候才進行編譯。編譯完之后也是放方法區。
2.2.2Java堆
哪些東西都在方法堆中呢?
-
對象實例(幾乎所有)
為什么說是幾乎所有對象?→ 后面會講 棧分配對象
-
數組
Java堆也是垃圾回收發生的主要區域,
Java堆的大小參數設置
-Xmx 堆區內存可被分配的最大上限
-Xms 堆區內存初始內存分配的大小
3.直接內存(了解即可)
直接內存不是虛擬機運行時數據區的一部分,也不是java虛擬機規范中定義的內存區域;
如果使用了NIO,這塊區域會被頻繁使用,在java堆內可以用directByteBuffer對象直接引用并操作;
這塊內存不受java堆大小限制,但受本機總內存的限制,
可以通過-XX:MaxDirectMemorySize來設置(默認與堆內存最大值一樣),所以也會出現OOM異常。
4.深入辨析堆和棧
功能
? 以棧幀的方式存儲方法調用的過程,并存儲方法調用過程中基本數據類型的變量(int、short、long、byte、float、double、boolean、char等)以及對象的引用變量,其內存分配在棧上,變量出了作用域就會自動釋放;
? 而堆內存用來存儲Java中的對象。無論是成員變量,局部變量,還是類變量,它們指向的對象都存儲在堆內存中;
線程獨享還是共享
? 棧內存歸屬于單個線程,每個線程都會有一個棧內存,其存儲的變量只能在其所屬線程中可見,即棧內存 可以理解成線程的私有內存。
? 堆內存中的對象對所有線程可見。堆內存中的對象可以被所有線程訪問
空間大小
? 棧的內存要遠遠小于堆內存,棧的深度是有限制的,可能發生StackOverFlowError問題。
(虛擬機棧可以理解為最大為1M,那100個線程–>100M)
5.深入理解對象
5.1虛擬機角度來看,一個對象的誕生
當虛擬機遇到一條new指令時,會經歷如下過程:
1.檢查加載
先執行相應的類加載過程。
2.分配內存
接下來虛擬機將為新生對象分配內存。為對象分配空間的任務等同于把一塊確定大小的內存從Java堆中劃分出來。
- 指針碰撞 、空閑列表
如果Java堆中內存是絕對規整的,所有用過的內存都放在一邊,空閑的內存放在另一邊,中間放著一個指針作為分界點的指示器,那所分配內存就僅僅是把那個指針向空閑空間那邊挪動一段與對象大小相等的距離,這種分配方式稱為“指針碰撞”。
如果Java堆中的內存并不是規整的,已使用的內存和空閑的內存相互交錯,那就沒有辦法簡單地進行指針碰撞了,虛擬機就必須維護一個列表,記錄上哪些內存塊是可用的,在分配的時候從列表中找到一塊足夠大的空間劃分給對象實例,并更新列表上的記錄,這種分配方式稱為“空閑列表”。
選擇哪種分配方式由Java堆是否規整決定,而Java堆是否規整又由所采用的垃圾收集器是否帶有壓縮整理功能決定。
空閑列表的產生和垃圾回收有一定關系,因為只有發生垃圾回收才會出現這種空的不連續。
-
并發安全
除如何劃分可用空間之外,還有另外一個需要考慮的問題是對象創建在虛擬機中是非常頻繁的行為,即使是僅僅修改一個指針所指向的位置,在并發情況下也并不是線程安全的,可能出現正在給對象A分配內存,指針還沒來得及修改,對象B又同時使用了原來的指針來分配內存的情況。
? CAS機制:
解決這個問題有兩種方案,一種是對分配內存空間的動作進行同步處理——實際上虛擬機采用CAS配上失敗重試的方式保證更新操作的原子性;
注意一下:A-B-A 即不關心中間過程,最終還是你的就行。
? ? 分配緩沖
另一種是把內存分配的動作按照線程劃分在不同的空間之中進行,即每個線程在Java堆中預先分配一小塊私有內存,也就是本地線程分配緩沖(Thread Local Allocation Buffer,TLAB),如果設置了虛擬機參數 -XX:UseTLAB,在線程初始化時,同時也會申請一塊指定大小的內存,只給當前線程使用,這樣每個線程都單獨擁有一個Buffer,如果需要分配內存,就在自己的Buffer上分配,這樣就不存在競爭的情況,可以大大提升分配效率,當Buffer容量不夠的時候,再重新從Eden區域申請一塊繼續使用。
TLAB的目的是在為新對象分配內存空間時,讓每個Java應用線程能在使用自己專屬的分配指針來分配空間,減少同步開銷。
TLAB只是讓每個線程有私有的分配指針,但底下存對象的內存空間還是給所有線程訪問的,只是其它線程無法在這個區域分配而已。當一個TLAB用滿(分配指針top撞上分配極限end了),就新申請一個TLAB。
3.內存空間初始化
(注意不是構造方法)內存分配完成后,虛擬機需要將分配到的內存空間都初始化為零值(如int值為0,boolean值為false等等)。這一步操作保證了對象的實例字段在Java代碼中可以不賦初始值就直接使用,程序能訪問到這些字段的數據類型所對應的零值。
4.設置
接下來,虛擬機要對對象進行必要的設置,例如這個對象是哪個類的實例、如何才能找到類的元數據信息、對象的哈希碼、對象的GC分代年齡等信息。這些信息存放在對象的對象頭之中。
5.對象初始化
在上面工作都完成之后,從虛擬機的視角來看,一個新的對象已經產生了,但從Java程序的視角來看,對象創建才剛剛開始,所有的字段都還為零值。所以,一般來說,執行new指令之后會接著把對象按照程序員的意愿進行初始化,這樣一個真正可用的對象才算完全產生出來。
5.2對象的內存布局
在HotSpot虛擬機中,對象在內存中存儲的布局可以分為3塊區域:對象頭(Header)、實例數據(Instance Data)和對齊填充(Padding)。
對象頭包括兩部分信息,第一部分用于存儲對象自身的運行時數據,如哈希碼(HashCode)、GC分代年齡、鎖狀態標志、線程持有的鎖、偏向線程ID、偏向時間戳等。
對象頭的另外一部分是類型指針,即對象指向它的類元數據的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。
第三部分對齊填充并不是必然存在的,也沒有特別的含義,它僅僅起著占位符的作用。由于HotSpot VM的自動內存管理系統要求對對象的大小必須是8字節的整數倍。當對象其他數據部分沒有對齊時,就需要通過對齊填充來補全。
5.3對象的訪問定位
-
句柄
如果使用句柄訪問的話,那么Java堆中將會劃分出一塊內存來作為句柄池,reference中存儲的就是對象的句柄地址,而句柄中包含了對象實例數據與類型數據各自的具體地址信息。
-
直接指針
如果使用直接指針訪問, reference中存儲的直接就是對象地址。
這兩種對象訪問方式各有優勢,使用句柄來訪問的最大好處就是reference中存儲的是穩定的句柄地址,在對象被移動(垃圾收集時移動對象是非常普遍的行為)時只會改變句柄中的實例數據指針,而reference本身不需要修改。
使用直接指針訪問方式的最大好處就是速度更快,它節省了一次指針定位的時間開銷,由于對象的訪問在Java中非常頻繁,因此這類開銷積少成多后也是一項非??捎^的執行成本。
對Sun HotSpot而言,它是使用直接指針訪問方式進行對象訪問的。
6.虛擬機優化技術(后期)——逃逸分析
定義:逃逸分析是目前JVM中比較前沿的優化技術,它不是直接的優化手段而是為其他優化手段提供依據的分析技術。逃逸分析的基本行為就是分析對象動態作用域。
棧上分配:
虛擬機提供的一種優化技術,基本思想是,對于線程私有的對象,將它打散分配在棧上,而不分配在堆上。好處是對象跟著方法調用自行銷毀,不需要進行垃圾回收,可以提高性能。
棧上分配需要的技術基礎,逃逸分析。逃逸分析的目的是判斷對象的作用域是否會逃逸出方法體。
注意,任何可以在多個線程之間共享的對象,一定都屬于逃逸對象。
虛擬機有一個逃逸技術,默認是開啟的。
- 牽涉到的JVM參數:
-XX:+DoEscapeAnalysis:啟用逃逸分析(默認打開) -XX:-DoEscapeAnalysis:關閉逃逸分析
-XX:+UseTLAB 本地線程分配緩沖(默認打開) 開啟了逃逸分析技術,這個也要開。否則對象是沒有地方分配的。
-XX:+EliminateAllocations:標量替換(默認打開),即一個標準
這三個都要打開,逃逸技術才起作用
其他一個可記: -XX:+PrintGC 打印垃圾回收的過程
使用堆和棧的好處是不同的:
使用棧,它就可以跟著線程來跑。
使用堆呢,你就必須要涉及到垃圾回收。使用棧的話,線程關閉了,它就沒有了。
7.面試會問那哪些呢?
- 這些概念 注意內存
- 程序計數器的問題
- 會問到棧,棧的特點 。JVM中為什么用棧,可以從方法和方法之間的調用去解釋。
- 虛擬棧的大小設置,比如虛擬機棧很有可能出現異常
- 寫遞歸一定要注意及時跳出。
- 是不是所有對象都在堆上面分配?
----不是的,因為有例外,在棧上面分配
總結
以上是生活随笔為你收集整理的深入解剖JVM内存区域的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 国产无线蓝牙耳机什么牌子好一点?国产无线
- 下一篇: android beam传输速率,S B