1 前言
相信有一定java開發(fā)經(jīng)驗的人或多或少都會遇到OutOfMemoryError的問題,這個問題曾困擾了我很長時間,隨著解決各類問題經(jīng)驗的積累以及對問題根源的探索,終于有了一個比較深入的認識。
在解決java內(nèi)存溢出問題之前,需要對jvm(java虛擬機)的內(nèi)存管理有一定的認識。jvm管理的內(nèi)存大致包括三種不同類型的內(nèi)存區(qū)域:Permanent Generation space(永久保存區(qū)域)、Heap space(堆區(qū)域)、Java Stacks(Java棧)。其中永久保存區(qū)域主要存放Class(類)和Meta的信息,Class第一次被Load的時候被放入PermGen space區(qū)域,Class需要存儲的內(nèi)容主要包括方法和靜態(tài)屬性。堆區(qū)域用來存放Class的實例(即對象),對象需要存儲的內(nèi)容主要是非靜態(tài)屬性。每次用new創(chuàng)建一個對象實例后,對象實例存儲在堆區(qū)域中,這部分空間也被jvm的垃圾回收機制管理。而Java棧跟大多數(shù)編程語言包括匯編語言的棧功能相似,主要基本類型變量以及方法的輸入輸出參數(shù)。Java程序的每個線程中都有一個獨立的堆棧。容易發(fā)生內(nèi)存溢出問題的內(nèi)存空間包括:Permanent Generation space和Heap space。
2 第一種OutOfMemoryError: PermGen space
發(fā)生這種問題的原意是程序中使用了大量的jar或class,使java虛擬機裝載類的空間不夠,與Permanent Generation space有關(guān)。解決這類問題有以下兩種辦法:
- 增加java虛擬機中的XX:PermSize和XX:MaxPermSize參數(shù)的大小,其中XX:PermSize是初始永久保存區(qū)域大小,XX:MaxPermSize是最大永久保存區(qū)域大小。如針對tomcat6.0,在catalina.sh 或catalina.bat文件中一系列環(huán)境變量名說明結(jié)束處(大約在70行左右) 增加一行:
JAVA_OPTS=" -XX:PermSize=64M -XX:MaxPermSize=128m"
3 第二種OutOfMemoryError: Java heap space
發(fā)生這種問題的原因是java虛擬機創(chuàng)建的對象太多,在進行垃圾回收之間,虛擬機分配的到堆內(nèi)存空間已經(jīng)用滿了,與Heap space有關(guān)。解決這類問題有兩種思路:
檢查程序,看是否有死循環(huán)或不必要地重復創(chuàng)建大量對象。找到原因后,修改程序和算法。(如果找不到原因,參考5.如何利用工具檢查內(nèi)存泄露)
增加Java虛擬機中Xms(初始堆大?。┖蚗mx(最大堆大小)參數(shù)的大小。如:set JAVA_OPTS= -Xms256m -Xmx1024m
4 第三種OutOfMemoryError:unable to create new native thread
這種錯誤在Java線程個數(shù)很多的情況下容易發(fā)生,一般有兩種可能:
在程序里面使用了http-client,http-client 有兩個超時參數(shù),但實際只設置了一個參數(shù),然后當對方的服務器宕機之后,httpClient一直處于等待狀態(tài),導致線程堆積。
httpClient-3.0的超時時間設置代碼如下:
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(10000); //設置連接超時時間
httpClient.getHttpConnectionManager().getParams().setSoTimeout(10000);//當連接上之后,等待對方傳輸數(shù)據(jù)的超時時間(大部分是這個時間忘記設置)
httpClient-4.0的超時時間設置代碼如下:
HttpPost httpPost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(10000).build();//設置請求和傳輸超時時間
做了多線程,而每個線程的耗時過多導致的(新增線程的速度比處理完成線程的速度快)。這種情況只能通過優(yōu)化程序或者進行集群化部署來解決。
5 如何利用工具檢查JAVA內(nèi)存泄露
巧婦難為無米之炊,當發(fā)生內(nèi)存溢出的時候,我們需要把內(nèi)存堆棧保存下來,之后才能進行分析。具體步驟如下:
5.1 獲取java的內(nèi)存堆棧
5.1.1 windows系統(tǒng)獲取java內(nèi)存堆棧
打開jdk安裝目錄\bin下的 jvisualvm.exe,如下圖:




5.1.2 linux系統(tǒng)獲取java內(nèi)存堆棧
1、運行ps -ef|grep java (你的java進程ID )
2、運行: jmap -dump:format=b,file=/usr/local/Client/log20151014.dmp 1186 (其中1186換為你的java的進程ID)
3、之后將log20151014.dmp拷貝到個人電腦上,以便后面分析。
5.2 使用IBM的內(nèi)存泄露分析工具進行分析
5.2.1 IBM-HeapAnalyzer下載地址
http://www.alphaworks.ibm.com/tech/heapanalyzer

5.2.1 IBM-HeapAnalyzer初級使用教程
使用javaw打開下載到的ha456.jar,如下圖:

(如果出現(xiàn)內(nèi)存不足,無法打開dmp文件的情況,可以打開命令窗口,進入jar 的目錄,然后輸入
java -jar -Xmx3000m ha456.jar)



然后通過搜索查詢相關(guān)資料,排查到是mysql的connector/J官方驅(qū)動存在內(nèi)存泄露,通過更新mysql的jdbc驅(qū)動解決此問題。
原文鏈接:https://blog.csdn.net/xxxx3/java/article/details/81009524