1. 程式人生 > >Java記憶體溢位的原因有哪些?Java程序佔用記憶體構成有哪些?

Java記憶體溢位的原因有哪些?Java程序佔用記憶體構成有哪些?

JVM記憶體佔用=作業系統自身耗記憶體 + 堆 + Java永久代/元資料區/方法區/常量池/程式碼快取 + 程式計數器(可忽略不計)*執行緒數 + 虛擬機器程序本身 + 虛擬機器棧(執行緒棧)*執行緒數 + 本地方法棧(JNI呼叫)*執行緒數 + 直接記憶體(Java NIO)


metaspace的組成
Klass Metaspace
開啟壓縮指標,Klass Metaspace就是用來存klass的,klass是我們熟知的class檔案在jvm裡的執行時資料結構-XX:CompressedClassSpaceSize=1g
NoKlass Metaspace
沒有開啟壓縮指標,就不會有-XX:CompressedClassSpaceSize=1g這塊記憶體,這種情況下klass都會存在NoKlass Metaspace裡
NoKlass Metaspace專門來存klass相關的其他的內容,比如method,constantPool等

GC回收的例項物件:
1、虛擬機器棧(棧幀中的本地變量表)中的引用的物件
2、方法區中的類靜態屬性引用的物件
3、方法區中的常量引用的物件
4、本地方法棧中的JNI(即一般說的Native方法)的引用的物件


GC回收的“無用的類”:
1、該類所有的例項都已經被回收,也就是Java堆中不存在該類的任何例項
2、載入該類的 ClassLoader已經被回收
3、該類對應的java.lang.Class物件沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法


記憶體溢位原因種類
1、堆溢位 jang.lang.OutOfMemoryError : Java heap space
原因:
1、堆的新生區或年老區沒有足夠的空間存放即將分配的記憶體
2、(不可回收記憶體+即將分配的記憶體)超過堆可用記憶體
改進辦法:
1、檢查堆記憶體洩露
2、合理設定堆的新生區和年老區比例
3、增大-Xmx


2、直接記憶體溢位(NIO、Unsafe包)java.lang.OutOfMemoryError
原因:
1、JDK中有一種基於通道(Channel)和緩衝區 (Buffer)的記憶體分配方式,將由C語言實現的native函式庫分配在直接記憶體中,用儲存在JVM堆中的DirectByteBuffer來引用。 由於直接記憶體受到本機器記憶體的限制,所以也可能出現OutOfMemoryError的異常。
2、-XX:MaxDirectMemorySize設定的很小,導致計算機記憶體不夠用來繼續分配直接記憶體
3、-Xmx設定的太小,且未設定-XX:MaxDirectMemorySize,導致計算機記憶體不夠用來繼續分配直接記憶體(-XX:MaxDirectMemorySize預設值跟-Xmx一樣)
4、-Xms設定的太大,導致計算機沒有足夠的剩餘記憶體給直接記憶體(也就是OS剩餘記憶體,不足以分配夠這個數值XX:MaxDirectMemorySize)
5、 直接記憶體不夠申請新的記憶體空間時,會執行System.gc()觸發Full GC,Full GC會將未被引用的物件及其指向的直接記憶體釋放掉
有些NIO框架或者RMI,會手工呼叫System.gc()來觸發Full GC
System.gc()的呼叫,需要注意正確設定引數:-XX:-DisableExplicitGC
    6、-XX:+DisableExplicitGC、-XX:+ExplicitGCInvokesConcurrent 、-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses 觸發CMS、G1回收,CMS和G1都會回收新生代和老年代,如果CMS或者G1回收失敗(堆內失敗),會呼叫Full GC
    nio DirectByteBuffer物件在建立的時候關聯了一個 PhantomReference,gc過程中如果發現某個物件除了只有PhantomReference引用它之外,並沒有其他的地方引用它了,
    那將會把這個引用放到 java.lang.ref.Reference.pending佇列裡,在gc完畢的時候通知ReferenceHandler這個守護執行緒去執行一些後置處理,而DirectByteBuffer關聯的PhantomReference是PhantomReference的一個子類,在最終的處理裡會通過Unsafe的free介面來釋放DirectByteBuffer對應的堆外記憶體塊
詳情:http://www.open-open.com/lib/view/open1431570358826.html
改進辦法:
1、檢查NIO、直接記憶體洩露,DirectByteBuffer類、Unsafe類. (真正分配記憶體的方法是unsafe.allocateMemory())
2、設定合理的值-XX:MaxDirectMemorySize(預設值跟-Xmx一樣)
3、增大-Xmx,未設定-XX:MaxDirectMemorySize的情況下(-XX:MaxDirectMemorySize預設值跟-Xmx一樣)
4、減小-Xmx,已手工設定大-XX:MaxDirectMemorySize的情況下(預留出更多的直接記憶體空間)
5、設定-XX:-DisableExplicitGC,設定可達的實體記憶體的值-XX:MaxDirectMemorySize
6、設定-XX:-DisableExplicitGC、-XX:-ExplicitGCInvokesConcurrent 、-XX:-ExplicitGCInvokesConcurrentAndUnloadsClasses


3、永久區溢位
原因:
1、永久區記憶體分配不足
jang.lang.OutOfMemoryError: PermGen space(jdk1.7)
jang.lang.OutOfMemoryError: Meta space(jdk1.8)
2、jvm配置引數有-Xnoclassgc
改進辦法:
1、減少系統需要的類的數量
2、增大-XX:PermSize -XX:MaxPermSize(jdk1.7)、增大-XX:MetaspaceSize -XX:MaxMetaspaceSize(jdk1.8)
3、使用ClassLoader合理地裝載各個類,並定期進行回收
4、去掉jvm配置引數-Xnoclassgc

4、虛擬機器棧和本地方法棧溢位(過多執行緒)
原因:
1、棧深度太大,方法呼叫棧層級太深,一般是死迴圈或者遞迴函式的層級太深引起
2、單個棧容量太小,棧幀裡放的大量的區域性變數,超過單個執行緒的-Xss值
(每個執行緒分配到的棧容量越大,可用建立的執行緒數量自然就越少)
能建立的執行緒數:
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
MaxProcessMemory 指的是一個程序的最大記憶體
JVMMemory         JVM記憶體
ReservedOsMemory  保留的作業系統記憶體
ThreadStackSize      執行緒棧的大小
改進辦法:
1、減少執行緒棧空間-Xss 
2、減少執行緒總數
3、減少最大堆空間-Xmx

5、執行時常量池溢位
原因:
1、使用String.intern()這個Native方法,常量池分配在方法區PermGen space(jdk1.7)/Meta space(jdk1.8)
2、jar包和class檔案裡定義定義的常量太多/太大
3、FULL GC之後,無法騰出足夠的記憶體給即將要分配的常量池區物件
改進辦法:
1、檢查不合理的String.intern()呼叫
2、增大-XX:PermSize -XX:MaxPermSize(jdk1.7)、增大-XX:MetaspaceSize -XX:MaxMetaspaceSize(jdk1.8)

6、方法區溢位
原因:
1、方法區存放Class相關資訊,產生大量的類填滿方法區
3、ASM、Cglib、javassit、動態代理庫生成大量的類
4、大量的JSP檔案編譯成Servlet類檔案
5、OSGI的應用,同一個類檔案,被不同的載入器載入,也會視為不同的類
6、FULL GC之後,無法騰出足夠的記憶體給即將要分配的方法區物件
7、jvm配置引數有-Xnoclassgc
改進辦法:
1、增大-XX:PermSize -XX:MaxPermSize(jdk1.7)、增大-XX:MetaspaceSize -XX:MaxMetaspaceSize(jdk1.8)
2、檢查類、類載入器導致記憶體洩漏
3、不需要的類及時解除安裝、回收,觸發FULL GC回收PermGen space、Meta space
4、去掉jvm配置引數-Xnoclassgc


7、GC效率低下
原因:
1、話在GC上的時間超過98%
2、老年代釋放的記憶體小於2%
3、eden區釋放的記憶體小於2%
4、連續5次都出現上述情況,丟擲異常 GC overhead limit exceeded


8、tomcat多工程java.lang.OutOfMemoryError: PermGen space
原因:
1、tomcat部署的專案多
改進辦法:
2、多工程共享jar包,修改tomcat/conf/catalina.properties
shared.loader=D:/soft/tomcat8-share-lib/*.jar
shared.loader=/home/dev/soft/tomcat8-share-lib/*.jar