一、OutOfMemoryError(內存溢出)
JVM 管理的內存大致包括三種區域:Heap space(堆區域)
、Java Stacks(Java 棧)
、Permanent Generation space(永久保存區域)
。由此,OOM 簡單的分為堆溢出、棧溢出、永久代溢出(常量池/方法區)。Java 程序的每個線程中都有一個獨立的堆棧。容易發生內存溢出問題的內存空間包括:Heap space 和 Permanent Generation space。
1??堆區域用來存放 Class 的實例(即對象),對象需要存儲的內容主要是非靜態屬性。每次用 new 創建一個對象實例后,對象實例存儲在堆區域中,這部分空間也被 JVM 的垃圾回收機制管理。
【Java 堆內存溢出】java.lang.OutOfMemoryError: Java heap space
此種情況最常見,一般由于內存泄露或者堆的大小設置不當引起。原因是 JVM 創建的對象太多,在進行垃圾回收之間,虛擬機分配的到堆內存空間已經用滿了,與 Heap space 有關。解決這類問題有兩種思路:
- 對于內存泄露,可以通過內存監控軟件查找程序中的泄露代碼。檢查程序,看是否有死循環或不必要地重復創建大量對象。找到原因后,修改程序和算法。
- 增加 JVM 中 Xms(初始堆大小)和 Xmx(最大堆大小)參數的大小。如:
set JAVA_OPTS= -Xms256m -Xmx1024m
2??Java 棧跟大多數編程語言包括匯編語言的棧功能相似,主要基本類型變量以及方法的輸入輸出參數。
【Java 棧內存溢出】java.lang.StackOverflowError
不會拋 OOM error,但也是比較常見的 Java 內存溢出。Java 棧溢出,一般是由于程序中存在死循環或者深度遞歸調用造成的,棧大小設置太小也會出現此種溢出。可以通過虛擬機參數-Xss來設置棧的大小。
3??永久保存區域主要存放 Class 和 Meta 的信息,Class 第一次被 Load 的時候被放入 PermGen space 區域,Class 需要存儲的內容主要包括方法和靜態屬性。
【Java 永久代溢出】java.lang.OutOfMemoryError: PermGen space
即方法區溢出了,一般出現于大量的 jar 或 Class 或者渲染視圖(jsp、vm、ftl)頁面,或者采用 cglib 等反射機制的情況,因為上述情況會產生大量的 Class 信息存儲于方法區,使得 JVM 裝載類的空間不夠。解決這類問題有以下兩種辦法:
- 增加 JVM 中的初始永久保存區域大小
XX:PermSize
和最大永久保存區域大小XX:MaxPermSize
參數的大小。如針對 tomcat6.0,在 catalina.sh 或 catalina.bat 文件中一系列環境變量名說明結束處(大約在70行左右)增加一行:
JAVA_OPTS=" -XX:PermSize=64M -XX:MaxPermSize=128m"
如果是 windows 服務器還可以在系統環境變量中設置。 - 清理應用程序中 web-inf/lib 下的 jar。如果 tomcat 部署了多個應用,很多應用都使用了相同的 jar,可以將共同的 jar 移到 tomcat 共同的 lib 下,減少類的重復加載。
二、常見場景
1??靜態集合類聲明為靜態 static 的 HashMap、Vector 等集合。通俗來講A中有B,當前只把B設置為空,A沒有設置為空,回收時B無法被回收,因為被A引用。
2??物理連接:DataSource.getConnection()
建立鏈接,必須通過close()關閉鏈接
3??內部類和外部模塊等的引用
GC 只會回收沒有被引用或者根集不可到達的對象(取決于 GC 算法),內部類在生命周期內始終持有外部類的對象的引用,造成外部類的對象始終不滿足 GC 的回收條件,反映在內存上就是內存泄露。常見解決方案:
- 將內部類定義為static
- 用static的變量引用匿名內部類的實例或將匿名內部類的實例化操作放到外部類的靜態方法中
4??【OutOfMemoryError:unable to create new native thread】Executors 返回的線程池對象的弊端如下:
- FixedThreadPool 和 SingleThreadPool:
允許的請求隊列長度為 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。 - CachedThreadPool 和 ScheduledThreadPool:
允許的創建線程數量為 Integer.MAX_VALUE,可能會創建大量的線程,從而導致 OOM。
三、分析定位
1??在未明確找到問題原因前,先添配置 JVM 啟動參數,監控復原 OOM 場景自動dump:-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${目錄}
2??
- 獲取內存的堆信息
jmap -dump:format=b,file=mem.dat pid
- 使用內存映像分析工具(如Eclipse Memory Analyzer)對 dump 出來的堆轉存快照進行分析,重點是確認內存中的對象是否是必要的,先分清是因為內存泄漏(Memory Leak)還是內存溢出(Memory Overflow)。
- 看分析圖,查找對應問題,分析內存占用情況 例如:localstore 存儲問題分析,大對象。