溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Java虛擬機(jī)中內(nèi)存分配與回收策略的示例分析

發(fā)布時(shí)間:2022-03-04 11:32:20 來源:億速云 閱讀:138 作者:小新 欄目:開發(fā)技術(shù)

小編給大家分享一下Java虛擬機(jī)中內(nèi)存分配與回收策略的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

內(nèi)存分配與回收策略

Java技術(shù)體系的自動內(nèi)存管理,最根本的目標(biāo)是自動化地解決兩個(gè)問題:自動給對象分配內(nèi)存以及自動回收分配給對象的內(nèi)存。

1. 綜述

對象的內(nèi)存分配,從概念上講,應(yīng)該都是在堆上分配(而實(shí)際上也有可能經(jīng)過即時(shí)編譯后被拆散為標(biāo)量類型并間接地在棧上分配)。在經(jīng)典分代的設(shè)計(jì)下,新生對象通常會分配在新生代中,少數(shù)情況下(例如對象大小超過一定閾值)也可能會直接分配在老年代。對象分配的規(guī)則并不是固定的,《Java虛擬機(jī)規(guī)范》并未規(guī)定新對象的創(chuàng)建和存儲細(xì)節(jié),這取決于虛擬機(jī)當(dāng)前使用的是哪一種垃圾收集器,以及虛擬機(jī)中與內(nèi)存相關(guān)的參數(shù)的設(shè)定。

(1)、對象優(yōu)先在Eden分配

大多數(shù)情況下,對象在新生代Eden區(qū)中分配。當(dāng)Eden區(qū)沒有足夠空間進(jìn)行分配時(shí),虛擬機(jī)將發(fā)起一次Minor GC。

1. Eden區(qū)有足夠空間的情形

虛擬機(jī)參數(shù)

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8

參數(shù)說明

嘗試分配三個(gè)2MB大小和一個(gè)4MB大小的對象,在運(yùn)行時(shí)通過-Xms20M、-Xmx20M、-Xmn10M這三個(gè)參數(shù)限制了Java堆大小為20MB,不可擴(kuò)展,其中10MB分配給新生代,剩下的10MB分配給老年代。-XX:Survivor-Ratio=8決定了新生代中Eden區(qū)與一個(gè)Survivor區(qū)的空間比例是8∶1。

package com.xiao.test.Test;

public class test {
    private static final int _1MB = 1024 * 1024;
    public static void main(String[] args) {
        byte[] byte1,byte2,byte3;
        byte1 = new byte[2 * _1MB];
        byte2 = new byte[2 * _1MB];
        byte3 = new byte[2 * _1MB];   
    }
}

Java虛擬機(jī)中內(nèi)存分配與回收策略的示例分析

2. Eden區(qū)沒有足夠空間的情形

虛擬機(jī)參數(shù)相同

package com.xiao.test.Test;

public class test {
    private static final int _1MB = 1024 * 1024;
    public static void main(String[] args) {
        byte[] byte1,byte2,byte3,byte4;
        byte1 = new byte[2 * _1MB];
        byte2 = new byte[2 * _1MB];
        byte3 = new byte[2 * _1MB];
        byte4 = new byte[3 * _1MB];
    }
}

Java虛擬機(jī)中內(nèi)存分配與回收策略的示例分析

顯然進(jìn)行了Minor GC

(2)、大對象直接進(jìn)入老年代

1. 什么是大對象?

大對象就是指需要大量連續(xù)內(nèi)存空間的Java對象,最典型的大對象便是那種很長的字符串,或者元素?cái)?shù)量很龐大的數(shù)組。

2. Java虛擬機(jī)中要避免大對象的原因

在分配空間時(shí),它容易導(dǎo)致內(nèi)存明明還有不少空間時(shí)就提前觸發(fā)垃圾收集,以獲取足夠的連續(xù)空間才能安置好它們。而當(dāng)復(fù)制對象時(shí),大對象就意味著高額的內(nèi)存復(fù)制開銷。

3. 大對象直接進(jìn)入老年代的好處

避免在Eden區(qū)及兩個(gè)Survivor區(qū)之間來回復(fù)制,產(chǎn)生大量的內(nèi)存復(fù)制操作(HotSpot虛擬機(jī)提供了-XX:PretenureSizeThreshold參數(shù),指定大于該設(shè)置值的對象直接在老年代分配)。

(3)、長期存活的對象將進(jìn)入老年代

1. 虛擬機(jī)是怎樣判斷對象是否是長期存活?

內(nèi)存回收時(shí)就必須能決策哪些存活對象應(yīng)當(dāng)放在新生代,哪些存活對象放在老年代中。為做到這點(diǎn),虛擬機(jī)給每個(gè)對象定義了一個(gè)對象年齡(Age)計(jì)數(shù)器,存儲在對象頭中。

2. 對象年齡增加及晉升至老年代過程

對象通常在Eden區(qū)里誕生,如果經(jīng)過第一次Minor GC后仍然存活,并且能被Survivor容納的話,該對象會被移動到Survivor空間中,并且將其對象年齡設(shè)為1歲。對象在Survivor區(qū)中每熬過一次Minor GC,年齡就增加1歲,當(dāng)它的年齡增加到一定程度(默認(rèn)為15),就會被晉升到老年代中。對象晉升老年代的年齡閾值,可以通過參數(shù)-XX:MaxTenuringThreshold設(shè)置。

3. 長期存活的對象將進(jìn)入老年代的原因

我們都知道新生代的垃圾收集算法算法是標(biāo)記-復(fù)制算法,如果長期存活的對象仍然存放在新生代的話,那么就會帶來復(fù)制的開銷增大的問題。所以我們將大于某一年齡閾值的對象放入老年代,這樣可以減輕新生代垃圾回收時(shí)的壓力。

(4)、動態(tài)對象年齡判定

為了能更好地適應(yīng)不同程序的內(nèi)存狀況,HotSpot虛擬機(jī)并不是永遠(yuǎn)要求對象的年齡必須達(dá)到-XX:MaxTenuringThreshold才能晉升老年代,如果在Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對象就可以直接進(jìn)入老年代,無須等到-XX:MaxTenuringThreshold中要求的年齡。

(5)、空間分配擔(dān)保

1. 空間分配擔(dān)保的內(nèi)容

在發(fā)生Minor GC之前,虛擬機(jī)必須先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象總空間,如果這個(gè)條件成立,那這一次Minor GC可以確保是安全的。如果不成立,則虛擬機(jī)會先查看-XX:HandlePromotionFailure參數(shù)的設(shè)置值是否允許擔(dān)保失??;如果允許,那會繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對象的平均大小,如果大于,將嘗試進(jìn)行一次Minor GC,盡管這次Minor GC是有風(fēng)險(xiǎn)的;如果小于,或者 -XX:HandlePromotionFailure設(shè)置不允許冒險(xiǎn),那這時(shí)就要改為進(jìn)行一次Full GC。

2. “冒險(xiǎn)”是冒了什么風(fēng)險(xiǎn)

前面提到過,新生代使用復(fù)制收集算法,但為了內(nèi)存利用率,只使用其中一個(gè)Survivor空間來作為輪換備份,因此當(dāng)出現(xiàn)大量對象在Minor GC后仍然存活的情況——最極端的情況就是內(nèi)存回收后新生代中所有對象都存活,需要老年代進(jìn)行分配擔(dān)保,把Survivor無法容納的對象直接送入老年代,這與生活中貸款擔(dān)保類似。老年代要進(jìn)行這樣的擔(dān)保,前提是老年代本身還有容納這些對象的剩余空間,但一共有多少對象會在這次回收中活下來在實(shí)際完成內(nèi)存回收之前是無法明確知道的,所以只能取之前每一次回收晉升到老年代對象容量的平均大小作為經(jīng)驗(yàn)值,與老年代的剩余空間進(jìn)行比較,決定是否進(jìn)行Full GC來讓老年代騰出更多空間。

3. 那我們需要把擔(dān)保打開嗎?

取歷史平均值來比較其實(shí)仍然是一種賭概率的解決辦法,也就是說假如某次Minor GC存活后的對象突增,遠(yuǎn)遠(yuǎn)高于歷史平均值的話,依然會導(dǎo)致?lián)J?。如果出現(xiàn)了擔(dān)保失敗,那就只好老老實(shí)實(shí)地重新發(fā)起一次Full GC,這樣停頓時(shí)間就很長了。雖然擔(dān)保失敗時(shí)繞的圈子是最大的,但通常情況下都還是會將-XX:HandlePromotionFailure開關(guān)打開,避免Full GC過于頻繁。

以上是“Java虛擬機(jī)中內(nèi)存分配與回收策略的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI