溫馨提示×

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

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

Java項(xiàng)目中哪些情況會(huì)出現(xiàn)內(nèi)存泄漏

發(fā)布時(shí)間:2020-11-30 15:40:15 來(lái)源:億速云 閱讀:165 作者:Leah 欄目:開發(fā)技術(shù)

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)Java項(xiàng)目中哪些情況會(huì)出現(xiàn)內(nèi)存泄漏,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

內(nèi)存泄漏代碼示例

第一個(gè)例子非常簡(jiǎn)單–下面的Java代碼嘗試分配一個(gè)2M整數(shù)數(shù)組。當(dāng)您編譯它并使用12MB的Java堆空間(Java-Xmx12m-OOM)啟動(dòng)時(shí),它將失敗java.lang.OutOfMemoryError:Java堆空間消息。有了13MB的Java堆空間,程序運(yùn)行得很好。

class OOM {
 static final int SIZE=2*1024*1024;
 public static void main(String[] a) {
  int[] i = new int[SIZE];
  }
}

第二個(gè)也是更現(xiàn)實(shí)的例子是內(nèi)存泄漏在Java中,當(dāng)開發(fā)人員創(chuàng)建和使用新的對(duì)象(如new Integer(5))時(shí),他們不必自己分配內(nèi)存—這是由Java虛擬機(jī)(JVM)負(fù)責(zé)的。在應(yīng)用程序的生命周期中,JVM會(huì)定期檢查內(nèi)存中哪些對(duì)象仍在使用,哪些對(duì)象沒有使用。未使用的對(duì)象可以丟棄,內(nèi)存可以回收并再次使用。這個(gè)過(guò)程稱為垃圾回收。JVM中負(fù)責(zé)收集的相應(yīng)模塊稱為垃圾收集器(GC)。

Java的自動(dòng)內(nèi)存管理依賴于GC定期查找未使用的對(duì)象并將其刪除。簡(jiǎn)單地說(shuō),java 內(nèi)存泄露是指應(yīng)用程序不再使用某些對(duì)象,但垃圾回收無(wú)法識(shí)別它的情況。因此,這些未使用的對(duì)象將無(wú)限期地保留在Java堆空間中。這起連環(huán)碰撞最終會(huì)觸發(fā)java.lang.OutOfMemoryError:Java堆空間錯(cuò)誤。

構(gòu)建一個(gè)滿足內(nèi)存泄漏定義的Java程序相當(dāng)容易:

class KeylessEntry {
 
  static class Key {
   Integer id;
 
   Key(Integer id) {
     this.id = id;
   }
 
   @Override
   public int hashCode() {
     return id.hashCode();
   }
  }
 
  public static void main(String[] args) {
   Map m = new HashMap();
   while (true)
     for (int i = 0; i < 10000; i++)
      if (!m.containsKey(new Key(i)))
        m.put(new Key(i), "Number:" + i);
  }
}

當(dāng)您執(zhí)行上面的代碼時(shí),您可能希望它永遠(yuǎn)運(yùn)行而不會(huì)出現(xiàn)任何問(wèn)題,假設(shè)天真的緩存解決方案只將底層映射擴(kuò)展到10000個(gè)元素,除此之外,所有的鍵都已經(jīng)存在于HashMap中。但是,實(shí)際上,由于Key類在hashCode()旁邊沒有適當(dāng)?shù)膃quals()實(shí)現(xiàn),所以元素將繼續(xù)被添加。

結(jié)果,隨著時(shí)間的推移,隨著泄漏代碼的不斷使用,“緩存”結(jié)果最終會(huì)消耗大量Java堆空間。當(dāng)泄漏的內(nèi)存填滿堆區(qū)域中的所有可用內(nèi)存,而垃圾回收無(wú)法清理它時(shí)java.lang.OutOfMemoryError:引發(fā)Java堆空間。

解決方案很簡(jiǎn)單–添加與下面類似的equals()方法的實(shí)現(xiàn),您就可以開始了。但在你找到病因之前,你肯定會(huì)失去一些珍貴的腦細(xì)胞。

@Override
public boolean equals(Object o) {
  boolean response = false;
  if (o instanceof Key) {
   response = (((Key)o).id).equals(this.id);
  }
  return response;
}

內(nèi)存溢出怎么解決?

在某些情況下,分配給JVM的堆的數(shù)量不足以滿足在JVM上運(yùn)行的應(yīng)用程序的需要。在這種情況下,您應(yīng)該只分配更多的堆—請(qǐng)參閱本章末尾的部分了解如何實(shí)現(xiàn)這一點(diǎn)。

然而,在許多情況下,提供更多的Java堆空間并不能解決問(wèn)題。例如,如果應(yīng)用程序包含內(nèi)存泄漏,則添加更多堆只會(huì)推遲java.lang.OutOfMemoryError:Java堆空間錯(cuò)誤。此外,增加Java堆空間量也會(huì)增加GC暫停的長(zhǎng)度,從而影響應(yīng)用程序的吞吐量或延遲。

如果您希望解決Java堆空間的底層問(wèn)題,而不是掩蓋癥狀,那么您需要找出代碼的哪一部分負(fù)責(zé)分配最多的內(nèi)存。換句話說(shuō),你需要回答以下問(wèn)題:

  1. 哪些對(duì)象占據(jù)堆的大部分

  2. 在源代碼中分配這些對(duì)象

在這一點(diǎn)上,一定要在你的日歷中清除幾天(或者-在項(xiàng)目符號(hào)列表下面自動(dòng)查看)。下面是一個(gè)粗略的流程大綱,可以幫助您回答上述問(wèn)題:

  • 獲得安全許可,以便從JVM執(zhí)行堆轉(zhuǎn)儲(chǔ)。“dump轉(zhuǎn)儲(chǔ)”基本上是堆內(nèi)容的快照,您可以對(duì)其進(jìn)行分析。因此,這些快照可能包含機(jī)密信息,如密碼、信用卡號(hào)碼等,因此出于安全原因,獲取此類轉(zhuǎn)儲(chǔ)甚至可能不可能。

  • 在適當(dāng)?shù)臅r(shí)候把垃圾處理掉。準(zhǔn)備好獲取一些轉(zhuǎn)儲(chǔ),因?yàn)楫?dāng)在錯(cuò)誤的時(shí)間執(zhí)行時(shí),堆轉(zhuǎn)儲(chǔ)包含大量的噪聲,實(shí)際上可能是無(wú)用的。另一方面,每個(gè)堆轉(zhuǎn)儲(chǔ)都會(huì)完全“freezes凍結(jié)”JVM,所以不要占用太多,否則最終用戶將面臨性能問(wèn)題。

  • 找一臺(tái)能裝垃圾的機(jī)器。當(dāng)您的JVM使用例如8GB的堆時(shí),您需要一臺(tái)大于8GB的機(jī)器來(lái)分析堆內(nèi)容。啟動(dòng)轉(zhuǎn)儲(chǔ)分析軟件(我們推薦Eclipse MAT,但也有同樣好的替代品)。

  • 檢測(cè)堆的最大使用者的GC根路徑。我們?cè)谶@里的另一篇文章中討論了這一活動(dòng)。這對(duì)初學(xué)者來(lái)說(shuō)尤其困難,但實(shí)踐將使你了解結(jié)構(gòu)和導(dǎo)航機(jī)制。

  • 接下來(lái),您需要弄清楚源代碼中潛在危險(xiǎn)的大量對(duì)象被分配到哪里。如果您對(duì)應(yīng)用程序的源代碼有很好的了解,那么您將能夠在幾次搜索中做到這一點(diǎn)。

或者,我們建議使用plumber,這是唯一一個(gè)具有自動(dòng)根本原因檢測(cè)功能的Java監(jiān)控解決方案。在其他性能問(wèn)題中,它包羅萬(wàn)象java.lang.OutOfMemoryErrors并自動(dòng)為您提供有關(guān)最需要內(nèi)存的數(shù)據(jù)結(jié)構(gòu)的信息。

Plumber負(fù)責(zé)在后臺(tái)收集必要的數(shù)據(jù)——這包括關(guān)于堆使用情況的相關(guān)數(shù)據(jù)(只有對(duì)象布局圖,沒有實(shí)際數(shù)據(jù)),還有一些甚至在堆轉(zhuǎn)儲(chǔ)中都找不到的數(shù)據(jù)。它還為您執(zhí)行必要的數(shù)據(jù)處理—在運(yùn)行中,只要JVM遇到j(luò)ava.lang.OutOfMemoryError. 這里有一個(gè)例子java.lang.OutOfMemoryError管道工事故警報(bào):

Java項(xiàng)目中哪些情況會(huì)出現(xiàn)內(nèi)存泄漏

無(wú)需任何其他工具或分析,您可以看到:

  • 哪些對(duì)象消耗的內(nèi)存最多

  • 在哪里分配這些對(duì)象(它們中的大多數(shù)在MetricManagerImpl類中分配,第304行)

  • 當(dāng)前引用這些對(duì)象的是什么(到GC根的完整引用鏈)

有了這些信息,您就可以放大潛在的根本原因,并確保將數(shù)據(jù)結(jié)構(gòu)縮減到適合您的內(nèi)存池的級(jí)別。

然而,當(dāng)您從內(nèi)存分析或閱讀plumber報(bào)告得出的結(jié)論是內(nèi)存使用是合法的,并且源代碼中沒有什么可更改的,那么您需要允許JVM有更多的Java堆空間來(lái)正常運(yùn)行。在這種情況下,更改JVM啟動(dòng)配置并添加(或增加值,如果存在):

-Xmx1024m

上述配置將為應(yīng)用程序提供1024MB的Java堆空間??梢允褂胓或g表示GB,m或m表示MB,k或k表示KB。例如,以下所有內(nèi)容都相當(dāng)于最大Java堆空間為1GB:

  java -Xmx1073741824 com.mycompany.MyClass
  java -Xmx1048576k com.mycompany.MyClass
  java -Xmx1024m com.mycompany.MyClass
  java -Xmx1g com.mycompany.MyClass

上述就是小編為大家分享的Java項(xiàng)目中哪些情況會(huì)出現(xiàn)內(nèi)存泄漏了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(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