1. 程式人生 > >Java記憶體各部分OOM出現原因及解決辦法

Java記憶體各部分OOM出現原因及解決辦法

原文:https://blog.csdn.net/ls5718/article/details/52411211?utm_source=copy 

一,jvm記憶體區域

1,  程式計數器

一塊很小的記憶體空間,作用是當前執行緒所執行的位元組碼的行號指示器。

2, java棧

與程式計數器一樣,java棧(虛擬機器棧)也是執行緒私有的,其生命週期與執行緒相同。通常存放基本資料型別,物件引用(一個指向物件起始地址的引用指標或一個代表物件的控制代碼),reeturnAddress型別(指向一條位元組碼指令的地址)

棧區域有兩種異常型別:如果執行緒請求的棧深度大於虛擬機器所允許的深度,將拋StrackOverflowError異常;如果虛擬機器棧可以動態擴充套件(大部分虛擬機器都可動態擴充套件),當擴充套件時無法申請到足夠的記憶體時會丟擲OutOfMemoryError異常。

3,  本地方法棧

與虛擬機器棧作用很相似,區別是虛擬機器棧為虛擬機器執行java方法服務,而本地方法棧則是為虛擬機器用到的Native方法服務。和虛擬機器棧一樣可能丟擲StackOverflowError和OutOfMemoryError異常。

4,  java堆

java Heap是jvm所管理的記憶體中最大的區域。JavaHeap是被所有執行緒共享的一塊記憶體區域,在虛擬機器啟動時建立。主要存放物件例項。JavaHeap是垃圾收集器管理的主要區域,其可細分為新生代和老年代。如果在堆中沒有記憶體完成例項分配,並且也無法再擴充套件時,會丟擲OutOfMemoryError異常。

5, 方法區

與javaHeap一樣是各個執行緒共享的記憶體區域,用於存放已被虛擬機器載入的類資訊、常量、靜態變數、及時編譯器編譯後的程式碼等資料。當方法區無法滿足記憶體分配的需求時,將丟擲OutOfMemoryError異常。方法同時包含常聽說的執行時常量池,用於存放編譯期生成的各種字面量和符號引用。

6,直接記憶體

直接記憶體並不是虛擬機器執行時資料區的一部分,也不是java虛擬機器規範中定義的記憶體區域,是jvm外部的記憶體區域,這部分割槽域也可能導致OutOfMemoryError異常。

二,jvm引數
-Xss(StackSpace)棧空間

-Xms ,-Xmx(heap memory space)堆空間:Heap是大家最為熟悉的區域,他是jvm用來儲存物件例項的區域,Heap在32位的系統中最大為2G,其大小通過-Xms和-Xmx來控制,-Xms為jvm啟動時申請的最小Heap記憶體,預設為實體記憶體的1/64,但小於1G,-Xmx為jvm可申請的最大的Heap記憶體,預設為實體記憶體的1/4,一般也小於1G,預設當空餘堆記憶體小於40%時,jvm會最大Heap的大小到-Xmx指定大小,可通過-XX:MinHeapFreeRatio來指定這個比例,當空餘堆記憶體大於70%時,JVM會將Heap的大小往-Xms指定的大小調整,可通過-XX:MaxHeapFreeRatio來指定這個比例,但通常為了避免頻繁調整HeapSize的大小,將-Xms和-Xmx的值設為相同。

-XX:PermSize  -XX:MaxPermSize :方法區持久代大小: 方法區域也是全域性共享的,在一定的條件下它也會被   GC ,當方法區域需要使用的記憶體超過其允許的大小時,會丟擲   OutOfMemory 的錯誤資訊。

三,常見記憶體溢位錯誤解決辦法
除了程式計數器外,虛擬機器記憶體的其他幾個執行時區域都有發生OutOfMemoryError(OOM)異常的可能,

1,Java Heap 溢位

一般的異常資訊:java.lang.OutOfMemoryError:Java heap spacess

java堆用於儲存物件例項,我們只要不斷的建立物件,並且保證GC Roots到物件之間有可達路徑來避免垃圾回收機制清除這些物件,就會在物件數量達到最大堆容量限制後產生記憶體溢位異常。

出現這種異常,一般手段是先通過記憶體映像分析工具(如Eclipse Memory Analyzer)對dump出來的堆轉存快照進行分析,重點是確認記憶體中的物件是否是必要的,先分清是因為記憶體洩漏(Memory Leak)還是記憶體溢位(Memory Overflow)。

如果是記憶體洩漏,可進一步通過工具(如Jrockit等工具)檢視洩漏物件到GC Roots的引用鏈。於是就能找到洩漏物件時通過怎樣的路徑與GC Roots相關聯並導致垃圾收集器無法自動回收。

如果不存在洩漏,那就應該檢查虛擬機器的引數(-Xmx與-Xms)的設定是否適當。

2,虛擬機器棧和本地方法棧溢位

如果執行緒請求的棧深度大於虛擬機器所允許的最大深度,將丟擲StackOverflowError異常。

如果虛擬機器在擴充套件棧時無法申請到足夠的記憶體空間,則丟擲OutOfMemoryError異常

棧用來儲存執行緒的區域性變量表、運算元棧、動態連結、方法出口等資訊。如果請求棧的深度不足時丟擲的錯誤會包含類似下面的資訊:
java.lang.StackOverflowError

另外,由於每個執行緒佔的記憶體大概為1M,因此執行緒的建立也需要記憶體空間。作業系統可用記憶體-Xmx-MaxPermSize即是棧可用的記憶體,如果申請建立的執行緒比較多超過剩餘記憶體的時候,也會丟擲如下類似錯誤:

java.lang.OutofMemoryError: unable to create new native thread

相關的JVM引數有:
-Xss: 每個執行緒的堆疊大小,JDK5.0以後每個執行緒堆疊大小為1M,以前每個執行緒堆疊大小為256K.
在相同實體記憶體下,減小這個值能生成更多的執行緒.但是作業系統對一個程序內的執行緒數還是有限制的,不能無限生成,經驗值在3000~5000左右。

這裡需要注意當棧的大小越大可分配的執行緒數就越少。

3,執行時常量池溢位

異常資訊:java.lang.OutOfMemoryError:PermGen space

如果要向執行時常量池中新增內容,最簡單的做法就是使用String.intern()這個Native方法。該方法的作用是:如果池中已經包含一個等於此String的字串,則返回代表池中這個字串的String物件;否則,將此String物件包含的字串新增到常量池中,並且返回此String物件的引用。由於常量池分配在方法區內,我們可以通過-XX:PermSize和-XX:MaxPermSize限制方法區的大小,從而間接限制其中常量池的容量。

4,方法區溢位

方法區用於存放Class的相關資訊,如類名、訪問修飾符、常量池、欄位描述、方法描述等。

異常資訊:java.lang.OutOfMemoryError:PermGen space

方法區溢位也是一種常見的記憶體溢位異常,一個類如果要被垃圾收集器回收,判定條件是很苛刻的。在經常動態生成大量Class的應用中,要特別注意這點。

java.lang.OutOfMemoryError:PermGen space解決方法參考:

https://www.cnblogs.com/mingforyou/archive/2012/03/03/2378143.html

解決方法: 設定MaxPermSize大小 
可以在myelipse裡選中相應的伺服器比如tomcat5,展開裡面的JDK子項頁面,來增加伺服器啟動的JVM引數設定:
-Xms128m 
-Xmx256m 
-XX:PermSize=128M 
-XX:MaxNewSize=256m 
-XX:MaxPermSize=256m
或者手動設定MaxPermSize大小,比如tomcat,
修改TOMCAT_HOME/bin/catalina.bat,在echo "Using CATALINA_BASE: $CATALINA_BASE"上面加入以下行: 
JAVA_OPTS="-server -XX:PermSize=64M -XX:MaxPermSize=128m

建議:將相同的第三方jar檔案移置到tomcat/shared/lib目錄下,這樣可以減少jar 文件重複佔用記憶體

---------------------