JVM内存溢出的几种情形
1.堆溢出
原因:大量對象占據(jù)了堆空間,而這些對象都有強引用導(dǎo)致無法回收,當對象大小之和>Xmx參數(shù)指定的堆大小時導(dǎo)致溢出!
List<byte[]> list = new ArrayList<>();for (int i = 0; i < 10240; i++) {list.add(new byte[1024 * 1024]);}
解決方法是使用-Xmx增大堆大小,但是堆空間畢竟不能無限增長,所以需要使用MAT和VM等工具找到大量占用堆空間的對象做出合理的程序優(yōu)化。
直接內(nèi)存溢出
NIO直接向操作系統(tǒng)申請,但是由于沒有內(nèi)JVM托管,如果使用不當也會導(dǎo)致內(nèi)存溢出。
過多線程導(dǎo)致OOM
因為每個線程需要占用系統(tǒng)內(nèi)存,當線程過多可能導(dǎo)致OOM,線程的棧空間是在堆外分配,和直接內(nèi)存很相似,如果要系統(tǒng)支持更多的線程,那么應(yīng)該使用小的堆空間。
解決辦法:
1).減少堆空間 -Xmx512m 這樣操作系統(tǒng)可以預(yù)留更多內(nèi)存用于線程創(chuàng)建 因此程序可以正常運行
2).減少線程所占的內(nèi)存空間,使用-Xss可以指定棧空間 ?-Xmx1g -Xss128k
使用1G堆空間,但是棧空間減少到128K,剩余可以用的內(nèi)存可以容納更多的線程,但是減少棧空間,棧溢出的風(fēng)險增加。
3).減少線程總數(shù)
2.棧溢出
java虛擬機規(guī)范定義了兩種異常與棧空間有關(guān):StackOverflowError和OutOfMemoryError
線程計算過程中 棧深度>最大可用棧深度 拋出StackOverflowError
如果棧可以動態(tài)擴展,如果擴展過程中沒有足夠內(nèi)存空間支持會拋出OutOfMemoryError
-Xss設(shè)置棧大小,棧大小決定了函數(shù)調(diào)用的可達深度
虛擬機棧在運行時使用了棧幀的數(shù)據(jù)結(jié)構(gòu)保持上下文數(shù)據(jù),棧幀中存放了局部變量表,操作數(shù)棧,動態(tài)連接方法和返回地址等信息。
每一個方法的調(diào)用都伴隨著棧幀的入棧操作,函數(shù)返回表示出棧。如果參數(shù)和局部變量過多那么棧幀的局部變量表會變大。
無限遞歸導(dǎo)致
public class TestDemo {private int count = 0;public void recursion() {count++;recursion();}@Testpublic void testStack() {try {recursion();} catch (Throwable e) {System.out.println("深度>>>" + count);e.printStackTrace();}}
}
jclasslib可以查看class文件每個方法分配的局部變量表內(nèi)容,
下載地址:https://github.com/ingokegel/jclasslib/releases
在棧幀中與性能調(diào)優(yōu)關(guān)系最密切的就是局部變量表:函數(shù)的參數(shù)+函數(shù)內(nèi)局部變量,局部變量表以字為單位,一個字32位長,long和double占2字,其余1字。
對于非static方法虛擬機還將對象this作為參數(shù)通過局部變量表傳遞給當前方法。
3.永久區(qū)溢出
如果系統(tǒng)定義了太多的類型,那么永久區(qū)會溢出,Jdk1.8中永久區(qū)被稱為元數(shù)據(jù)的區(qū)域代替,但是功能是類似的,都是保持類的元信息。
例如使用Cglib動態(tài)產(chǎn)生新類(而不是new對象)N次會都愛吃O(shè)OM異常。
解決辦法:
1.增加MaxPermSize
2.減少系統(tǒng)需要的類的數(shù)量
3.使用ClassLoader合理的裝載類定期回收
參考《Java程序性能優(yōu)化 讓你的Java程序更快、更穩(wěn)定》
總結(jié)
以上是生活随笔為你收集整理的JVM内存溢出的几种情形的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 事务隔离机制原理分析以及是否可以防止订单
- 下一篇: String和常量池