本文將重溫此HotSpot VM錯誤,并為您提供建議和解決策略。
如果您不熟悉HotSpot JVM,我首先建議您從高角度查看其內部HotSpot JVM內存空間 。 為了使您了解與本機(C-Heap)內存空間有關的OutOfMemoryError問題,此知識很重要。
OutOfMemoryError:無法創建新的本機線程–這是什么?
讓我們從一個基本的解釋開始。 當內部JVM本機代碼無法創建新的Java線程時,將引發此HotSpot JVM錯誤。 更準確地說,這意味著JVM本機代碼無法從操作系統(Solaris,Linux,MAC,Windows等)創建新的“本機”線程。
我們可以從如下的OpenJDK 1.6和1.7實現中清楚地看到此邏輯:
不幸的是,在這一點上,您不會得到比該錯誤更詳細的信息,沒有跡象表明為什么JVM無法從OS創建新線程…
HotSpot JVM:32位還是64位?
在進行進一步分析之前,必須從Java或Java EE環境中確定的一個基本事實是,您正在使用的HotSpot VM版本是32位還是64位。
為什么這么重要? 您將很快了解到,這個JVM問題通常與本機內存耗盡有關。 在JVM進程或OS級別。 目前,請記住:
- 理論上,一個32位JVM進程允許增長到4 GB(在某些較舊的32位Windows版本上甚至更低)。
- 對于32位JVM進程,C堆正在與Java堆和PermGen空間競爭 ,例如C堆容量= 2-4 GB – Java堆大小 (-Xms,-Xmx)– PermGen大小(-XX :MaxPermSize)
- 從理論上講,允許使用64位JVM進程使用大多數可用的OS虛擬內存或最多16 EB(1600萬TB)
如您所見,如果您為32位JVM進程分配了較大的Java Heap(2 GB +),則本機內存空間容量將自動減少,這為JVM本機內存分配失敗打開了方便之門。
對于64位JVM進程,從JVM C堆的角度來看,您主要關心的是操作系統物理,虛擬和交換內存的容量和可用性。
好的,但是本機內存如何影響Java線程的創建?
現在回到我們的主要問題。 另一個需要了解的JVM基本方面是,從JVM創建的Java線程需要來自操作系統的本機內存。 現在,您應該開始了解問題的根源…
高級線程創建過程如下:
- 從Java程序和JDK請求一個新的Java線程
- 然后,JVM本機代碼嘗試從OS創建一個新的本機線程
- 然后,操作系統嘗試根據包括線程堆棧大小在內的屬性創建一個新的本機線程。 然后,將本地內存從OS分配(保留)到Java進程本地內存空間; 假設該進程具有足夠的地址空間(例如32位進程)來滿足請求
- 如果32位Java進程大小耗盡了其內存地址空間,例如2 GB,3 GB或4 GB進程大小限制,則OS將拒絕任何進一步的本機線程和內存分配
- 如果操作系統的虛擬內存已耗盡(包括Solaris交換空間耗盡),則操作系統還將拒絕任何進一步的線程和本機內存分配,因為對堆棧的線程訪問可能會產生SIGBUS錯誤,從而使JVM崩潰* http://bugs.sun .com / view_bug.do?bug_id = 6302804
綜上所述:
- 創建Java線程需要從OS獲得可用的本機內存。 適用于32位和64位JVM進程
- 對于32位JVM,創建Java線程還需要C-Heap或進程地址空間中可用的內存
問題診斷
既然您對本地內存和JVM線程的創建有了更好的了解,現在是時候來看看您的問題了。 首先,我建議您遵循以下分析方法:
- 確定您使用的是HotSpot 32位還是64位JVM
- 觀察到問題時,請進行JVM線程轉儲,并確定有多少個活動線程
- 在OOM問題復制之前和期間密切監視Java進程大小利用率
- 在OOM問題復制之前和期間,密切監視OS虛擬內存利用率; 如果使用Solaris OS,則包括交換內存空間利用率
根據上述適當的數據收集將使您可以收集適當的數據點,從而使您可以進行第一級調查。 下一步將是研究可能的問題模式,并確定哪種模式適用于您的問題案例。
問題模式1 – C堆耗盡(32位JVM)
根據我的經驗,OutOfMemoryError:對于32位JVM進程,無法創建新的本機線程是很常見的。 與C堆容量相比創建太多線程時,經常會出現此問題。 JVM線程轉儲分析和Java進程大小監視將使您確定是否是原因。
問題模式2 –操作系統虛擬內存耗盡(64位JVM)
在這種情況下,操作系統虛擬內存已完全耗盡。 這可能是由于一些64位JVM進程占用了大量內存,例如10 GB +和/或其他占用大量內存的流氓進程。 同樣,Java進程大小和OS虛擬內存監視將使您確定是否是原因。
問題模式3 –操作系統虛擬內存耗盡(32位JVM)
第三種情況不那么頻繁,但仍然可以觀察到。 診斷可能會稍微復雜一些,但是關鍵的分析點是確定哪些進程導致了OS虛擬內存的全部耗盡。 您的32位JVM進程可能是源,也可能是受害者,例如使用大多數OS虛擬內存的流氓進程,并阻止您的32位JVM進程為其線程創建過程保留更多的本機內存。
請注意,當Solaris上的OS虛擬內存或交換空間用盡時,此問題也可能表現為完全JVM崩潰(如下示例所示)。
#
# A fatal error has been detected by the Java Runtime Environment:
#
# java.lang.OutOfMemoryError: requested 32756 bytes for ChunkPool::allocate. Out of swap space?
#
# Internal Error (allocation.cpp:166), pid=2290, tid=27
# Error: ChunkPool::allocate
#
# JRE version: 6.0_24-b07
# Java VM: Java HotSpot(TM) Server VM (19.1-b02 mixed mode solaris-sparc )
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
#--------------- T H R E A D ---------------Current thread (0x003fa800): JavaThread "CompilerThread1" daemon [_thread_in_native, id=27, stack(0x65380000,0x65400000)]Stack: [0x65380000,0x65400000], sp=0x653fd758, free space=501k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
………………
本機內存耗盡:是癥狀還是根本原因?
現在,您了解了您的問題,并且知道了要處理的問題模式。 您現在準備提供解決問題的建議……是嗎?
您的工作尚未完成,請記住,此JVM OOM事件通常只是問題實際根源的“癥狀”。 根本原因通常更深,因此在向客戶提供建議之前,我建議您確實進行更深入的分析。 您要做的最后一件事就是簡單地解決并掩蓋癥狀。 僅當您對根本原因和生產環境容量要求有充分了解后,才應考慮采用諸如增加操作系統物理/虛擬內存或將所有JVM進程升級到64位之類的解決方案。
下一個要回答的基本問題是,發生OutOfMemoryError時有多少個線程處于活動狀態? 根據我在Java EE生產系統中的經驗,最常見的根本原因實際上是應用程序和/或Java EE容器在遇到不滿意的路徑(例如卡在遠程IO調用中的線程)時試圖在給定的時間創建太多線程在這種情況下,當嘗試滿足傳入的客戶端請求時,Java EE容器可能開始創建太多線程,從而增加了C堆和本機內存分配的壓力。 最重要的是,在責怪JVM之前,請執行盡職調查并確定是否要處理應用程序或Java EE容器線程調優問題是根本原因。
一旦了解并解決了根本原因(線程創建的根源),您就可以調整JVM和OS內存容量,以使其具有更高的容錯能力并更好地“生存”這些突發的線程激增方案。
建議:
- 首先執行JVM線程轉儲分析,并確定所有活動線程的源與已建立的基準。 確定是什么原因導致Java應用程序或Java EE容器在發生故障時創建了這么多線程
- 請確保您的監視工具密切監視Java VM進程大小和OS虛擬內存。 進行完整的根本原因分析將需要此關鍵數據
- 不要以為您正在處理OS內存容量問題。 查看所有正在運行的進程,并確定您的JVM進程實際上是問題的根源還是其他消耗所有虛擬內存的進程的受害者
- 重新訪問Java EE容器線程配置和JVM線程堆棧大小。 確定是否允許Java EE容器創建比JVM進程和/或OS可以處理的線程更多的線程
- 確定您的32位JVM的Java堆大小是否太大,從而阻止JVM創建足夠的線程來滿足客戶端請求。 在這種情況下,您將不得不考慮減小Java堆大小(如果可能),垂直擴展或升級到64位JVM。
救援能力規劃分析
從我以前關于Java EE企業性能問題的十大原因的文章中可能已經看到,缺乏容量規劃分析通常是問題的根源。 任何全面的負載和性能測試活動都應正確確定生產環境的Java EE容器線程,JVM和OS本機內存需求; 包括“不快樂”路徑的影響測量。 這種方法將使您的生產環境避免此類問題,從長遠來看,可以帶來更好的系統可伸縮性和穩定性。
別忘了分享!
參考: OutOfMemoryError:無法創建新的本機線程– Java EE支持模式和Java教程博客上的JCG合作伙伴 Pierre-Hugues Charbonneau 揭開了神秘的面紗 。
翻譯自: https://www.javacodegeeks.com/2012/09/outofmemoryerror-unable-to-create-new.html