OOM:由於java堆或本機記憶體中的記憶體耗盡而發生記憶體不足錯誤。在JVM中,當JVM由於堆記憶體不足而無法分配物件時,會丟擲 OutOfMemoryError 錯誤,並且垃圾收集器無法提供更多的堆記憶體。
記憶體洩漏:如果應用程式正在使用記憶體,而應用程式在使用完記憶體後沒有釋放記憶體,則會發生記憶體洩漏。記憶體洩漏可能發生在java堆或本機記憶體中,並且最終會導致記憶體不足的情況。
故障排除請注意,並非所有以下專案都需要完成。有些問題只能透過以下幾個專案來解決。
故障排除步驟Java堆、本機記憶體和程序大小Java堆:這是JVM用來分配Java物件的記憶體。java堆記憶體的最大值是使用java命令列中的 -Xmx標誌指定的。如果沒有指定最大堆大小,那麼這個限制是由JVM考慮機器中的物理記憶體量和此時可用的可用記憶體量等因素決定的。始終建議指定max java heap值。
本機記憶體:這是JVM用於自身內部操作的記憶體。JVM將使用的本機記憶體堆的數量取決於生成的程式碼數量、建立的執行緒數量、GC期間用於儲存java物件資訊的記憶體以及程式碼生成、最佳化等期間使用的臨時空間。
如果有第三方本機模組,它也可以使用本機記憶體。例如,本機JDBC驅動程式分配本機記憶體。
本機記憶體的最大數量受任何給定作業系統上的虛擬程序大小限制以及已提交給帶有 -Xmxflag的java堆的記憶體量的限制。例如,如果應用程式總共可以分配3 GB,如果最大java堆是1 GB,那麼最大可能的本機記憶體大約是2 GB。
程序大小:程序大小將是java堆、本機記憶體以及載入的可執行檔案和庫所佔用的記憶體的總和。在32位作業系統上,程序的虛擬地址空間最多可達4GB。在這個4GB中,OS核心為自己保留了一些部分(通常為1-2GB)。其餘的可用於應用程式。
Windows:不同版本的Windows支援不同的程序大小。
redhat linux:RH-Linux上有不同的核心,這些不同的核心支援不同的程序大小。
對於其他作業系統,請參閱作業系統文件以瞭解您的配置。
有關為WebLogic伺服器配置所有這些的更多資訊,請參閱 調優Java虛擬機器(jvm) 。
程序地址空間和物理記憶體之間的差異每個程序都有自己的地址空間。在32位作業系統中,此地址空間的範圍為0到4 GB。這與機器中可用的RAM或交換空間無關。由於jvm gc效能要求java堆的大部分位於RAM中,因此新增RAM比增加交換更有幫助尺寸。用於例如,對於一個使用8gig Java堆的Java應用程式,新增4gig的RAM加上12gig的swap並沒有新增等量的純RAM那麼有用。
程序中的記憶體地址是虛擬的。核心將這個虛擬地址對映到物理地址。物理地址指向物理記憶體中的某個位置。在任何給定的時間,計算機中正在執行的程序使用的所有虛擬記憶體的總和都不能超過該計算機上可用的總物理記憶體。
為什麼會出現OOM問題,JVM在這種情況下會做什麼?java堆記憶體不足如果JVM不能在java堆中獲得更多的記憶體來分配更多的java物件,JVM就會丟擲java記憶體不足錯誤。如果java堆中充滿了活動物件,並且JVM無法再擴充套件java堆,那麼JVM將無法分配更多的java物件。
在這種情況下,JVM讓應用程式決定在丟擲 java.lang.OutOfMemoryError 錯誤. 例如,應用程式可能會處理此錯誤並決定以安全的方式關閉自身,或者決定忽略此錯誤執行。如果應用程式沒有處理這個錯誤,那麼丟擲這個錯誤的執行緒將退出(如果進行java執行緒轉儲,您將看不到這個執行緒)。
在WebLogic伺服器的情況下,如果這個錯誤是由一個execute執行緒丟擲的,則會處理這個錯誤,並且會記錄錯誤。如果連續丟擲此錯誤,則核心執行狀況監視器執行緒將關閉WebLogic伺服器。
本機堆記憶體不足如果JVM不能獲得更多的本機記憶體,它就會丟擲本機記憶體不足(native OOM)。這通常發生在程序達到該作業系統上的程序大小限制或計算機記憶體和交換空間不足時。
當這種情況發生時,JVM將處理本機OOM條件,記錄一條訊息,指出本機記憶體不足或無法獲取記憶體並退出。如果JVM或任何其他載入的模組(如libc或第三方模組)不能處理這種本地OOM情況,那麼OS將向JVM傳送sigabort訊號,使JVM退出。通常,JVM在收到SIGABROT訊號時會生成一個核心檔案。
除錯問題的步驟首先,確定它是java堆OOM還是本機OOM( 堆外 記憶體溢位 )
java.lang.OutOfMemoryErrorjava.lang.OutOfMemoryError
請注意,上面的訊息轉到stdout或stderr,而不是特定於應用程式的日誌檔案,如weblogic.log.
對於Java OOM:
收集並分析詳細垃圾收集(GC)輸出啟用詳細GC日誌記錄。為了有效地記錄GC活動,啟動時JVM中應包括以下選項:
1. 對於HotSpot: -verbose:gc 、 -XX:+PrintGCDetails 和 -XX:+PrintGCTimeStamps 。 Xloggc :也可以指定將GC詳細統計資訊重定向到輸出檔案。除了日誌檔案消耗的一些磁碟空間之外,基本GC的開銷是空的(有關更多詳細資訊,請參閱Java熱點VM選項)。
2. 對於JRockit: -verbose:gc , gcpause , memdbg (有關詳細資訊,請參閱 JRockit命令列選項 )。
確保JVM在丟擲java oom之前執行以下操作
完全GC執行:執行一個完整的GC,所有不可到達的、幻象的、弱的和不可到達的物件都被移除,並且這些空間被回收。有關不同級別的物件可達性的更多詳細資訊,請訪問: http://java.sun.com/docs/books/performance/1st_edition/html/JPAppGC.fm.html ,參見“A.4.1參考物件型別”。
您可以檢查是否在OOM訊息之前完成了完全GC。當完成完整的GC時,會列印如下訊息(格式因JVM而異:檢查JVM幫助訊息以瞭解格式)
[memory ] 7.160: GC 131072K->130052K (131072K) in 1057.359 ms
上述輸出的格式如下(注意:整個模式將使用相同的格式):
[memory ] <start>: GC <before>K-><after>K (<heap>K), <total> ms[memory ] <start> - start time of collection (seconds since jvm start)[memory ] <before> - memory used by objects before collection (KB)[memory ] <after> - memory used by objects after collection (KB)[memory ] <heap> - size of heap after collection (KB)[memory ] <total> - total time of collection (milliseconds)
但是,無法斷定是否使用詳細訊息刪除了軟/弱/幻影可及物件。如果垃圾收集演算法是分代演算法(對於Jrockit,是gencopy或gencon,對於其他jdk,是預設演算法),您還將看到詳細的輸出,如下所示:
[memory ] 2.414: Nursery GC 31000K->20760K (75776K), 0.469 ms
上面是託管GC(或年輕GC)迴圈,它將把活動物件從託管(或年輕空間)提升到舊空間。這個迴圈對於我們的分析並不重要。在JVM文件中可以找到關於時代演算法的更多細節。
如果GC迴圈沒有在java oom之前發生,那麼它就是一個JVM錯誤。
完全壓實:確保JVM進行了適當的壓縮工作,並且記憶體沒有碎片化,這可能會阻止分配大型物件並觸發java oom錯誤。
Java物件需要連續的記憶體。如果可用的空閒記憶體是碎片化的,那麼JVM將無法分配一個大物件,因為它可能不適合任何可用的空閒記憶體塊。在這種情況下,JVM應該進行完全壓縮,以便形成更多連續的可用記憶體來容納大型物件。
壓縮工作涉及將物件(資料)從java堆記憶體中的一個位置移動到另一個位置,並更新對這些物件的引用以指向新位置。JVM可能不會壓縮所有物件,除非有需要。這是為了減少GC迴圈的暫停時間。
我們可以透過分析詳細的gc訊息來檢查javaoom是否是由於碎片造成的。如果您看到類似於下面的輸出,其中丟擲OOM,即使有可用的java堆,那麼這是由於碎片造成的。
[memory ] 8.162: GC 73043K->72989K (131072K) in 12.938 ms[memory ] 8.172: GC 72989K->72905K (131072K) in 12.000 ms[memory ] 8.182: GC 72905K->72580K (131072K) in 13.509 msjava.lang.OutOfMemoryError
在上面的例子中,您可以看到指定的最大堆是128MB,當實際記憶體使用量只有72580K時JVM丟擲了OOM,堆使用率只有55%。因此,在這種情況下,碎片的效果是丟擲OOM,即使有45%的空閒堆。這是一個JVM錯誤或限制。您應該聯絡JVM供應商。
如果JVM工作正常(以上步驟中提到的所有事情),那麼java oom可能是一個應用程式問題。應用程式可能會不斷洩漏一些java記憶體,這可能會導致此問題。或者,應用程式使用更多的活動物件,需要更多的java堆記憶體。可以在應用程式中檢查以下內容:
在應用程式中快取——如果應用程式在記憶體中快取java物件,那麼我們應該確保這個快取不會不斷增長。快取中物件的數量應該有限制。我們可以嘗試減少這個限制,看看它是否減少了java堆的使用。
長壽命物件-如果應用程式中有長壽命物件,那麼我們可以儘可能減少物件的壽命。例如,調整HTTP會話超時將有助於更快地回收空閒會話物件。
記憶體洩漏:記憶體洩漏的一個例子是在applicationserver中使用資料庫連線池時。使用連線池時,必須在 finally 塊中顯式關閉JDBC語句和resultset物件。這是因為對池中的連線物件呼叫 close() 只會將連線返回到池中以供重用,而實際上不會關閉連線和關聯的語句/結果集物件。
建議遵循以下文件中建議的編碼實踐,以避免應用程式中的記憶體洩漏。
JDBC-關閉JDBC物件JNDI-關閉上下文JMS-釋放物件資源增加java堆—如果可能的話,我們還可以嘗試增加java堆,看看這是否解決了問題。
變通方法—作為一種臨時變通方法,當java堆使用率達到90%左右時,應用程式可以正常地重新啟動。遵循此解決方法時,可以將java max堆設定為儘可能高的值,以便應用程式需要更多的時間來填充所有java堆。可以透過在java命令列中新增' -verbosegc '標誌(見上文)來監視java堆的使用情況,該標誌將GC/堆使用情況資訊傳送到stdout或stderr。
如果上述建議都不適用於該應用程式,那麼我們需要使用基於JVMPI(jvmprofiler Interface)的探查器來找出哪些物件正在佔用java堆。探查器還提供了java程式碼中建立這些物件的位置的詳細資訊。本文件不包括每個探查器的詳細資訊。請參閱探查器文件,瞭解如何使用此探查器設定和啟動應用程式。一般來說,基於JVMPI的探查器有很高的開銷,並且大大降低了應用程式的效能。因此,不建議在生產環境中使用這些探查器。從這個站點可以瀏覽許多開源分析工具。
對於本機OOM問題
收集以下資訊:1. 啟用詳細GC日誌記錄(見上文)以監視java堆的使用情況。這將有助於理解此應用程式的java記憶體需求。
應該注意的是,與應用程式實際使用的java堆無關,指定的max heap量(在java命令列中使用 -Xmx 標誌)是在JVM啟動時保留的,並且該保留記憶體不可用於任何其他用途。
在JRockit的情況下,使用 -verbose 而不是 -verbosegc ,因為這除了提供GC資訊外,還提供了codegen資訊。
2. 從應用程式啟動到JVM耗盡本機記憶體,定期記錄程序虛擬記憶體大小。這將有助於瞭解程序是否真正達到了該作業系統的大小限制。
對於Windows,請使用以下過程監視虛擬程序大小:
1. 在開始->執行。。。對話方塊中,輸入“perfmon”並單擊“確定”。
3. 在生成的“新增計數器”對話方塊中選擇以下選項:
效能物件:程序(不是預設處理器)從列表中選擇計數器:虛擬位元組selectinstancesfromlist:選擇JVM(java)例項4. 單擊“新增”,然後單擊“關閉”
對於Unix或Linux,對於給定的PID,可以使用以下命令找到虛擬記憶體大小: ps-p<PID>-ovsz。
在Linux中,單個JVM例項中的每個java執行緒都顯示為一個單獨的程序。如果我們採用根java程序的PID就足夠了。根java程序可以使用ps命令的 --forest 選項找到。例如, ps-lU<user>--forest 將為指定使用者啟動的所有程序提供一個ASCII樹。
計算機記憶體可用性如果機器沒有足夠的RAM和交換空間,那麼作業系統將無法為該程序提供更多記憶體,這也可能導致記憶體不足。確保磁碟中RAM和交換空間的總和足以滿足該計算機中所有正在執行的程序的需要。
調整java堆如果java堆的使用率在max堆內,那麼減少java max堆將為JVM提供更多的本機記憶體。這不是一個解決方案,而是一個可以嘗試的解決方法。由於作業系統限制了程序大小,我們需要在java堆和本機堆之間取得平衡。
應用程式中的第三方本機模組或JNI程式碼檢查是否正在使用任何第三方本機模組(如資料庫驅動程式)。這些本機模組還可以分配本機記憶體,洩漏可能來自這些模組。為了縮小問題範圍,您應該嘗試在沒有這些第三方模組的情況下再現問題。例如,可以使用純java驅動程式而不是本機資料庫驅動程式。
檢查應用程式是否使用了一些JNI程式碼。這也可能導致本機記憶體洩漏,如果可能,您可以嘗試在不使用JNI程式碼的情況下執行應用程式。
如果在上述步驟之後找不到本機記憶體的源,那麼您需要與JVM供應商合作,以獲得一個特殊的構建,該構建可以跟蹤本機記憶體分配呼叫並提供有關洩漏的更多資訊。
JVM記憶體洩露檢查工具以下URL提供了一些特定於HP JVM的OOM情況的工具和提示:HP JVM工具/提示
JRockit特定功能JRockit支援JRA錄製(Java執行時分析器)。這有助於在JVM執行時收集資訊,這些資訊將提供有關應用程式的資訊,例如,正在執行的GC的數量、軟/弱/虛引用的數量、熱方法等。如果JVM有效能問題或掛起問題,則記錄幾分鐘並分析資料是很有用的。
為了解決記憶體洩漏問題,我們建議在每次舊的收集之後進行飛行記錄器錄製 JFR 並檢查堆的使用情況。持續上升的記憶體使用率可能表示記憶體洩漏。有關建立和解釋飛行記錄器錄製的資訊,請參閱《Oracle JRockit飛行記錄器執行時指南》。
流行的JVM堆分析工具Java VisualVMJavaVisualVM是一個工具,它提供了一個視覺化介面,用於檢視Java應用程式在Java虛擬機器(JVM)上執行時的詳細資訊,以及對這些應用程式進行故障排除和分析。Sun發行的Java開發工具包(JDK)提供了各種可選工具,包括Java VisualVM,用於檢索有關執行JVM軟體例項的不同型別的資料。例如,大多數以前獨立的工具JConsole、jstat、jinfo、jstack和jmap都是javavisualvm的一部分。JavaVisualVM將這些工具聯合起來,從JVM軟體中獲取資料,然後以圖形方式重新組織和呈現資訊,使您能夠統一檢視有關多個Java應用程式的不同資料,無論這些應用程式是在本地還是在遠端機上執行。此外,開發人員可以透過建立外掛並將其釋出到工具的內建更新中心來擴充套件JavaVisualVM以新增新功能。
Java應用程式開發人員可以使用Java VisualVM對應用程式進行故障排除,並監視和改進應用程式的效能。JavaVisualVM允許開發人員生成和分析堆轉儲,跟蹤記憶體洩漏,瀏覽平臺的MBean並在這些MBean上執行操作,執行和監視垃圾收集,以及執行輕量級記憶體和CPU評測。
JavaVisualVM在JDKversion6,update7中首先和Java平臺標準版(JavaSE)捆綁在一起。
有關更多資訊,請參閱Java VisualVM。
JRockit記憶體洩漏檢測器JRockit記憶體洩漏檢測器是一種工具,用於發現和查詢Java應用程式中記憶體洩漏的原因。JRockit記憶體洩漏檢測器的趨勢分析器發現了緩慢的洩漏,它顯示了詳細的堆統計資訊(包括引用洩漏物件的型別和例項)、分配站點,並提供了對記憶體洩漏原因的快速深入分析。記憶體洩漏檢測器使用先進的圖形表示技術,使它更容易導航和理解有時複雜的資訊。有關更多詳細資訊,請參閱記憶體洩漏檢測入門。
Eclipse記憶體分析工具(MAT)獨立記憶體分析工具(MAT)基於eclipsercp。如果您不想在執行堆分析的系統上安裝一個完整的IDE,那麼它非常有用。
原文連結:http://javakk.com/1335.html