-
1 # 程式設計師小石同學
-
2 # 網際網路小杜
1.程式計數器(執行緒私有)
程式計數器是一塊較小的記憶體空間,可以看作是當前執行緒所執行位元組碼的行號指示器。
分支、迴圈、跳轉、異常處理、執行緒處理等基礎功能都需要依賴這個計數器完成。
由於Java虛擬機器的多執行緒是透過執行緒輪流切換並分配處理器執行時間的方式實現的。為了執行緒切換後能恢復到正確的執行位置,
每條執行緒都需要一個獨立的程式計數器,各執行緒之間的計數器互不影響,獨立儲存。
1.如果執行緒正在執行的是Java方法,計數器記錄的正在執行的虛擬位元組碼指令的地址;
2.如果正在執行的是Native方法,這個計數器的值為空。
程式計數器是唯一一個沒有規定任何OutOfMemoryError的區域。
2.Java虛擬機器棧(執行緒私有)
Java虛擬機器棧是執行緒私有的,生命週期與執行緒相同。虛擬機器棧描述的是Java方法執行的記憶體模型:每個方法被執行的時候都會建立一個棧幀,儲存
1.區域性變量表
2.操作棧
3.動態連結
4.方法出口
每一個方法被呼叫到執行完成的過程,就對應著一個棧幀在虛擬機器棧中從入棧到出棧的過程。
這個區域有兩種異常情況:
1.StackOverflowError:執行緒請求的棧深度大於虛擬機器所允許的深度
2.OutMemoryError:虛擬機器棧擴充套件到無法申請足夠的記憶體時
3.本地方法棧(執行緒私有)
虛擬機器棧為虛擬機器執行Java方法(位元組碼)服務。
本地方法棧(Native Method Stacks)為虛擬機器使用到的Native方法服務。
4.Java 堆(執行緒共享)
Java 堆(Java Heap)是 Java 虛擬機器中記憶體最大的一塊。Java 堆在虛擬機器啟動時建立,被所有執行緒共享。
作用:存放物件例項。垃圾收集器主要管理的就是 Java 堆。Java 堆在物理上可以不連續,只要邏輯上連續即可。
5.方法區(執行緒共享)
方法區(Method Area)被所有執行緒共享,用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。
和 Java 堆一樣,不需要連續的記憶體,可以選擇固定的大小,更可以選擇不實現垃圾收集。
6.執行時常量池
執行時常量池(Runtime Constant Pool)是方法區的一部分。儲存 Class 檔案中的符號引用、翻譯出來的直接引用。執行
-
3 # 會點程式碼的大叔
JVM 執行時資料區域大致可以分為:程式計數器、虛擬機器棧、本地方法棧、堆區、元空間、執行時常量池、直接記憶體等區域;就是下面這個樣子的:
其中有些區域,隨著 JDK 版本的升級不斷調整,例如:
JDK 1.6,字串常量池位於永久代的執行時常量池中;
JDK 1.7,字串常量池從永久代剝離,放入了堆中;
JDK 1.8,元空間取代了永久代,並且放入了本地記憶體(Native memory)中。
以上幾個區域,按照執行緒公有還是私有可分為:
執行緒隔離:程式計數器、虛擬機器棧、本地方法棧;
執行緒公有:其它的都是執行緒共享的區域。
執行緒私有1. 程式計數器
一個 CPU 在某個時間點,只能做一件事情,在多執行緒的情況下,CPU 執行時間被劃分成若干個時間片,分配給各個執行緒執行;
程式計數器的作用就是記錄當前執行緒執行的位置,當執行緒被切換回來的時候,能夠找到該執行緒上次執行到哪兒了;所以程式計數器一定是執行緒隔離的。
2. 虛擬機器棧和本地方法棧
虛擬機器棧:每個 Java 方法在執行的同時,會建立一個棧幀,用於儲存區域性變量表、運算元棧、常量池引用等資訊;方法的呼叫過程,就是一個棧幀在 Java 虛擬機器棧中入棧和出棧的過程;
本地方法棧:和虛擬機器棧很類似,區別在於虛擬機器棧為 Java 方法服務,本地方法棧為 Native 方法服務;其中 Native 方法可以看做用其它語言(C、C++ 或組合語言等)編寫的方法;
HotSpot 虛擬機器就選擇了將虛擬機器棧和本地方法棧合併在了一起;
為了保證執行緒中的區域性變數不被別的執行緒訪問到,所以虛擬機器棧和本地方法棧是執行緒隔離的。執行緒公有1. 堆區
對於堆疊的區別總結一句話:堆中存物件,棧中存基本資料型別和堆中物件的引用;一個物件的大小是可以動態變化的,而引用是固定大小的。
這麼看就容易理解堆為什麼是執行緒公有的了,省地兒啊。
2. 元空間區/方法區
方法區用於存放已被載入的類資訊、常量、靜態變數、即編譯器編譯後的程式碼等。
還有要注意的一點:方法區是 JVM 的規範,在 JDK 1.8 之前,方法區的實現是永久代;從 JDK 1.8 開始 JVM 移除了永久代,使用本地記憶體來儲存元資料並稱之為:元空間(Metaspace)。
3. 執行時常量池
Class 檔案中的常量池,會在類載入後被放入這個區域。
另外在 JDK 1.7 之前,字串常量池就在執行時常量池中,後來字串常量池放入了堆中,而執行時常量池仍然在方法區(元空間區)中。
有興趣的朋友可以自己測試一下,以死迴圈方式建立字串常量,JDK 1.6 會報永久代 OOM ;JDK 1.7 會報堆區 OOM 。
4. 直接記憶體
也叫做堆外記憶體,並不是虛擬機器執行時資料區的一部分,也不是Java 虛擬機器規範中定義的記憶體區域。
JDK 1.4 加入的 NIO 類,引入了一種基於通道 ( Channel ) 與緩衝區 ( Buffer ) 的 I/O 方式,它可以使用 native 函式庫直接分配堆外記憶體,然後透過堆上的DirectByteBuffer物件對這塊記憶體進行引用和操作。
簡單來說,直接記憶體就是 JVM 記憶體之外有一塊記憶體區域,我們透過堆上的一個物件可以操作它;具體等講到 NIO 部分的時候,再回來加深理解。
-
4 # Java架構師CAT
概述
對於 Java 程式設計師來說,在虛擬機器自動記憶體管理機制下,不再需要像 C/C++程式開發程式設計師這樣為每一個 new 操作去寫對應的 delete/free 操作,不容易出現記憶體洩漏和記憶體溢位問題。正是因為 Java 程式設計師把記憶體控制權利交給 Java 虛擬機器,一旦出現記憶體洩漏和溢位方面的問題,如果不瞭解虛擬機器是怎樣使用記憶體的,那麼排查錯誤將會是一個非常艱鉅的任務。
執行時資料區域
Java 虛擬機器在執行 Java 程式的過程中會把它管理的記憶體劃分成若干個不同的資料區域。
執行緒私有的:
程式計數器 虛擬機器棧 本地方法棧執行緒共享的:
堆 方法區 直接記憶體 (非執行時資料區的一部分)
回覆列表
Java虛擬機器(JVM)定義了在程式執行期間使用的各種執行時資料區域,這些JVM資料區域中的某些區域是按執行緒建立的,其他區域則是在JVM啟動時建立的,並且記憶體區域線上程之間共享。
根據使用情況,JVM執行時資料區域可分為六個區域:
程式計數器虛擬機器棧本地方法棧堆方法區執行時常量池如上所述,這些儲存區域可以分為兩類:
執行緒私有(程式計數器,虛擬機器棧,本機方法棧)執行緒共享(堆,方法區,執行時常量池)程式計數器在JVM中,在任何給定時間,可能正在執行許多執行緒。每個執行執行緒都有自己的PC暫存器。
如果JVM執行緒執行的方法是JAVA方法,則PC暫存器包含當前正在執行的Java虛擬機器指令的地址。如果執行緒正在執行本機方法,則Java虛擬機器的pc暫存器的值未定義。
虛擬機器棧每個JVM執行緒都有自己的JVM堆疊,該堆疊線上程啟動時建立。JVM堆疊儲存被推入堆疊並從堆疊彈出的框架,而JVM堆疊永遠不會被直接操縱。在發生任何異常時,您都可以透過此堆疊跟蹤獲取每個元素代表一個堆疊框架的位置。
與Java虛擬機器堆疊相關的特殊條件:
如果執行緒中的計算需要比允許的Java虛擬機器更大的堆疊,則Java虛擬機器將丟擲StackOverflowError。如果可以動態擴充套件Java虛擬機器堆疊,並且嘗試進行擴充套件,但是可以提供足夠的記憶體來實現擴充套件,或者如果沒有足夠的記憶體來為新執行緒建立初始Java虛擬機器堆疊,則Java虛擬機器機器丟擲OutOfMemoryError。虛擬機器棧中的框架
呼叫方法時會建立一個新框架,然後將該框架推入該執行緒的JVM堆疊中。當框架的方法呼叫完成時,該框架將被銷燬。
每個框架都有自己的區域性變數陣列,自己的運算元堆疊以及對當前方法類的執行時常量池的引用。區域性變數陣列和運算元堆疊的大小在編譯時確定,並與與幀關聯的方法的程式碼一起提供。
在任何時候,只有一個幀處於活動狀態,這是執行方法的幀。該幀稱為當前幀,其方法稱為當前方法。定義當前方法的類是當前類。
請注意,由執行緒建立的框架在該執行緒本地,並且不能被任何其他執行緒引用。
區域性變數 -建立並新增到JVM堆疊的每個框架都包含一個稱為其區域性變數的變數陣列。區域性變數陣列的長度在編譯時自行確定,並以類或介面的二進位制表示形式以及與框架關聯的方法的程式碼提供。運算元堆疊 –每個幀都包含一個稱為幀的運算元堆疊的後進先出(LIFO)堆疊。運算元堆疊的最大深度稱為編譯時間本身,並與與幀關聯的方法的程式碼一起提供。執行動態連結 -在已編譯的.class檔案中,方法的程式碼是指要呼叫的方法和要透過符號引用訪問的變數。這些符號方法引用透過動態連結轉換為具體的方法引用,並根據需要載入類以解析符號在那個時候還沒有定義。本地方法棧JVM也可以使用常規堆疊來支援本地方法,本地方法是用Java程式語言以外的其他語言編寫的方法。建立每個執行緒時,將為每個執行緒分配本地方法棧。
以下異常條件與本機方法堆疊相關聯:
如果執行緒中的計算所需的本機方法堆疊超出允許的範圍,則Java虛擬機器將引發StackOverflowError。如果可以動態擴充套件本機方法堆疊並嘗試進行本機方法堆疊擴充套件,但可以提供足夠的記憶體,或者可以提供足夠的記憶體來為新執行緒建立初始本機方法堆疊,則Java虛擬機器將引發OutOfMemoryError。堆堆是JVM執行時資料區域,從中可以將記憶體分配給物件,例項變數和陣列。堆是在JVM啟動時建立的,並在所有Java虛擬機器執行緒之間共享。一旦儲存在堆中的物件沒有任何引用,該物件的記憶體就會被垃圾收集器回收,該垃圾收集器是一種自動儲存管理系統。物件永遠不會顯式釋放。
以下異常情況與堆相關聯:
如果計算需要的堆多於自動儲存管理系統可以提供的堆,則Java虛擬機器將引發OutOfMemoryError。方法區JVM具有在所有JVM執行緒之間共享的方法區域。方法區域儲存有關已載入的類和介面的元資料。它儲存每個類的結構,例如執行時常量池,欄位和方法資料,以及方法和建構函式的程式碼。
對於JVM載入的每種型別,儲存在方法區域中的型別資訊如下-
類/介面的全限定名稱。任何直接超類的完全限定名稱。使用的修飾符。任何擴充套件超級介面的全限定名稱。區分載入型別是類還是介面的資訊。除型別資訊方法區域外,還儲存:
執行時常量池。欄位資訊,包括欄位名稱,型別,修飾符。方法資訊,包括方法名稱,修飾符,返回型別,引數。靜態(類)變數。方法程式碼,包含位元組碼,區域性變數大小,運算元堆疊大小。方法區域通常是非堆儲存器的一部分,以前被稱為PermGen空間。注意, PermGen Space已從Java 8更改為MetaSpace。
以下異常條件與方法區域相關聯:
如果無法使方法區域中的記憶體可用以滿足分配請求,則Java虛擬機器將丟擲OutOfMemoryError。執行時常量池執行時常量池是該類的constant_pool表的每個類或每個介面儲存。Constant_pool包含在編譯時已知的常量(字串文字,數字文字),它還儲存必須在執行時解析的方法和欄位引用。
執行時常量池線上程之間共享,並從JVM的方法區域分配。
不是將所有內容儲存在位元組碼中,而是為該類維護單獨的常量池,並且位元組碼包含對常量池的引用。這些符號參考透過動態連結轉換為具體的方法參考。