我們知道,在C 語言裡,如果想使用一個對象,需要對其進行new操作;如果不用這個對象了,需要對其進行delete操作。一旦開發人員忘記寫delete語句了,就會造成記憶體外洩。 【記憶體被物件佔用著不還,就叫記憶體外洩。 】
而java就聰明了,它從“手動”進化成了“自動”,把內存的控制權力交給了虛擬機。下面我們就來窺探一下jvm是怎麼進行自動記憶體管理的。
自動記憶體管理分為兩部分:
給物件分配記憶體和回收分配給物件的記憶體。在本篇我們說說前者,也就是記憶體劃分和記憶體分配。下篇再說GC(垃圾回收)。
1、記憶體分割
我們來看看虛擬機器記憶體裡都有什麼東西。 JVM的記憶體區域大致分為Class檔案、類別裝載子系統、執行時期資料區、執行引擎。今天我們只說說運行時資料區。 【這張圖是基於JDK7的。 JDK7以前,常量池是存放在方法區的。從JDK7以後,常量池放到了堆中。 】
線程公有
在執行時資料區中,方法區和堆是屬於執行緒公有的,也就是這兩塊區域是「循環利用」的,所以要對其進行垃圾回收。其是在虛擬機器啟動時創建。
執行緒私有
虛擬機堆疊、本機方法堆疊、程式計數器是屬於執行緒私有的,其與執行緒「同生死」,屬於「一次性」的,所以不用對其進行垃圾回收。
(一)方法區
儲存已被虛擬機載入的類別信息,常數,靜態變量,即時編譯器編譯後的程式碼等數據。
其中有一個運行時常數池。其儲存的是Class檔案中描述的符號引用,直接引用。在編譯期和執行期都可以將新的常數放入此池子中。
(2) 堆疊
概念:如果說堆疊解決的是程式執行問題,即程式如何處理資料;則堆疊解決的是資料儲存問題,也就是資料怎麼放,放在哪裡。
特點:
a、堆是虛擬機器記憶體中最大的一塊,大概佔記憶體的四分之三。例如一個32位元windows平台中每個進程有2GB的內存,則一般將1.5GB的內存劃分給堆。可見堆的所佔空間之大。
b、可處於物理上不連續的記憶體空間中,只要邏輯上是連續的即可。
作用:
存放物件實例,幾乎所有的物件實例都在這裡分配記憶體。
分類:
從記憶體回收的角度看,分為新生代和老年代。
從記憶體分配的角度來看,可分割出多個執行緒私有的分配緩衝區。
(3)虛擬機器堆疊
虛擬機堆疊裡面儲存的是堆疊幀,堆疊幀裡面儲存的是局部變數表,操作數棧,動態鏈接,方法出口等資訊。
堆疊中的堆疊幀
每個方法在執行的同時都會建立一個堆疊幀,一個方法從呼叫到執行完成的過程,就對應一個堆疊幀在虛擬機器堆疊中入棧到出棧的過程。
堆疊幀中的局部變數表
存放的是編譯期可知的各種基本資料類型,物件引用,returnAddress類型。所以其所需的記憶體空間在編譯期間就能完成分配,在運作期間不會改變其大小。
在分配基本資料型別所佔的空間時,除了64位元的long和double型別的資料會佔用2個局部變數空間,其餘的資料型別只佔用1個。
(4)本地方法堆疊
本地方法堆疊和虛擬機器堆疊的作用是相同的,只不過虛擬機器堆疊執行的是java方法,本地方法棧執行的是Native方法。
java方法就是開發人員寫的java程式碼,Native方法就是一個java呼叫非java程式碼的介面。
(5)程式計數器
程式計數器中存放的是目前執行緒所執行的字節碼的行號。 jvm工作時,就是透過改變這個計數器的值來選取一條需要執行的字節碼指令。
2、記憶體分配
這部分我們說物件在java堆中是如何分配,佈局和存取的,以及記憶體分配的原則。
物件的建立
我們用new來建立對象,來看看系統運行到new時,虛擬機器在幹嘛。此時的類別就像一塊肉,他要經過層層安檢,才能到達人類的飯桌。第一步:查看在常數池中是否有對應的符號參考。 【在方法區中進行】
第二步:查看此類是否已加載,解析和初始化過。 【在方法區中進行】
第三步:領取新生物件的記憶體。有兩種方式:指標碰撞和空閒列表。 【在堆中進行】
第四步:將分配到的記憶體空間初始化為零值。
第五步:對物件進行必要的設置,例如其是哪個類別的實例,物件的雜湊碼之類的。這些資訊存放在物件的物件頭之中
第六步:如果java程式碼中對物件進行了賦初值,則會進行第六步:執行方法。此方法的作用就是對物件進行初始化。
物件的記憶體佈局
#物件在記憶體中的儲存佈局分為3部分:物件頭實例資料對齊填充
物件頭
物件頭裡面有兩部分資訊:
(1)運行時數據,包括哈希碼,GC分代年齡,鎖狀態標誌等。
(2)類型指針,虛擬機器透過這個指針來決定這個物件是哪個類別的實例。
實例資料
實例資料中存放的是程式碼中定義的各種類型的欄位內容。
對齊填充
對齊填充起的是佔位符的作用,不是必然存在的。其只要保證物件的大小是8位元組的整數倍即可。
物件的存取定位
#建立完物件後,我們就可以使用物件了。使用時,怎麼才能找到想找的對象?有兩種方式:句柄和直接指標
句柄:
句柄存取就是在java堆中分割出一塊記憶體來作為句柄池,句柄中包含了物件實例資料和類型資料各自特定的位址資訊。
直接指標:
直接指標之所以“直接”,是因為它去除了句柄這個中介。所以在速度上比句柄快。在HotSpot虛擬機器中,使用的是這種方式。
說完了物件在java堆中是如何分配,佈局和存取的,接下來我們說說記憶體分配的原則
記憶體分配的原則:
堆大致分為新生代,老年代,永久代。物件的記憶體分配主要分配在新生代的Eden區,少數情況下會直接分配到老年代。分配的規則不是100%固定的,取決於垃圾收集器組合和參數設定等。以下有幾個分配原則可供參考。
(1)物件優先在Eden分配。
(2)大物件直接進入老年代。
(3)長期存活的對象將進入老年代。
(4)動態物件年齡判定。
(5)空間分配擔保。
以上便是JAVA虛擬機器中關於記憶體的分割部分,更多問題請造訪PHP中文網:JAVA影片教學
以上是JAVA虛擬機器(JVM)詳細講解(二)-記憶體的劃分的詳細內容。更多資訊請關注PHP中文網其他相關文章!