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