java 内存溢出 内存泄露_JVM——内存泄漏与内存溢出
1.內(nèi)存溢出
1.1 什么是Java的內(nèi)存溢出?
在Java程序運(yùn)行的過(guò)程中,經(jīng)常會(huì)碰到以下錯(cuò)誤:java.lang.OutOfMemoryError。
通俗講,內(nèi)存溢出是指程序在申請(qǐng)內(nèi)存時(shí),沒有足夠的內(nèi)存空間供其使用,出現(xiàn)OutOfMemoryError。
1.2 產(chǎn)生原因?
簡(jiǎn)單來(lái)講為以下兩點(diǎn):
1. JVM內(nèi)存過(guò)小
2. 產(chǎn)生過(guò)多的,沒有被回收的垃圾
以下討論主要基于JVM上不同內(nèi)存區(qū)域的討論
1.3 Java堆溢出
Java堆是存放對(duì)象實(shí)例的地方,當(dāng)我們不斷申請(qǐng)創(chuàng)建對(duì)象,并且保證這些對(duì)象始終可以從GC Roots可達(dá),總?cè)萘烤蜁?huì)觸及最大堆的容量限制而拋出內(nèi)存溢出異常
例如以下代碼,將虛擬機(jī)的初始大小設(shè)為 20M ,并且不可變(將堆的最小值 -Xms 和 堆的最大值 -Xmx 設(shè)置為一樣可以避免Java堆自動(dòng)擴(kuò)展!)
public class OOM {
/*
-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
*/
static class OOMObject{
}
public static void main(String[] args){
List list = new ArrayList<>();
while(true){
list.add(new OOMObject());
}
}
}
解決方法
常規(guī)處理方法是通過(guò)內(nèi)存映像分析工具對(duì) Dump 出來(lái)的堆轉(zhuǎn)儲(chǔ)快照進(jìn)行分析。
1.首先分析是內(nèi)存泄漏還是內(nèi)存溢出
2.如果是內(nèi)存泄漏通過(guò)工具查看泄露對(duì)象到 GC Roots 的引用鏈,分析垃圾收集器無(wú)法回收他們的原因,進(jìn)而定位到出現(xiàn)問(wèn)題的代碼
3.如果不是內(nèi)存泄漏,即對(duì)象都應(yīng)該必須活著,就應(yīng)該對(duì)比 JVM堆內(nèi)存 和機(jī)器內(nèi)存相比是否還有向上調(diào)整的空間;或者從代碼上檢查某些對(duì)象是否生命周期過(guò)長(zhǎng),持有狀態(tài)時(shí)間過(guò)長(zhǎng),存儲(chǔ)結(jié)構(gòu)設(shè)計(jì)不合理等,盡量減少程序運(yùn)行期間的內(nèi)存消耗
1.4 虛擬機(jī)棧和本地方法棧溢出
1. 如果線程請(qǐng)求棧的深度大于虛擬機(jī)所允許的最大深度,將拋出 StackOverflowError 異常
2. 如果虛擬機(jī)的棧內(nèi)存允許動(dòng)態(tài)的擴(kuò)展,當(dāng)擴(kuò)展棧的容量無(wú)法申請(qǐng)到足夠的內(nèi)存的時(shí)候,將拋出’OutOfMemoryError’異常
解決方法
1.出現(xiàn) StackOverflowError 異常時(shí),會(huì)有明確錯(cuò)誤堆棧可供分析,比較容易定位問(wèn)題所在,例如遞歸沒有終止條件。棧深度大多數(shù)情況下到達(dá)1000-2000是沒有問(wèn)題的,對(duì)于正常方法的調(diào)用,這個(gè)深度完全是夠用的。
2.但如果是因?yàn)榻⑦^(guò)多線程導(dǎo)致內(nèi)存溢出,在不能減少線程的數(shù)量的情況下,只能通過(guò)減少最大堆的容量或者減少棧的容量來(lái)獲取更多的線程!
1.5 方法區(qū)和運(yùn)行時(shí)常量池溢出
由于在JDK 8 以后,永久代退出了歷史舞臺(tái),元空間作為其替代者登場(chǎng),即元空間使用的是直接內(nèi)存,受限于本機(jī)物理內(nèi)存的大小,不再容易拋出方法區(qū)的內(nèi)存溢出。而在JDK 8 之前,方法區(qū)的實(shí)現(xiàn)永久代是會(huì)因?yàn)榧虞d了大量的類(比如CG Lib字節(jié)碼技術(shù))而拋出方法區(qū)內(nèi)存溢出的,或者因?yàn)檫\(yùn)行時(shí)常量池(JDK 6 還是屬于方法區(qū)的一部分,拋出的是方法區(qū)的內(nèi)存溢出;而JDK7之后便移到Java堆上,拋出的是java的堆內(nèi)存溢出)而拋出對(duì)應(yīng)區(qū)域的內(nèi)存溢出!
1.6 本機(jī)直接內(nèi)存溢出
直接內(nèi)存可以通過(guò) -XX:MaxDirectMemorySize 參數(shù)來(lái)指定,若不指定則默認(rèn)與堆的最大值保持一致。
由直接內(nèi)存導(dǎo)致的內(nèi)存溢出,一個(gè)明顯的特征就是在dump下的文件不會(huì)看到有明顯的異常情況,或者該文件很小,而程序又直接或者間接的使用了 Direct Memory ,就應(yīng)該去考慮是否是本機(jī)直接內(nèi)存溢出
2. 內(nèi)存泄漏
Memory Leak,是指程序在申請(qǐng)內(nèi)存后,無(wú)法釋放已申請(qǐng)的內(nèi)存空間,一次內(nèi)存泄露危害可以忽略,但內(nèi)存泄露堆積后果很嚴(yán)重,無(wú)論多少內(nèi)存,遲早會(huì)被占光。
在Java中,內(nèi)存泄漏就是存在一些被分配的對(duì)象,這些對(duì)象有下面兩個(gè)特點(diǎn):
首先,這些對(duì)象是可達(dá)的,即在有向圖中,存在通路可以與其相連;
其次,這些對(duì)象是無(wú)用的,即程序以后不會(huì)再使用這些對(duì)象。
如果對(duì)象滿足這兩個(gè)條件,這些對(duì)象就可以判定為Java中的內(nèi)存泄漏,這些對(duì)象不會(huì)被GC所回收,然而它卻占用內(nèi)存。
3.兩者聯(lián)系
內(nèi)存泄露會(huì)最終會(huì)導(dǎo)致內(nèi)存溢出。
相同點(diǎn):都會(huì)導(dǎo)致應(yīng)用程序運(yùn)行出現(xiàn)問(wèn)題,性能下降或掛起。
不同點(diǎn):
1. 內(nèi)存泄露是導(dǎo)致內(nèi)存溢出的原因之一,內(nèi)存泄露積累起來(lái)將導(dǎo)致內(nèi)存溢出。
1. 內(nèi)存泄露可以通過(guò)完善代碼來(lái)避免,內(nèi)存溢出可以通過(guò)調(diào)整配置來(lái)減少發(fā)生頻率,但無(wú)法徹底避免。
本作品采用《CC 協(xié)議》,轉(zhuǎn)載必須注明作者和本文鏈接
總結(jié)
以上是生活随笔為你收集整理的java 内存溢出 内存泄露_JVM——内存泄漏与内存溢出的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java for list i_Java
- 下一篇: java环境变量设置的作用_JDK环境变