Java內(nèi)存各部分OOM出現(xiàn)原因及解決方法(必看)
一,jvm內(nèi)存區(qū)域
1,程序計(jì)數(shù)器
一塊很小的內(nèi)存空間,作用是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器。
2,java棧
與程序計(jì)數(shù)器一樣,java棧(虛擬機(jī)棧)也是線程私有的,其生命周期與線程相同。通常存放基本數(shù)據(jù)類型,對象引用(一個指向?qū)ο笃鹗嫉刂返囊弥羔樆蛞粋€代表對象的句柄),reeturnAddress類型(指向一條字節(jié)碼指令的地址)
棧區(qū)域有兩種異常類型:如果線程請求的棧深度大于虛擬機(jī)所允許的深度,將拋StrackOverflowError異常;如果虛擬機(jī)??梢詣討B(tài)擴(kuò)展(大部分虛擬機(jī)都可動態(tài)擴(kuò)展),當(dāng)擴(kuò)展時無法申請到足夠的內(nèi)存時會拋出OutOfMemoryError異常。
3,本地方法棧
與虛擬機(jī)棧作用很相似,區(qū)別是虛擬機(jī)棧為虛擬機(jī)執(zhí)行java方法服務(wù),而本地方法棧則是為虛擬機(jī)用到的Native方法服務(wù)。和虛擬機(jī)棧一樣可能拋出StackOverflowError和OutOfMemoryError異常。
4,java堆
java Heap是jvm所管理的內(nèi)存中最大的區(qū)域。JavaHeap是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動時創(chuàng)建。主要存放對象實(shí)例。JavaHeap是垃圾收集器管理的主要區(qū)域,其可細(xì)分為新生代和老年代。如果在堆中沒有內(nèi)存完成實(shí)例分配,并且也無法再擴(kuò)展時,會拋出OutOfMemoryError異常。
5,方法區(qū)
與javaHeap一樣是各個線程共享的內(nèi)存區(qū)域,用于存放已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、及時編譯器編譯后的代碼等數(shù)據(jù)。當(dāng)方法區(qū)無法滿足內(nèi)存分配的需求時,將拋出OutOfMemoryError異常。方法同時包含常聽說的運(yùn)行時常量池,用于存放編譯期生成的各種字面量和符號引用。
6,直接內(nèi)存
直接內(nèi)存并不是虛擬機(jī)運(yùn)行時數(shù)據(jù)區(qū)的一部分,也不是java虛擬機(jī)規(guī)范中定義的內(nèi)存區(qū)域,是jvm外部的內(nèi)存區(qū)域,這部分區(qū)域也可能導(dǎo)致OutOfMemoryError異常。
二,jvm參數(shù)
-Xss(StackSpace)??臻g
-Xms ,-Xmx(heap memory space)堆空間:Heap是大家最為熟悉的區(qū)域,他是jvm用來存儲對象實(shí)例的區(qū)域,Heap在32位的系統(tǒng)中最大為2G,其大小通過-Xms和-Xmx來控制,-Xms為jvm啟動時申請的最小Heap內(nèi)存,默認(rèn)為物理內(nèi)存的1/64,但小于1G,-Xmx為jvm可申請的最大的Heap內(nèi)存,默認(rèn)為物理內(nèi)存的1/4,一般也小于1G,默認(rèn)當(dāng)空余堆內(nèi)存小于40%時,jvm會最大Heap的大小到-Xmx指定大小,可通過-XX:MinHeapFreeRatio來指定這個比例,當(dāng)空余堆內(nèi)存大于70%時,JVM會將Heap的大小往-Xms指定的大小調(diào)整,可通過-XX:MaxHeapFreeRatio來指定這個比例,但通常為了避免頻繁調(diào)整HeapSize的大小,將-Xms和-Xmx的值設(shè)為相同。
-XX:PermSize -XX:MaxPermSize :方法區(qū)持久代大小: 方法區(qū)域也是全局共享的,在一定的條件下它也會被 GC ,當(dāng)方法區(qū)域需要使用的內(nèi)存超過其允許的大小時,會拋出 OutOfMemory 的錯誤信息。
三,常見內(nèi)存溢出錯誤解決辦法
除了程序計(jì)數(shù)器外,虛擬機(jī)內(nèi)存的其他幾個運(yùn)行時區(qū)域都有發(fā)生OutOfMemoryError(OOM)異常的可能,
1,Java Heap 溢出
一般的異常信息:java.lang.OutOfMemoryError:Java heap spacess
java堆用于存儲對象實(shí)例,我們只要不斷的創(chuàng)建對象,并且保證GC Roots到對象之間有可達(dá)路徑來避免垃圾回收機(jī)制清除這些對象,就會在對象數(shù)量達(dá)到最大堆容量限制后產(chǎn)生內(nèi)存溢出異常。
出現(xiàn)這種異常,一般手段是先通過內(nèi)存映像分析工具(如Eclipse Memory Analyzer)對dump出來的堆轉(zhuǎn)存快照進(jìn)行分析,重點(diǎn)是確認(rèn)內(nèi)存中的對象是否是必要的,先分清是因?yàn)閮?nèi)存泄漏(Memory Leak)還是內(nèi)存溢出(Memory Overflow)。
如果是內(nèi)存泄漏,可進(jìn)一步通過工具(如Jrockit等工具)查看泄漏對象到GC Roots的引用鏈。于是就能找到泄漏對象時通過怎樣的路徑與GC Roots相關(guān)聯(lián)并導(dǎo)致垃圾收集器無法自動回收。
如果不存在泄漏,那就應(yīng)該檢查虛擬機(jī)的參數(shù)(-Xmx與-Xms)的設(shè)置是否適當(dāng)。
2,虛擬機(jī)棧和本地方法棧溢出
如果線程請求的棧深度大于虛擬機(jī)所允許的最大深度,將拋出StackOverflowError異常。
如果虛擬機(jī)在擴(kuò)展棧時無法申請到足夠的內(nèi)存空間,則拋出OutOfMemoryError異常
這里需要注意當(dāng)棧的大小越大可分配的線程數(shù)就越少。
3,運(yùn)行時常量池溢出
異常信息:java.lang.OutOfMemoryError:PermGen space
如果要向運(yùn)行時常量池中添加內(nèi)容,最簡單的做法就是使用String.intern()這個Native方法。該方法的作用是:如果池中已經(jīng)包含一個等于此String的字符串,則返回代表池中這個字符串的String對象;否則,將此String對象包含的字符串添加到常量池中,并且返回此String對象的引用。由于常量池分配在方法區(qū)內(nèi),我們可以通過-XX:PermSize和-XX:MaxPermSize限制方法區(qū)的大小,從而間接限制其中常量池的容量。
4,方法區(qū)溢出
方法區(qū)用于存放Class的相關(guān)信息,如類名、訪問修飾符、常量池、字段描述、方法描述等。
異常信息:java.lang.OutOfMemoryError:PermGen space
方法區(qū)溢出也是一種常見的內(nèi)存溢出異常,一個類如果要被垃圾收集器回收,判定條件是很苛刻的。在經(jīng)常動態(tài)生成大量Class的應(yīng)用中,要特別注意這點(diǎn)。
以上這篇Java內(nèi)存各部分OOM出現(xiàn)原因及解決方法(必看)就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
java 中遍歷取值異常(Hashtable Enumerator)解決辦法
這篇文章主要介紹了java 中遍歷取值異常(Hashtable Enumerator)解決辦法的相關(guān)資料,用迭代器取值時拋出的異常:java.util.NoSuchElementException: Hashtable Enumerator ,需要的朋友可以參考下2017-08-08SpringBoot擴(kuò)展點(diǎn)EnvironmentPostProcessor實(shí)例詳解
這篇文章主要介紹了SpringBoot擴(kuò)展點(diǎn)EnvironmentPostProcessor的相關(guān)知識,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04Java中的StringTokenizer實(shí)現(xiàn)字符串切割詳解
這篇文章主要介紹了Java中的StringTokenizer實(shí)現(xiàn)字符串切割詳解,java.util工具包提供了字符串切割的工具類StringTokenizer,Spring等常見框架的字符串工具類(如Spring的StringUtils),需要的朋友可以參考下2024-01-01Java學(xué)習(xí)之Lambda表達(dá)式的使用詳解
Lambda表達(dá)式是Java SE 8中一個重要的新特性,允許通過表達(dá)式來代替功能接口。本文將通過一些簡單的示例和大家講講Lamda表達(dá)式的使用,感興趣的可以了解一下2022-12-12編碼實(shí)現(xiàn)從無序鏈表中移除重復(fù)項(xiàng)(C和JAVA實(shí)例)
如果不能使用臨時緩存,你怎么實(shí)現(xiàn)無序鏈表中移除重復(fù)項(xiàng)(?C和JAVA實(shí)例無序鏈表中移除重復(fù)項(xiàng)。2013-10-10Java實(shí)現(xiàn)File轉(zhuǎn)換MultipartFile格式的例子
本文主要介紹了Java實(shí)現(xiàn)File轉(zhuǎn)換MultipartFile格式的例子,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07Spring?boot2.0?實(shí)現(xiàn)日志集成的方法(2)
這篇文章主要介紹了Spring?boot2.0?實(shí)現(xiàn)日志集成的方法,上一章講解了spring?boot日志簡單集成,這篇我們將日志進(jìn)行分類,常規(guī)日志、異常日志、監(jiān)控日志等,需要將日志輸出到不同的文件,具體內(nèi)容需要的小伙伴可以參考一下2022-04-04