首頁 >Java >java教程 >JVM之記憶體管理

JVM之記憶體管理

高洛峰
高洛峰原創
2016-11-21 14:00:221636瀏覽

  都說搞C的牛叉,那是因為C解決問題,全靠程式設計師自己,他們對自己的程式在記憶體中是什麼樣瞭如指掌。而Java呢不需要有太多作業系統的知識,不用時時注意記憶體的問題,但這不代表我們就不用去了解它背後的原理,(其實我是被公司的C嘲諷了一下OoO)。 Java之所以容易上手,那是因為最困難的問題,已經被前人解決了,而這一切都歸功於Java Virtual Machine-Java虛擬機,JVM其實就是一台抽象的計算機,它有自己的指令集,有自己的機器語言,有自己的記憶體管理。本系列會一一解開它的真面目。

  本文基於Java HotSpot™ 虛擬機,JDK 1.8,將討論:

JVM 內部結構

JVM 記憶體管理

JVM 記憶體模型

JVM 記憶體管理JVM之記憶體管理

JVM 記憶體模型

一個程式執行的過程是這樣的,以C語言為例,原始碼首先被編譯成可執行文件,以二進制的形式存放到磁碟上,當執行時,首先從磁碟載入到記憶體中,然後處理器就開始執行目標程式中機器指令。反觀Java,先編譯成字節碼文件,與平台無關,JVM透過ClassLoader載入到記憶體中,然後執行其中的機器指令,JVM幫我們跟作業系統打交道。有了字節碼和JVM,Java實現了平台無關性。 JVM也可以認為是一個進程,在啟動時申請一塊內存,然後按照功能的不同,把內存分為以下不同區域:

(1) Heap

  堆,一個非常重要的區域,被所有線程共享,基本所有的物件實例都在這裡分配,大部分的垃圾回收也都發生在這裡。這部分內存,由JVM使用Garbage Collector(自動內存管理工具)來管理,職責就是為物件分配內存,釋放空閒內存。 Java 堆的大小可以使用參數來控制是固定的,還是動態擴充的。

(2) JVM Stacks

  棧,與執行緒息息相關,執行緒私有,隨執行緒生而生,死而死,是執行緒執行工作的記憶體。 HotSpot中Java棧和本地方法棧合而為一,都在本地記憶體空間中分配,這部分記憶體就不需要JVM刻意的去管理了。 JVM棧主要用來儲存棧幀,當呼叫一個方法時建立一個棧幀,方法結束銷毀,從棧的角度來看,就是入棧和出棧兩個操作。

  棧幀是一個資料結構,用來儲存局部變量,操作數棧,和當前類別運行時常數池的參考。局部變數數組:用來保存方法內定義的基本型別變量,下標從0 開始,JVM使用局部變數表傳遞方法參數,當呼叫一個實例方法,第0 位置儲存的是目前物件的this 參考;運算元堆疊:用來執行運算和準備呼叫方法的參數以及方法的回傳結果;動態連結:引用物件的運行時常數池。

(3) PC Register

  程式計數器,線程私有,主要作用就是儲存指令位址,取指,解碼和執行。每個執行緒都關聯著唯一的堆疊和PC暫存器。

(4) Metaspace

  元空間,在JDK8之前的HotSpot VM稱之為方法區或永久代,被各線程共享,它存儲了一個類的結構信息,如常量池、字段、方法等。存放在本地記憶體中,與堆不相關。

(5) Native Method Stacks

  JVM棧是為Java方法準備的,那麼本地方法棧則是為虛擬機器呼叫本地方法服務的。

2. JVM 記憶體管理

  Java不允許直接操作內存,記憶體的申請和釋放統一交給虛擬機處理。

2.1 Garbage Collection 自動記憶體管理

  自動記憶體管理(以下簡稱GC)的職責:

分配記憶體

確保引用對象保留在記憶體

回收不可達引用物件的記憶體的大部分記憶體記憶體分配問題,它本身也佔用一定的資源。當堆滿了或它的某個組成部分達到一個閥值就會觸發垃圾回收。垃圾回收主要從這幾方面考慮:回收的頻率和時間,例如堆小那麼回收的次數就多,堆大回收次數少,但回收一次的時間長;記憶體碎片問題;多執行緒程式下的垃圾回收。

垃圾回收策略:

(1)串列與並行(Serial versus Parallel)

  串行,同一時間只能有一個垃圾回收線程工作;並行,在多CPU系統中,可有多個垃圾回收線程同時工作。

(2)併發與Stop-the-world(Concurrent versus Stop-the-world)

  Stop-the-world 垃圾回收器,在回收期間,應用程式完全暫停工作,此時堆就相當於被凍結了,物件的狀態不可變;並發,一個或多個垃圾收集任務與應用程式同時執行,可能會出現短暫的Stop-the-world,在收集時,物件的狀態可能會改變。

(3)複製(Copying)

  將內存分為兩半,回收時將存活的對象複製到另一半空間,然後清除當前內存,後續內存的分配比較容易,但內存的利用率比較低。

(4) 標記清除與標記整理(Compacting versus Non-compacting)

  標記清除,標記可回收的對象,統一進行回收,不進行內存壓縮,會產生大量的內存碎片,在分配大對象時,可能無法找到連續的記憶體;標記整理,在標記完後,先對記憶體進行壓縮整理,把所有存活的物件放到一起回收。

(5)分代收集

  把堆分成幾個區域,新生代和老年代,不同的區域使用上面的不同的回收方法。

2.2 HotSpot 中的分代收集

  在HotSpot 中,把內存分為新生代和老年代,新生代又分為Eden和兩個大小一樣的Survivor空間,大部分對像在Eden分配,一些大對象可能直接分配到老年代。

堆的結構如下:

JVM之記憶體管理

圖 2 堆

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn