面試蘇寧時,問的,內存溢出怎麼處理?
處理思路:先救火,再防火。
首先dump JVM的內存信息,這個信息用於後續的問題分析。如果重啓了服務,JVM也會重啓,這個信息就會丟失,所以務必先備份JVM的內存信息!!!
總的來說,是通過jmap命令來備份JVM的內存信息,同時,需要我們瞭解進程的pid。
1、獲取進程的pid:
ps -e | grep "NAME"
其中,NAME指的是進程的名字。執行命令得到的信息中,包含pid,可以根據進程的信息找到對應的pid。
當然,如果覺得查詢出現的內容太多,也可以指定顯示哪一列。例如,我的進程名字是“dialogue”,上面查詢顯示的第二列信息是pid,所以我的命令可以如下輸入:
ps -e | grep "dialogue" | awk '{print $2}'
2、備份內存信息:
jmap -dump:format=b,file=[fileName] [pid]
jmap命令可以用來獲取JVM堆內存的信息,jmap -dump用來dump堆內存的信息。fileName指的是備份的堆內存信息保存的文件名,pid表示需要備份的進行的pid。例如,我的進程pid是73452,想要將堆內存信息保存在當前目錄的jvm_heap.dump文件中,可以執行以下命令:
jmap -dump:format=b,file=jvm_heap.dump 73452
另外,如果想要直接查看進程當前的堆內存信息,也可以使用jmap命令:
1.JVM裏邊有什麼?(運行時的數據區)
首先我們只有知道JVM運行時的數據區,我們才能去一一診斷哪些地方可能會帶來內存溢出以及內存泄漏的問題。
- 程序計數器
- 虛擬機棧
- 本地方法棧
- 堆
- 方法區
【!!!下面的公式很重要!!!】
JVM內存【線程數*(最大棧容量)+最大堆值+其他內存(忽略不計或者一般不改動)】= 機器最大內存 - 直接內存
1.1 程序計數器(唯一一個沒有內存溢出問題)
由於Java的多線程是通過線程輪流切換完成的,一個線程沒有執行完時就需要一個東西記錄它執行到哪了,下次搶佔到了CPU資源時再從這開始,這個東西就是程序計數器,正是因爲這樣,所以它也是“線程私有”的內存。
1.2 Java虛擬機棧(可能會有內存溢出,機率很小)
虛擬機棧是線程私有的,是Java的方法執行的內存模型,執行時會創建棧幀,裏邊放了一大堆東西,有局部變量表,操作數棧,動態鏈接,方法出口等信息。
1.3 本地方法棧
虛擬機中使用了Native方法。
棧溢出(StackOverflowError)棧容量由-Xss指定
原因:方法運行時,棧的深度超過了虛擬機容許的最大深度所致
這裏要思考兩個問題:當棧空間無法繼續分配時,到底是棧內存太小,還是已經使用的棧空間太大?
解決思路:
一)、是否有遞歸調用
二)、是否有大量循環或死循環
三)、全局變量是否過多
四)、 數組、List、map數據是否過大
1.4 堆(線程共享)通過-Xmx和-Xms控制
JVM裏最大的一塊內存區域,存放對象和數組。堆 = 新生代+老生代
1.4.1 堆內存溢出 java.lang.OutOfMemoryError:Java heap space
解決辦法:
一)、檢查代碼,代碼沒有問題情況下,適當調整-Xms和-Xmx兩個jvm參數,然後壓測調整成最優值;
二)、避免大的對象內存申請,像文件上傳、大批量從數據庫獲取,儘量分塊分批操作;
三)、儘量提高一次請求的執行速度,垃圾回收越早越好;
1.4.2 堆內存泄漏 java . lang.OutOfMemoryError
描述:對象不再被使用,但是沒有被垃圾收集識別,造成大量堆積,觸發異常。
內存泄漏可以通過完善代碼來避免。
某個業務系統在一段時間突然變慢,我們懷疑是因爲出現內存泄露問題導致的,於是踏上排查之路。
採用下面的步驟分析:
1. 用工具生成java應用程序的heap dump(如jmap)
2. 使用Java heap分析工具(如MAT),找出內存佔用超出預期的嫌疑對象
3. 根據情況,分析嫌疑對象和其他對象的引用關係。
4. 分析程序的源代碼,找出嫌疑對象數量過多的原因。
1.5 方法區
方法區用於存放Class的相關信息,如類名、訪問修飾符、常量池、字段描述、方法描述等。
1.5.1 方法區和運行時常量池溢出
1.6及之前的版本,會報PermGen Space
1.7及之後,會報Java heap space
1.5.2 元空間內存溢出
系統的代碼非常多或引用的第三方包非常多或者通過動態代碼生成類加載等方法,導致元空間的內存佔用很大。
1)優化參數配置,避免影響其他JVM進程
2)慎重引用第三方包
3)關注動態生成類的框架
2.其他內存溢出
2.1 直接內存溢出
JVM虛擬機是運行在操作系統上的進程,操作系統分配給JVM的內存在啓動是有限的,不可能把全部內存都分配給JVM,Java NIO又用到了直接內存技術,利用Channel和Buffer直接操作JVM外的內存,避免數據在JVM和操作系統內存之間來回複製。但是,當JVM和直接內存的和大於操作系統總內存時,就會發生內存溢出。
解決方法:可以考慮設置參數:-XX:MaxDirectMemorySize,並及時clear內存。
2.2 創建本地線程內存溢出
線程基本只佔用heap以外的內存區域,也就是這個錯誤說明除了heap以外的區域,無法爲線程分配一塊內存區域了,這個要麼是內存本身就不夠,要麼heap的空間設置得太大了,導致了剩餘的內存已經不多了,而由於線程本身要佔用內存,所以就不夠用了。
解決方法:首先檢查操作系統是否有線程數的限制,使用shell也無法創建線程,如果是這個問題就需要調整系統的最大可支持的文件數。
日常開發中儘量保證線程最大數的可控制的,不要隨意使用線程池。不能無限制的增長下去。