www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當前位置:首頁 > 公眾號精選 > 架構師社區(qū)
[導讀]由于面試官僅提到OOM,但Java的OOM又分很多類型:堆溢出(“java.lang.OutOfMemoryError:Javaheapspace”)永久代溢出(“java.lang.OutOfMemoryError:Permgenspace”)不能創(chuàng)建線程(“java.lang...


百度二面:一個線程OOM了,其它線程還能運行嗎?

由于面試官僅提到OOM,但 Java 的OOM又分很多類型:

  • 堆溢出(“java.lang.OutOfMemoryError: Java heap space”)

  • 永久代溢出(“java.lang.OutOfMemoryError:Permgen space”)

  • 不能創(chuàng)建線程(“java.lang.OutOfMemoryError:Unable to create new native thread”)

OOM在《Java虛擬機規(guī)范》里,除程序計數(shù)器,虛擬機內(nèi)存的其他幾個運行時區(qū)域都可能發(fā)生OOM,那本文的目的是啥呢?

  • 通過代碼驗證《Java虛擬機規(guī)范》中描述的各個運行時區(qū)域儲存的內(nèi)容

  • 在工作中遇到實際的內(nèi)存溢出異常時,能根據(jù)異常的提示信息迅速得知是哪個區(qū)域的內(nèi)存溢出,知道怎樣的代碼可能會導致這些區(qū)域內(nèi)存溢出,以及出現(xiàn)這些異常后該如何處理。

本文代碼均由筆者在基于OpenJDK 8中的HotSpot虛擬機上進行過實際測試。

1 Java堆溢出



Java堆用于儲存對象實例,只要不斷地創(chuàng)建對象,并且保證GC Roots到對象之間有可達路徑來避免GC機制清除這些對象,則隨對象數(shù)量增加,總容量觸及最大堆的容量限制后就會產(chǎn)生內(nèi)存溢出異常。

限制Java堆的大小20MB,不可擴展

-XX: HeapDumpOnOutOf-MemoryError可以讓虛擬機在出現(xiàn)內(nèi)存溢出異常的時候Dump出當前的內(nèi)存堆轉儲快照。



案例1

百度二面:一個線程OOM了,其它線程還能運行嗎?

百度二面:一個線程OOM了,其它線程還能運行嗎?

不久后報錯!
百度二面:一個線程OOM了,其它線程還能運行嗎?
Java堆內(nèi)存的OOM是實際應用中最常見的內(nèi)存溢出異常場景。出現(xiàn)Java堆內(nèi)存溢出時,異常堆棧信息“java.lang.OutOfMemoryError”會跟隨進一步提示“Java heap space”。

那既然發(fā)生了,如何解決這個內(nèi)存區(qū)域的異常呢?
一般先通過內(nèi)存映像分析工具(如jprofile)對Dump出來的堆轉儲快照進行分析。
第一步首先確認內(nèi)存中導致OOM的對象是否是必要的,即先分清楚:

  • 內(nèi)存泄漏(Memory Leak)

  • 內(nèi)存溢出(Memory Overflow)

使用 jprofile打開的堆轉儲快照文件(java_pid44526.hprof)

百度二面:一個線程OOM了,其它線程還能運行嗎?

若是內(nèi)存泄漏,可查看泄漏對象到GC Roots的引用鏈,找到泄漏對象是通過怎樣的引用路徑、與哪些GC Roots相關聯(lián),才導致垃圾收集器無法回收它們。根據(jù)泄漏對象的類型信息以及它到GC Roots引用鏈的信息,一般可以比較準確地定位到這些對象創(chuàng)建的位置,進而找出產(chǎn)生內(nèi)存泄漏的代碼的具體位置。

若不是內(nèi)存泄漏,即就是內(nèi)存中的對象確實都必須存活,則應:

  1. 檢查JVM堆參數(shù)(-Xmx與-Xms)的設置,與機器內(nèi)存對比,看是否還有向上調整的空間

  2. 再檢查代碼是否存在某些對象生命周期過長、持有狀態(tài)時間過長、存儲結構設計不合理等情況,盡量減少程序運 行期的內(nèi)存消耗

以上是處理Java堆內(nèi)存問題的簡略思路。



案例 2

百度二面:一個線程OOM了,其它線程還能運行嗎?

JVM啟動參數(shù)設置:

-Xms5m -Xmx10m -XX: HeapDumpOnOutOfMemoryError百度二面:一個線程OOM了,其它線程還能運行嗎?
百度二面:一個線程OOM了,其它線程還能運行嗎?

JVM堆空間的變化
百度二面:一個線程OOM了,其它線程還能運行嗎?
堆的使用大小,突然抖動!說明當一個線程拋OOM后,它所占據(jù)的內(nèi)存資源會全部被釋放掉,而不會影響其他線程的正常運行!
所以一個線程溢出后,進程里的其他線程還能照常運行。
發(fā)生OOM的線程一般情況下會死亡,也就是會被終結掉,該線程持有的對象占用的heap都會被gc了,釋放內(nèi)存。因為發(fā)生OOM之前要進行gc,就算其他線程能夠正常工作,也會因為頻繁gc產(chǎn)生較大的影響。


2 虛擬機棧/本地方法棧溢出



由于HotSpot JVM并不區(qū)分虛擬機棧和本地方法棧,因此HotSpot的-Xoss參數(shù)(設置本地方法棧的大?。╇m然存在,但無任何效果,棧容量只能由-Xss參數(shù)設定。

關于虛擬機棧和本地方法棧,《Java虛擬機規(guī)范》描述如下異常:

  1. 若線程請求的棧深度大于虛擬機所允許的最大深度,將拋出StackOverflowError異常

  2. 若虛擬機的棧內(nèi)存允許動態(tài)擴展,當擴展棧容量無法申請到足夠的內(nèi)存時,將拋出 OutOfMemoryError異常

《Java虛擬機規(guī)范》明確允許JVM實現(xiàn)自行選擇是否支持棧的動態(tài)擴展,而HotSpot虛擬機的選擇是不支持擴展,所以除非在創(chuàng)建線程申請內(nèi)存時就因無法獲得足夠內(nèi)存而出現(xiàn)OOM,否則在線程運行時是不會因為擴展而導致內(nèi)存溢出的,只會因為棧容量無法容納新的棧幀而導致StackOverflowError。

如何驗證呢?

做倆實驗,先在單線程操作,嘗試下面兩種行為是否能讓HotSpot OOM:

使用-Xss減少棧內(nèi)存容量

示例
百度二面:一個線程OOM了,其它線程還能運行嗎?

結果
百度二面:一個線程OOM了,其它線程還能運行嗎?
拋StackOverflowError異常,異常出現(xiàn)時輸出的堆棧深度相應縮小。


不同版本的Java虛擬機和不同的操作系統(tǒng),棧容量最小值可能會有所限制,這主要取決于操作系統(tǒng)內(nèi)存分頁大小。譬如上述方法中的參數(shù)-Xss160k可以正常用于62位macOS系統(tǒng)下的JDK 8,但若用于64位Windows系統(tǒng)下的JDK 11,則會提示棧容量最小不能低于180K,而在Linux下這個值則可能是228K,如果低于這個最小限制,HotSpot虛擬器啟動時會給出如下提示:

The stack size specified is too small, Specify at

定義大量局部變量,增大此方法幀中本地變量表的長度

示例:
百度二面:一個線程OOM了,其它線程還能運行嗎?

結果:
百度二面:一個線程OOM了,其它線程還能運行嗎?所以無論是由于棧幀太或虛擬機棧容量太小,當新的棧幀內(nèi)存無法分配時, HotSpot 都拋SOF??扇粼谠试S動態(tài)擴展棧容量大小的虛擬機上,相同代碼則會導致不同情況。

若測試時不限于單線程,而是不斷新建線程,在HotSpot上也會產(chǎn)生OOM。但這樣產(chǎn)生OOM和棧空間是否足夠不存在直接的關系,主要取決于os本身內(nèi)存使用狀態(tài)。甚至說這種情況下,給每個線程的棧分配的內(nèi)存越大,反而越容易產(chǎn)生OOM。
不難理解,os分配給每個進程的內(nèi)存有限制,比如32位Windows的單個進程最大內(nèi)存限制為2G。HotSpot提供參數(shù)可以控制Java堆和方法區(qū)這兩部分的內(nèi)存的最大值,那剩余的內(nèi)存即為2G(os限制)減去最大堆容量,再減去最大方法區(qū)容量,由于程序計數(shù)器消耗內(nèi)存很小,可忽略,若把直接內(nèi)存和虛擬機進程本身耗費的內(nèi)存也去掉,剩下的內(nèi)存就由虛擬機棧和本地方法棧來分配了。因此為每個線程分配到的棧內(nèi)存越大,可以建立的線程數(shù)量越少,建立線程時就越容易把剩下的內(nèi)存耗盡:

示例:
百度二面:一個線程OOM了,其它線程還能運行嗎?

結果:

Exception in thread "main" java.lang.OutOfMemoryError: unable to create native thread

出現(xiàn)SOF時,會有明確錯誤堆棧可供分析,相對容易定位問題。如果使用HotSpot虛擬機默認參數(shù),棧深度在大多數(shù)情況下(因為每個方法壓入棧的幀大小并不是一樣的)到達1000~2000沒有問題,對于正常的方法調用(包括不能做尾遞歸優(yōu)化的遞歸調用),這個深度應該完全夠用。但如果是建立過多線程導致的內(nèi)存溢出,在不能減少線程數(shù)量或者更換64位虛擬機的情況下,就只能通過減少最大堆和減少棧容量換取更多的線程。這種通過“減少內(nèi)存”手段解決內(nèi)存溢出的方式,如果沒有這方面處理經(jīng)驗,一般比較難以想到。也是由于這種問題較為隱蔽,從 JDK 7起,以上提示信息中“unable to create native thread”后面,虛擬機會特別注明原因可能是“possibly

#define OS_NATIVE_THREAD_CREATION_FAILED_MSG "unable to create native thread: possibly out of memory or process/resource limits reached"3 方法區(qū)和運行時常量池溢出



運行時常量池是方法區(qū)的一部分,所以這兩個區(qū)域的溢出測試可以放到一起。

HotSpot從JDK 7開始逐步“去永久代”,在JDK 8中完全使用元空間代替永久代。

那么方法區(qū)使用“永久代”還是“元空間”來實現(xiàn),對程序有何影響呢?



String::intern()

百度二面:一個線程OOM了,其它線程還能運行嗎?

一個本地方法:若字符串常量池中已經(jīng)包含一個等于此String對象的字符串,則返回代表池中這個字符串的String對象的引用;否則,會將此String對象包含的字符串添加到常量池,并且返回此String對象的引用。

在JDK6或之前HotSpot虛擬機,常量池都是分配在永久代,可以通過如下兩個參數(shù):
百度二面:一個線程OOM了,其它線程還能運行嗎?
限制永久代的大小,即可間接限制其中常量池的容量,

實例百度二面:一個線程OOM了,其它線程還能運行嗎?

結果:

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space at java.lang.String.intern(Native Method) at org.fenixsoft.oom.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java: 18)

可見,運行時常量池溢出時,在OutOfMemoryError異常后面跟隨的提示信息是“PermGen space”,說明運行時常量池的確是屬于方法區(qū)(即JDK 6的HotSpot虛擬機中的永久代)的 一部分。

而使用JDK 7或更高版本的JDK來運行這段程序并不會得到相同的結果,無論是在JDK 7中繼續(xù)使 用-XX:MaxPermSize參數(shù)或者在JDK 8及以上版本使用-XX:MaxMeta-spaceSize參數(shù)把方法區(qū)容量同樣限制在6MB,也都不會重現(xiàn)JDK 6中的溢出異常,循環(huán)將一直進行下去,永不停歇。
這種變化是因為自JDK 7起,原本存放在永久代的字符串常量池被移至Java堆,所以在JDK 7及以上版 本,限制方法區(qū)的容量對該測試用例來說是毫無意義。

這時候使用-Xmx參數(shù)限制最大堆到6MB就能看到以下兩種運行結果之一,具體取決于哪里的對象分配時產(chǎn)生了溢出:

// OOM異常一:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.base/java.lang.Integer.toString(Integer.java:440) at java.base/java.lang.String.valueOf(String.java:3058) at RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:12)
// OOM異常二:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.base/java.util.HashMap.resize(HashMap.java:699) at java.base/java.util.HashMap.putVal(HashMap.java:658) at java.base/java.util.HashMap.put(HashMap.java:607) at java.base/java.util.HashSet.add(HashSet.java:220) at RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java from InputFile-Object:14)

字符串常量池的實現(xiàn)位置還有很多趣事:
百度二面:一個線程OOM了,其它線程還能運行嗎?
JDK 6中運行,結果是兩個false
JDK 7中運行,一個true和一個false
百度二面:一個線程OOM了,其它線程還能運行嗎?
因為JDK6的intern()會把首次遇到的字符串實例復制到永久代的字符串常量池中,返回的也是永久代里這個字符串實例的引用,而由StringBuilder創(chuàng)建的字符串對象實例在 Java 堆,所以不可能是同一個引用,結果將返回false。

JDK 7及以后的intern()無需再拷貝字符串的實例到永久代,字符串常量池已移到Java堆,只需在常量池里記錄一下首次出現(xiàn)的實例引用,因此intern()返回的引用和由StringBuilder創(chuàng)建的那個字符串實例是同一個。

str2比較返回false,這是因為“java”這個字符串在執(zhí)行String-Builder.toString()之前就已經(jīng)出現(xiàn)過了,字符串常量池中已經(jīng)有它的引用,不符合intern()方法要求“首次遇到”的原則,而“計算機軟件”這個字符串則是首次 出現(xiàn)的,因此結果返回true!

對于方法區(qū)的測試,基本的思路是運行時產(chǎn)生大量類去填滿方法區(qū),直到溢出。雖然直接使用Java SE API也可動態(tài)產(chǎn)生類(如反射時的 GeneratedConstructorAccessor和動態(tài)代理),但操作麻煩。
借助了CGLib直接操作字節(jié)碼運行時生成大量動態(tài)類。當前的很多主流框架,如Spring、Hibernate對類進行增強時,都會使用到 CGLib字節(jié)碼增強,當增強的類越多,就需要越大的方法區(qū)以保證動態(tài)生成的新類型可以載入內(nèi)存。
很多運行于JVM的動態(tài)語言(例如Groovy)通常都會持續(xù)創(chuàng)建新類型來支撐語言的動態(tài)性,隨著這類動態(tài)語言的流行,與如下代碼相似的溢出場景也越來越容易遇到

在JDK 7中的運行結果:

Caused by: java.lang.OutOfMemoryError: PermGen space at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632) at java.lang.ClassLoader.defineClass(ClassLoader.java:616)

JDK8及以后:可以使用

-XX:MetaspaceSize=10M-XX:MaxMetaspaceSize=10M

設置元空間初始大小以及最大可分配大小。
1.如果不指定元空間的大小,默認情況下,元空間最大的大小是系統(tǒng)內(nèi)存的大小,元空間一直擴大,虛擬機可能會消耗完所有的可用系統(tǒng)內(nèi)存。
2.如果元空間內(nèi)存不夠用,就會報OOM。
3.默認情況下,對應一個64位的服務端JVM來說,其默認的-XX:MetaspaceSize值為21MB,這就是初始的高水位線,一旦元空間的大小觸及這個高水位線,就會觸發(fā)Full GC并會卸載沒有用的類,然后高水位線的值將會被重置。
4.從第3點可以知道,如果初始化的高水位線設置過低,會頻繁的觸發(fā)Full GC,高水位線會被多次調整。所以為了避免頻繁GC以及調整高水位線,建議將-XX:MetaspaceSize設置為較高的值,而-XX:MaxMetaspaceSize不進行設置。

JDK8 運行結果:
百度二面:一個線程OOM了,其它線程還能運行嗎?
一個類如果要被gc,要達成的條件比較苛刻。在經(jīng)常運行時生成大量動態(tài)類的場景,就應該特別關注這些類的回收狀況。
這類場景除了之前提到的程序使用了CGLib字節(jié)碼增強和動態(tài)語言外,常見的還有:

  • 大量JSP或動態(tài)產(chǎn)生JSP 文件的應用(JSP第一次運行時需要編譯為Java類)

  • 基于OSGi的應用(即使是同一個類文件,被不同的加載器加載也會視為不同的類)

JDK8后,永久代完全廢棄,而使用元空間作為其替代者。在默認設置下,前面列舉的那些正常的動態(tài)創(chuàng)建新類型的測試用例已經(jīng)很難再迫使虛擬機產(chǎn)生方法區(qū)OOM。
為了讓使用者有預防實際應用里出現(xiàn)類似于如上代碼那樣的破壞性操作,HotSpot還是提供了一些參數(shù)作為元空間的防御措施:

  • -XX:MetaspaceSize
    指定元空間的初始空間大小,以字節(jié)為單位,達到該值就會觸發(fā)垃圾收集進行類型卸載,同時收集器會對該值進行調整。如果釋放了大量的空間,就適當降低該值,如果釋放了很少空間,則在不超過-XX:MaxMetaspaceSize(如果設置了的話)的情況下,適當提高該值

  • -XX:MaxMetaspaceSize
    設置元空間最大值,默認-1,即不限制,或者說只受限于本地內(nèi)存的大小

  • -XX:MinMetaspaceFreeRatio
    在GC后控制最小的元空間剩余容量的百分比,可減少因為元空間不足導致的GC頻率

  • -XX:Max-MetaspaceFreeRatio
    控制最大的元空間剩余容量的百分比

4 本機直接內(nèi)存溢出



直接內(nèi)存(Direct Memory)的容量大小可通過-XX:MaxDirectMemorySize指定,若不指定,則默認與Java堆最大值(-Xmx)一致。

這里越過DirectByteBuffer類,直接通過反射獲取Unsafe實例進行內(nèi)存分配。
Unsafe類的getUnsafe()指定只有引導類加載器才會返回實例,體現(xiàn)了設計者希望只有虛擬機標準類庫里面的類才能使用Unsafe,JDK10時才將Unsafe的部分功能通過VarHandle開放給外部。
因為雖然使用DirectByteBuffer分配內(nèi)存也會拋OOM,但它拋異常時并未真正向os申請分配內(nèi)存,而是通過計算得知內(nèi)存無法分配,就在代碼里手動拋了OOM,真正申請分配內(nèi)存的方法是Unsafe::allocateMemory()

使用unsafe分配本機內(nèi)存:
百度二面:一個線程OOM了,其它線程還能運行嗎?

結果:
百度二面:一個線程OOM了,其它線程還能運行嗎?
由直接內(nèi)存導致的內(nèi)存溢出,一個明顯的特征是在Heap Dump文件中不會看見有什么明顯異常,若發(fā)現(xiàn)內(nèi)存溢出之后產(chǎn)生的Dump文件很小,而程序中又直接或間接使用了 DirectMemory(比如使用NIO),則該考慮直接內(nèi)存了。


本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或將催生出更大的獨角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉型技術解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關鍵字: 汽車 人工智能 智能驅動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務中斷的風險,如企業(yè)系統(tǒng)復雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務連續(xù)性,提升韌性,成...

關鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質量流程IT總裁陶景文發(fā)表了演講。

關鍵字: 華為 12nm EDA 半導體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權最終是由生態(tài)的繁榮決定的。

關鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務引領增長 以科技創(chuàng)新為引領,提升企業(yè)核心競爭力 堅持高質量發(fā)展策略,塑強核心競爭優(yōu)勢...

關鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術學會聯(lián)合牽頭組建的NVI技術創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術創(chuàng)新聯(lián)...

關鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關鍵字: BSP 信息技術
關閉
關閉