1、概念:
內存溢出是指應用系統中存在無法回收的內存,或者某一時刻調用的內存超過虛擬機能提供的最大內存而導致的程序罷工現象
2、原因
根本原因就是內存不夠用,常見原因有如下幾種:
<1>內存中加載的數據量過於龐大,如一次從數據庫取出過多數據;
<2>集合類中有對對象的引用,使用完未能及時清空,導致JVM不能回收;
<3>代碼中存在死循環
<4>循環中產生過多重複的對象實體
<5>tomcat啓動配置內存參數設置較低
<6>使用第三方軟件中的bug
3、分類
<1>java堆和方法區
java堆主要存放對象實例和數組等;
方法區保存類信息、常量、靜態變量等。運行時常量池也是方法區的一部分
這兩塊區域是線程共享的區域,只會拋出OutOfMemory異常
<2>程序計數器
程序計數器是線程私有的,主要作用是通過改變計數器值來選取下一條需要執行的字節碼指令。線程之間都是互不影響的
<3>虛擬機棧和本地方法棧
虛擬機棧用於存儲局部變量表、操作數棧、動態連接,方法出口等信息,爲虛擬機執行java方法服務;
本地方法棧爲虛擬機提供native方法服務
4、溢出實例
<1>堆溢出
堆是用來存放實例對象的,我們只要無限創建實例對象,如果設置的堆內存比較低,堆區不斷積累就會堆滿
代碼示例如下:
public class OutOfMemory{
public class Test{}
public static void main(String[] args){
List<Test> testList = new ArrayList<Test>();
while(true){
testList.add(new Test());
}
}
}
<2>虛擬機棧和本地方法棧溢出
java虛擬機規範中描述了兩種異常:
①如果線程請求的棧深度大於虛擬機鎖允許的最大深度,將拋出StackOverflow異常
使用方法遞歸調用模擬:
代碼示例如下:
public class StackOverflow{
public static void main(String[] args){
test();
}
private static void go(){
System.out.println("StackOverflow異常");
test();
}
}
②如果虛擬機在擴展棧時無法申請到足夠的內存空間,則拋出OutOfMemory異常
使用遞歸調用模擬,類直接調用
代碼實例如下:
public class StackOverflowTwo{
private int stackLength = 1;
public void stackLeak(){
stackLength++;
stackLeak();
}
public static void main(String[] args){
StackOverflowTwo test = new StackOverflowTwo();
test.stackLeak();
}
}
③方法區和運行時常量池溢出
常量池:String.intern()是一個Native方法,他的作用是如果常量池中已經包含一個等於String對象的字符串,則返回代表池中這個字符串String對象,否則將此字符串放到常量池中,並返回對象的引用。在jdk1.6之前常量池放在永久代,因此可能會出現常量池溢出
方法區:方法區存放Class相關信息
④本機直接內存溢出:
5、解決思路
<1>修改jvm參數,直接增加內存
鏈接:https://blog.csdn.net/Knight_Key/article/details/80018981
<2>堆代碼進行排查
排查工具可以用jvm自帶的內存檢測軟件,還有Memory Analyzer Tool