溫馨提示×

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

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

如何理解Java代碼的內(nèi)存管理

發(fā)布時(shí)間:2021-11-25 14:23:34 來源:億速云 閱讀:131 作者:柒染 欄目:編程語言

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)如何理解Java代碼的內(nèi)存管理,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

小編通過幾個(gè)方面來介紹Java代碼的內(nèi)存管理。

有的代碼,GC根本就回收不了,直接系統(tǒng)掛掉。GC是一段程序,不是智能,他只回收他認(rèn)為的垃圾,而不是回收你認(rèn)為的垃圾。

GC垃圾回收:

Grabage Collection相信學(xué)過JAVA的人都知道這個(gè)是什么意思.但是他是如何工作的呢?

首先,JVM在管理內(nèi)存的時(shí)候?qū)τ谧兞康墓芾砜偸欠中聦?duì)象和老對(duì)象。新對(duì)象也就是開發(fā)者new出來的對(duì)象,但是由于生命周期短,那么他占用的內(nèi)存并不是馬上釋放,而是被標(biāo)記為老對(duì)象,這個(gè)時(shí)候該對(duì)象還是要存在一段時(shí)間。然后由JVM決定他是否是垃圾對(duì)象,并進(jìn)行回收。

所以我們可以知道,垃圾內(nèi)存并不是用完了馬上就被釋放,所以就會(huì)產(chǎn)生內(nèi)存釋放不及時(shí)的現(xiàn)象,從而降低了內(nèi)存的使用。而當(dāng)程序浩大的時(shí)候。這種現(xiàn)象更為明顯,并且GC的工作也是需要消耗資源的。所以,也就會(huì)產(chǎn)生內(nèi)存浪費(fèi)。

JVM中的對(duì)象生命周期里談內(nèi)存回收:

對(duì)象的生命周期一般分為7個(gè)階段:創(chuàng)建階段,應(yīng)用階段,不可視階段,不可到達(dá)階段,可收集階段,終結(jié)階段,釋放階段。

創(chuàng)建階段:首先大家看一下,如下兩段代碼:

test1:

for( int i=0; i<10000; i++)  Object obj=new Object();

test2:

Object obj=null;  for( int i=0; i<10000; i++)  obj=new Object();

這兩段代碼都是相同的功能,但是顯然test2的性能要比test1性能要好,內(nèi)存使用率要高,這是為什么呢?原因很簡(jiǎn)單,test1每次執(zhí)行for循環(huán)都要?jiǎng)?chuàng)建一個(gè)Object的臨時(shí)對(duì)象,但是這些臨時(shí)對(duì)象由于JVM的GC不能馬上銷毀,所以他們還要存在很長(zhǎng)時(shí)間,而test2則只是在內(nèi)存中保存一份對(duì)象的引用,而不必創(chuàng)建大量新臨時(shí)變量,從而降低了內(nèi)存的使用。

另外不要對(duì)同一個(gè)對(duì)象初始化多次。例如:

public class A{  private Hashtable table = new Hashtable();  public A(){ table = new Hashtable();  // 這里應(yīng)該去掉,因?yàn)閠able已經(jīng)被初始化.  }  }

這樣就new了兩個(gè)Hashtable,但是卻只使用了一個(gè)。另外一個(gè)則沒有被引用.而被忽略掉.浪費(fèi)了內(nèi)存.并且由于進(jìn)行了兩次new操作.也影響了代碼的執(zhí)行速度。

應(yīng)用階段:即該對(duì)象至少有一個(gè)引用在維護(hù)他.

不可視階段:即超出該變量的作用域。這里有一個(gè)很好的做法,因?yàn)镴VM在GC的時(shí)候并不是馬上進(jìn)行回收,而是要判斷對(duì)象是否被其他引用在維護(hù).所以,這個(gè)時(shí)候如果我們?cè)谑褂猛暌粋€(gè)對(duì)象以后對(duì)其obj=null或者obj.doSomething()操作,將其標(biāo)記為空,可以幫助JVM及時(shí)發(fā)現(xiàn)這個(gè)垃圾對(duì)象.

不可到達(dá)階段:就是在JVM中找不到對(duì)該對(duì)象的直接或者間接的引用。

可收集階段,終結(jié)階段,釋放階段:此為回收器發(fā)現(xiàn)該對(duì)象不可到達(dá),finalize方法已經(jīng)被執(zhí)行,或者對(duì)象空間已被重用的時(shí)候。

JAVA的析構(gòu)方法:

可能不會(huì)有人相信,JAVA有析構(gòu)函數(shù)? 是的,有。因?yàn)镴AVA所有類都繼承至Object類,而finalize就是Object類的一個(gè)方法,這個(gè)方法在JAVA中就是類似于C++析構(gòu)函數(shù).一般來說可以通過重載finalize方法的形式才釋放類中對(duì)象.如:

public class A{  public Object a;  public A(){ a = new Object ;}  protected void finalize() throws java.lang.Throwable{  a = null; // 標(biāo)記為空,釋放對(duì)象  super.finalize(); // 遞歸調(diào)用超類中的finalize方法.  }  }

當(dāng)然,什么時(shí)候該方法被調(diào)用是由JVM來決定的\

一般來說,我們需要?jiǎng)?chuàng)建一個(gè)destory的方法來顯式的調(diào)用該方法.然后在finalize也對(duì)該方法進(jìn)行調(diào)用,實(shí)現(xiàn)雙保險(xiǎn)的做法.

由于對(duì)象的創(chuàng)建是遞歸式的,也就是先調(diào)用超級(jí)類的構(gòu)造,然后依次向下遞歸調(diào)用構(gòu)造函數(shù),所以應(yīng)該避免在類的構(gòu)造函數(shù)中初始化變量,這樣可以避免不必要的創(chuàng)建對(duì)象造成不必要的內(nèi)存消耗.當(dāng)然這里也就看出來接口的優(yōu)勢(shì).

數(shù)組的創(chuàng)建:

由于數(shù)組需要給定一個(gè)長(zhǎng)度,所以在不確定數(shù)據(jù)數(shù)量的時(shí)候經(jīng)常會(huì)創(chuàng)建過大,或過小的數(shù)組的現(xiàn)象.造成不必要的內(nèi)存浪費(fèi),所以可以通過軟引用的方式來告訴JVM及時(shí)回收該內(nèi)存.(軟引用,具體查資料).

例如:

Object obj = new char[10000000000000000];  SoftReference ref = new SoftReference(obj);

共享靜態(tài)存儲(chǔ)空間:

我們都知道靜態(tài)變量在程序運(yùn)行期間其內(nèi)存是共享的,因此有時(shí)候?yàn)榱斯?jié)約內(nèi)存工件,將一些變量聲明為靜態(tài)變量確實(shí)可以起到節(jié)約內(nèi)存空間的作用.但是由于靜態(tài)變量生命周期很長(zhǎng),不易被系統(tǒng)回收,所以使用靜態(tài)變量要合理,不能盲目的使用.以免適得其反。

因此建議在下面情況下使用:

1,變量所包含的對(duì)象體積較大,占用內(nèi)存過多.

2,變量所包含對(duì)象生命周期較長(zhǎng).

3,變量所包含數(shù)據(jù)穩(wěn)定.

4,該類的對(duì)象實(shí)例有對(duì)該變量所包含的對(duì)象的共享需求.(也就是說是否需要作為全局變量).

對(duì)象重用與GC:

有的時(shí)候,如數(shù)據(jù)庫操作對(duì)象,一般情況下我們都需要在各個(gè)不同模塊間使用,所以這樣的對(duì)象需要進(jìn)行重用以提高性能.也有效的避免了反復(fù)創(chuàng)建對(duì)象引起的性能下降.

一般來說對(duì)象池是一個(gè)不錯(cuò)的注意.如下:

public abstarct class ObjectPool{  private Hashtable locked,unlocked;  private long expirationTime;  abstract Object create();  abstract void expire( Object o);  abstract void validate( Object o);  synchronized Object getObject(){...};  synchronized void freeObject(Object o){...};  }

這樣我們就完成了一個(gè)對(duì)象池,我們可以將通過對(duì)應(yīng)的方法來存取刪除所需對(duì)象.來維護(hù)這快內(nèi)存提高內(nèi)存重用.

當(dāng)然也可以通過調(diào)用System.gc()強(qiáng)制系統(tǒng)進(jìn)行垃圾回收操作.當(dāng)然這樣的代價(jià)是需要消耗一些cpu資源.

不要提前創(chuàng)建對(duì)象:

盡量在需要的時(shí)候創(chuàng)建對(duì)象,重復(fù)的分配,構(gòu)造對(duì)象可能會(huì)因?yàn)槔厥兆鲱~外的工作降低性能.

JVM內(nèi)存參數(shù)調(diào)優(yōu):

強(qiáng)制內(nèi)存回收對(duì)于系統(tǒng)自動(dòng)的內(nèi)存回收機(jī)制會(huì)產(chǎn)生負(fù)面影響,會(huì)加大系統(tǒng)自動(dòng)回收的處理時(shí)間,所以應(yīng)該盡量避免顯式使用System.gc(),

JVM的設(shè)置可以提高系統(tǒng)的性能.例如:

java -XX:NewSize=128m -XX:MaxNewSize=128m -XX:SurvivorRatio=8 -Xms512m -Xmx512m

具體可以查看java幫助文檔.我們主要介紹程序設(shè)計(jì)方面的性能提高.

JAVA程序設(shè)計(jì)中有關(guān)內(nèi)存管理的其他經(jīng)驗(yàn):

根據(jù)JVM內(nèi)存管理的工作原理,可以通過一些技巧和方式讓JVM做GC處理時(shí)更加有效.,從而提高內(nèi)存使用和縮短GC的執(zhí)行時(shí)間.

1,盡早釋放無用對(duì)象的引用.即在不使用對(duì)象的引用后設(shè)置為空,可以加速GC的工作.(當(dāng)然如果是返回值.....)

2,盡量少用finalize函數(shù),此函數(shù)是JAVA給程序員提供的一個(gè)釋放對(duì)象或資源的機(jī)會(huì),但是卻會(huì)加大GC工作量.

3,如果需要使用到圖片,可以使用soft應(yīng)用類型,它可以盡可能將圖片讀入內(nèi)存而不引起OutOfMemory.

4,注意集合數(shù)據(jù)類型的數(shù)據(jù)結(jié)構(gòu),往往數(shù)據(jù)結(jié)構(gòu)越復(fù)雜,GC工作量更大,處理更復(fù)雜.

5,盡量避免在默認(rèn)構(gòu)造器(構(gòu)造函數(shù))中創(chuàng)建,初始化大量的對(duì)象.

6,盡量避免強(qiáng)制系統(tǒng)做垃圾回收.會(huì)增加系統(tǒng)做垃圾回收的最終時(shí)間降低性能.

7,盡量避免顯式申請(qǐng)數(shù)組,如果不得不申請(qǐng)數(shù)組的話,要盡量準(zhǔn)確估算數(shù)組大小.

8,如果在做遠(yuǎn)程方法調(diào)用.要盡量減少傳遞的對(duì)象大小.或者使用瞬間值避免不必要數(shù)據(jù)的傳遞.

9,盡量在合適的情況下使用對(duì)象池來提高系統(tǒng)性能減少內(nèi)存開銷,當(dāng)然,對(duì)象池不能過于龐大,會(huì)適得其反.

上述就是小編為大家分享的如何理解Java代碼的內(nèi)存管理了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

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

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

AI