java虛擬機器

來源:互聯網
上載者:User

標籤:階段   位元組   text   開發   消失   1.2   引入   現在   節點   

Java記憶體模型

程式計數器
記憶體中較小的記憶體空間,通過計數器的值可以選取下一條執行的位元組碼指令,分支、迴圈、跳轉、異常處理、線程恢複等基礎功能都需要依賴這個計數器來完成。

線程私人,生命週期跟線程相同。

如果正在執行一個Native方法,那麼這個計數器值將為空白。

虛擬機器棧
線程私人,生命週期跟線程相同。

每個方法在執行同時都會建立一個棧幀,用於儲存局部變數表、運算元棧、動態連結、方法出口等資訊。

在Java虛擬機器規範中,對這個地區規定了兩種異常情況:如果線程請求的棧深度大於虛擬機器所允許的深度,將拋出StackOverflowError異常;
如果虛擬機器棧可以動態擴充,如果擴充時無法申請到足夠的記憶體,就會拋出OutOfMemoryError異常。

本地方法棧
跟虛擬機器棧所發揮的作用相似,區別在於虛擬機器棧為虛擬機器執行Java(也就是位元組碼)服務,而本地方法棧則為虛擬機器使用到的Native方法服務。

Java堆
用於存放對象執行個體,是Java虛擬機器所管理的記憶體中最大的一塊,同時也是所有線程共用的一塊記憶體地區。

因為Java堆是垃圾收集器管理的主要區域,因此很多時候也被稱為“GC"堆。由於現在收集器基本都採用分代收集演算法,所以Java堆還可以細分為

新生代
老年代
永久代(永久代是Hotspot虛擬機器特有的概念,是方法區的一種實現,別的JVM都沒有這個東西。在Java 8中,永久代被徹底移除,取而代之的是另一塊與堆不相連的本地記憶體——元空間。)
當一個對象被建立時,它首先進入新生代,之後有可能被轉移到老年代中。

新生代存放著大量的生命很短的對象,因此新生代在三個地區中記憶體回收的頻率最高。為了更高效地進行記憶體回收,把新生代繼續劃分成以下三個空間:

Eden
From Survivor
To Survivor
方法區
與Java堆一樣,各個線程共用的記憶體地區,儲存已被虛擬機器載入的類資訊、常量、靜態變數、即使編譯器編譯後的代碼等資料。

運行時常量池
方法區的一部分,用於存放編譯器產生的各種字面量和符號引用。

運行時常量池相對於class檔案常量池的另外一個重要特徵是具備動態性,Java語言並不要求常量一定只有編譯期才能產生,也就是並非預置入class檔案中常量池的內容才能進入方法區運行時常量池,運行期間也可能將新的常量放入池中,這種特性被開發人員利用得比較多的便是String類的intern()方法。

直接記憶體
在JDK1.4中新加入了NIO類,引入了一種基於通道與緩衝區的I/O方法,它可以使用Native函數庫直接分配堆外記憶體,然後通過一個儲存在Java堆中的DirectByteBuffer對象作為這塊記憶體的引用進行操作。
堆外記憶體之 DirectByteBuffer詳解

HotSpot虛擬機器對象
對象的建立
在語言層上,建立對象通常僅僅是一個new關鍵字而已,而當虛擬機器遇到一條new執行時,將由一下步驟:

檢查類是否載入、解析、初始化過,沒有則先執行相應的類載入過程。
在堆中分配記憶體

劃分可用空間:

指標碰撞:堆記憶體規整
空閑列表:堆記憶體不規整
並發問題

同步:採用CAS配上失敗重試的方式保證更新操作的原子性
把記憶體配置動作按照線程劃分在不同的空間之中進行
將分配到的記憶體空間都初始化零值
設定對象的類執行個體、中繼資料、雜湊碼、GC分代年齡等資訊。
執行<init>方法
對象的記憶體布局
對象在記憶體中儲存的布局可以分為3塊地區:

對象頭

對象運行時資料、雜湊碼、GC分代年齡、鎖狀態標記、線程持有的鎖、偏向線程ID等
類型執行:即對象執向它的類別中繼資料的指標,指明對象資料哪個類的執行個體。
執行個體資料

對象真正儲存的有效資訊
對齊填充

預留位置作用
對象的訪問定位
控制代碼定位
直接指標
垃圾收集器
程式計數器、虛擬機器棧和本地方法棧這三個地區屬於線程私人的,只存在於線程的生命週期內,線程結束之後也會消失,因此不需要對這三個地區進行記憶體回收。記憶體回收主要是針對 Java 堆和方法區進行。

判斷對象是否死亡
引用計數演算法
給對象添加一個引用計數器,每當有一個地方引用它,計數器值就加1;引用時效時,計算機值就減1;當計數器值為0的對象就是不可能再被使用的。

當兩個對象相互引用時,此時引用計數器的值永遠不為0,導致無法對它們進行記憶體回收。

public class ReferenceCountingGC {
public Object instance = null;

    public static void testGC() {        ReferenceCountingGC objA = new ReferenceCountingGC();        ReferenceCountingGC objB = new ReferenceCountingGC();        objA .instance = objB ;        objB .instance = objA ;        objA = null;        objB = null;        System.gc();    }}

可達性分析演算法
以GC Roots為起始點,從這些節點開始向下搜尋,能夠搜尋到的對象都是存活的,不可達的對象則為不可用。

在Java語言中,可作為GC Roots的對象包括下面幾種:

虛擬機器棧中引用的對象
方法區中靜態屬性引用的對象
方法區中常量引用的對象
本地方法棧中Native方法引用的對象
參考型別
無論是引用計數演算法還是可達性分析演算法判斷對象是否存活都與引用有關。在JDK1.2之後,Java對引用的概念進行了擴充,劃分為強度不同的四個的參考型別。

強引用
通過new來建立對象的參考型別,被強引用的對象永遠不會被垃圾收集器回收。

Object obj = new Object();
軟引用
通過SortReference類來實現,只有在記憶體不足的時候才會被回收。

Object obj = new Object();SoftReference<Object> sr = new SoftReference<Object>(obj);obj = null;

弱引用
通過WeakReference類來實現,只能存活到下一次垃圾收集發生之前。

Object obj = new Object();WeakReference<Object> wr = new WeakReference<Object>(obj);obj = null;

WeakHashMap 的 Entry 繼承自 WeakReference,主要用來實現緩衝。

private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V>
Tomcat 中的 ConcurrentCache 就使用了 WeakHashMap 來實現緩衝功能。ConcurrentCache 採取的是分代緩衝,經常使用的對象放入 eden 中,而不常用的對象放入 longterm。eden 使用 ConcurrentHashMap 實現,longterm 使用 WeakHashMap,保證了不常使用的對象容易被回收。

public final class ConcurrentCache<K, V> {

private final int size;private final Map<K, V> eden;private final Map<K, V> longterm;public ConcurrentCache(int size) {    this.size = size;    this.eden = new ConcurrentHashMap<>(size);    this.longterm = new WeakHashMap<>(size);}public V get(K k) {    V v = this.eden.get(k);    if (v == null) {        v = this.longterm.get(k);        if (v != null)            this.eden.put(k, v);    }    return v;}public void put(K k, V v) {    if (this.eden.size() >= size) {        this.longterm.putAll(this.eden);        this.eden.clear();    }    this.eden.put(k, v);}

}
虛引用
也稱為幽靈引用或者幻影引用,是最弱的一種參考關聯性。

通過PhantomReference類來實現,為一個對象設定虛引用關聯的唯一目的就是能在這個對象被收集器回收時收到一個系統通知。

Object obj = new Object();PhantomReference<Object> wr = new PhantomReference<Object>(obj, null);obj = null;

垃圾收集演算法
標記清除
演算法分為“標記”跟“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成之後統一回收所有被標記的對象。

不足:

效率問題,標記跟清除兩個過程的效率都不高
空間問題,標記清除之後會產生大量不連續的記憶體片段。

複製演算法
將記憶體分為大小相等的兩塊,每次只使用其中的一塊,當這塊記憶體用完了,就將還存活的對象負責到另一塊上面,然後再把一是要難過過得記憶體空間一次清理掉。

不足:

代價太高,只使用一半記憶體。

標記整理演算法
首先標記出所有需要回收的對象,然後將所有存活的對象都向一端移動,最後清理掉端邊界以外的記憶體。

分代收集演算法
根據對象的存活周期將記憶體劃分為幾塊。一般將Java堆分為新生代跟老年代,這樣就可以根據各個年代的特點採用最適當的收集演算法。

新生代:複製演算法
老年代:標記整理或標記清除演算法。

java虛擬機器

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.