posts - 35,  comments - 7,  trackbacks - 0


          內(nèi)存不足 (OOM) - 由于 java 堆或本地內(nèi)存中的內(nèi)存耗盡,應(yīng)用程序顯示“內(nèi)存不足”錯(cuò)誤。
          內(nèi)存泄漏 - java 堆或本地內(nèi)存的持續(xù)內(nèi)存增長,最終將導(dǎo)致內(nèi)存不足狀態(tài)。調(diào)試內(nèi)存泄漏狀態(tài)的技術(shù)與調(diào)試內(nèi)存不足狀態(tài)的技術(shù)相同。

          Java 堆 - 這是 JVM 用來分配 java 對(duì)象的內(nèi)存。java 堆內(nèi)存的最大值用 java 命令行中的 -Xmx 標(biāo)志來指定。
          如果未指定最大的堆大小,那么該極限值由 JVM 根據(jù)諸如計(jì)算機(jī)中的物理內(nèi)存量和該時(shí)刻的可用空閑內(nèi)存量這類
          因素來決定。始終建議您指定最大的 java 堆值。

          本地內(nèi)存 - 這是 JVM 用于其內(nèi)部操作的內(nèi)存。JVM 將使用的本地內(nèi)存堆數(shù)量取決于生成的代碼量、創(chuàng)建的線程、
          GC 期間用于保存 java 對(duì)象信息的內(nèi)存,以及在代碼生成、優(yōu)化等過程中使用的臨時(shí)空間。
          如果有一個(gè)第三方本地模塊,那么它也可能使用本地內(nèi)存。例如,本地 JDBC 驅(qū)動(dòng)程序?qū)⒎峙浔镜貎?nèi)存。
          最大本地內(nèi)存量受到任何特定操作系統(tǒng)上的虛擬進(jìn)程大小限制的約束,也受到用 -Xmx 標(biāo)志指定用于 java 堆的內(nèi)存量的限制。
          例如,如果應(yīng)用程序能分配總計(jì)為 3 GB 的內(nèi)存量,并且最大 java 堆的大小為 1 GB,那么本地內(nèi)存量的最大值可能在 2 GB 左右。
           
          進(jìn)程大小 - 進(jìn)程大小將是 java 堆、本地內(nèi)存與加載的可執(zhí)行文件和庫所占用內(nèi)存的總和。在 32 位操作系統(tǒng)上,進(jìn)程的虛擬地址
          空間最大可達(dá)到 4 GB。從這 4 GB 內(nèi)存中,操作系統(tǒng)內(nèi)核為自己保留一部分內(nèi)存(通常為 1 - 2 GB)。剩余內(nèi)存可用于應(yīng)用程序。

          Windows缺省情況下,2 GB 可用于應(yīng)用程序,剩余 2 GB 保留供內(nèi)核使用。但是,在 Windows 的一些變化版本中,有一個(gè) /3GB 開關(guān)
          可用于改變?cè)摲峙浔嚷?,使?yīng)用程序能夠獲得 3 GB。有關(guān) /3GB 開關(guān)的詳細(xì)信息,可以在以下網(wǎng)址中找到:
          http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ddtools/hh/ddtools/bootini_1fcj.asp
          RH Linux AS 2.1 - 3 GB 可用于應(yīng)用程序。

          進(jìn)程地址空間和物理內(nèi)存之間的差異:
          每個(gè)進(jìn)程都獲得其自有的地址空間。在 32 位操作系統(tǒng)中,此地址空間范圍為 0 到 4 GB。此范圍與計(jì)算機(jī)的可用隨機(jī)存取內(nèi)存 (RAM)
          或交換空間無關(guān)。計(jì)算機(jī)中的可用物理內(nèi)存總量是該計(jì)算機(jī)上的可用 RAM 和交換空間之和。所有運(yùn)行的進(jìn)程共享這些物理內(nèi)存。

          進(jìn)程內(nèi)的存儲(chǔ)地址是虛擬地址。內(nèi)核將此虛擬地址映射到物理地址上。物理地址指向物理內(nèi)存中的某個(gè)位置。在任一給定時(shí)間,計(jì)算機(jī)
          中運(yùn)行進(jìn)程所使用的全部虛擬內(nèi)存的總和不能超過該計(jì)算機(jī)上可用物理內(nèi)存的總量。


          為什么會(huì)發(fā)生 OOM 問題,JVM 在這種情況下如何處理?

          如果 JVM 不能在 java 堆中獲得更多內(nèi)存來分配更多 java 對(duì)象,將會(huì)拋出 java 內(nèi)存不足 (java OOM) 錯(cuò)誤。如果 java 堆充滿了活
          動(dòng)對(duì)象,并且 JVM 無法再擴(kuò)展 java 堆,那么它將不能分配更多 java 對(duì)象。
          在這種情況下,JVM 讓應(yīng)用程序決定在拋出 java.lang.OutOfMemoryError 后該執(zhí)行什么操作。例如,應(yīng)用程序可以處理此錯(cuò)誤,并決定
          以安全方式自行關(guān)閉或決定忽略此錯(cuò)誤。如果應(yīng)用程序不處理此錯(cuò)誤,那么拋出此錯(cuò)誤的線程將退出(如果您進(jìn)行 java Thread Dump,那
          么將看不到該線程)。

          在使用 Weblogic Server 的情況下,如果此錯(cuò)誤是由某個(gè)執(zhí)行線程拋出的,則會(huì)處理此錯(cuò)誤并將其記錄在日志中。如果連續(xù)拋出此錯(cuò)誤,
          那么核心運(yùn)行狀況監(jiān)視器線程將關(guān)閉 Weblogic Server。

          如果 JVM 無法獲得更多本地內(nèi)存,它將拋出本地內(nèi)存不足(本地 OOM)錯(cuò)誤。當(dāng)進(jìn)程到達(dá)操作系統(tǒng)的進(jìn)程大小限值,或者當(dāng)計(jì)算機(jī)用完
          RAM 和交換空間時(shí),通常會(huì)發(fā)生這種情況。當(dāng)發(fā)生這種情況時(shí),JVM 處理本地 OOM 狀態(tài),記錄說明它已用完本地內(nèi)存或無法獲得內(nèi)存的
          消息,然后退出。如果 JVM 或加載的任何其它模塊(如 libc 或第三方模塊)不處理這個(gè)本地 OOM 狀態(tài),那么操作系統(tǒng)將給 JVM 發(fā)送
          命令 JVM 退出的 sigabort 信號(hào)。通常情況下,JVM 收到 sigabort 信號(hào)時(shí)將會(huì)生成一個(gè)核心文件。


          排除故障的步驟:
          確定是 Java OOM 還是本地 OOM:
          如果 stdout/stderr 消息說明這是一個(gè) java.lang.OutOfMemoryError,那么這就是 Java OOM
          如果 stdout/stderr 消息說明無法獲得內(nèi)存,那么這就是本地 OOM
          請(qǐng)注意,上述消息僅發(fā)送到 stdout 或 stderr 中,而不發(fā)送到應(yīng)用程序特定的日志文件(如 weblogic.log)


          對(duì)于 Java OOM,收集和分析 verbose:gc 輸出 在 java 命令行中添加“-verbose:gc”標(biāo)志。這樣將會(huì)把 GC 活動(dòng)信息打印到
          stdout/stderr。將 stdout/stderr 重定向到一個(gè)文件。運(yùn)行應(yīng)用程序,直到該問題重現(xiàn)。 確保 JVM 在拋出 java OOM 之
          前完成下列任務(wù),執(zhí)行一次完整 GC 運(yùn)行,并且刪除了所有不可及對(duì)象以及虛可及、弱可及、軟可及對(duì)象,并回收了那些空間。
          有關(guān)不同級(jí)別的對(duì)象可及性的詳細(xì)信息,可以在以下網(wǎng)址中可找到:
          http://java.sun.com/developer/technicalArticles/ALT/RefObj
          您可以檢查是否在發(fā)出 OOM 消息之前執(zhí)行了完整 GC 運(yùn)行。當(dāng)完成一次完整 GC 運(yùn)行時(shí),將會(huì)打印類似如下消息(格式取決
          于 JVM - 請(qǐng)查看 JVM 幫助信息以了解有關(guān)格式)
          [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)

          但是,沒有辦法斷定是否使用 verbose 消息刪除了軟/弱/虛可及的對(duì)象。如果您懷疑在拋出 OOM 時(shí)這些對(duì)象仍然存在,請(qǐng)
          與 JVM 供應(yīng)商聯(lián)系。

          如果垃圾回收算法是一種按代回收算法(對(duì)于 Jrockit 為 gencopy 或 gencon,對(duì)于其它 JDK 則是缺省算法),您也將看
          到類似如下的 verbose 輸出:
          [memory ] 2.414: Nursery GC 31000K->20760K (75776K), 0.469 ms
          以上是 nursery GC(即 young GC)周期,它將把活動(dòng)對(duì)象從 nursery(或 young 空間)提升到 old 空間。這個(gè)周期對(duì)我
          們的分析不重要。有關(guān)按代回收算法的詳細(xì)信息,可以在 JVM 文檔中找到。
          如果在 java OOM 之前未發(fā)生 GC 周期,那么這是一個(gè) JVM 錯(cuò)誤。

          完全壓縮:
          確保 JVM 執(zhí)行了適當(dāng)?shù)膲嚎s工作,并且內(nèi)存并未成碎片(否則會(huì)阻止分配大對(duì)象并觸發(fā) java OOM 錯(cuò)誤)。
          Java 對(duì)象要求內(nèi)存是連續(xù)的。如果可用空閑內(nèi)存是一些碎片,那么 JVM 將無法分配大對(duì)象,因?yàn)樗赡軣o法放入任何可用
          空閑內(nèi)存塊中。在這種情況下,JVM 將執(zhí)行一次完全壓縮,以便形成更多連續(xù)的空閑內(nèi)存來容納大對(duì)象。
          壓縮工作包括在 java 堆內(nèi)存中將對(duì)象從一個(gè)位置移動(dòng)到另一個(gè)位置,以及更新對(duì)這些對(duì)象的引用以指向新位置。除非確有
          必要,否則 JVM 不會(huì)壓縮所有對(duì)象。這是為了減少 GC 周期的暫停時(shí)間。
          我們可以通過分析 verbose gc 消息來檢查 java OOM 是否由碎片引起。如果您看到類似如下的輸出(在此無論是否有可用
          的空閑 java 堆都會(huì)拋出 OOM),那么這就是由碎片引起的。

          [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 ms
          java.lang.OutOfMemoryError
          在上述情況中您可以看到,所指定的最大堆內(nèi)存是 128MB,并且當(dāng)實(shí)際內(nèi)存使用量僅為 72580K 時(shí),JVM 拋出 OOM。堆使用量
          僅為 55%。因此在這種情況下,碎片影響是:即使還有 45% 的空閑堆,內(nèi)存也會(huì)拋出 OOM。這是一個(gè) JVM 錯(cuò)誤或缺陷。您應(yīng)
          當(dāng)與 JVM 供應(yīng)商聯(lián)系。

          如果 JVM 一切都正常(上一步中提到的所有操作),那么此 java OOM 可能是應(yīng)用程序的問題。應(yīng)用程序可能在不斷泄漏一些
          java 內(nèi)存,而這可能導(dǎo)致出現(xiàn)上述問題?;蛘?,應(yīng)用程序使用更多的活動(dòng)對(duì)象,因此它需要更多 java 堆內(nèi)存。在應(yīng)用程序中
          可以檢查以下方面: 應(yīng)用程序中的緩存功能 - 如果應(yīng)用程序在內(nèi)存中緩存 java 對(duì)象,則應(yīng)確保此緩存并沒有不斷增大。對(duì)
          緩存中的對(duì)象數(shù)應(yīng)有一個(gè)限值。我們可以嘗試減少此限值,來觀察其是否降低 java 堆使用量。 Java 軟引用也可用于數(shù)據(jù)緩存,
          當(dāng) JVM 用完 java 堆時(shí),可以保證刪除軟可及對(duì)象。

          長期活動(dòng)對(duì)象 - 如果應(yīng)用程序中有長期活動(dòng)對(duì)象,則可以嘗試盡可能減少這些對(duì)象的存在期。例如,調(diào)整 HTTP 會(huì)話超時(shí)值將
          有助于更快地回收空閑會(huì)話對(duì)象。
          內(nèi)存泄漏 - 內(nèi)存泄漏的一個(gè)例子是在應(yīng)用服務(wù)器中使用數(shù)據(jù)庫連接池。當(dāng)使用連接池時(shí),必須在 finally 塊中顯式關(guān)閉 JDBC
          語句和結(jié)果集對(duì)象。這是因?yàn)?,?dāng)從池中調(diào)用連接對(duì)象上的 close() 時(shí),只是簡單地把連接返回池中以供重用,并沒有實(shí)際關(guān)閉
          連接和關(guān)聯(lián)的語句/結(jié)果集對(duì)象。

          增加 java 堆 - 如果可能的話,我們也可嘗試增加 java 堆,以觀察是否能解決問題。

          如果上述建議都不適用于該應(yīng)用程序,那么,我們需要使用一個(gè)基于 JVMPI(JVM 事件探查器接口)的事件探查器(如 Jprobe 或
          OptimizeIt)來找出哪些對(duì)象正在占用 java 堆。事件探查器還提供 java 代碼中正在創(chuàng)建這些對(duì)象的位置的詳細(xì)信息。本文檔并
          不介紹每個(gè)事件探查器的詳細(xì)信息。可以參考事件探查器文檔來了解如何用事件探查器設(shè)置和啟動(dòng)應(yīng)用程序。一般而言,基于 JVMPI
           的事件探查器需要較高的系統(tǒng)開銷,并會(huì)大大降低應(yīng)用程序的性能。因此,在生產(chǎn)環(huán)境中使用這些事件探查器并不可取。
          http://www.borland.com/optimizeit
          http://www.quest.com/jprobe

          對(duì)于本地 OOM 問題:
          收集下列信息: .verbosegc 輸出,通過它可監(jiān)視 java 堆使用量。這樣將有助于了解此應(yīng)用程序的 java 內(nèi)存要求。
          應(yīng)當(dāng)注意,指定的最大堆內(nèi)存量(在 java 命令行中使用 Xmx 標(biāo)志)與應(yīng)用程序的實(shí)際 java 堆使用量無關(guān),其在 JVM 啟動(dòng)時(shí)被保留,
          并且此保留內(nèi)存不能用于其它任何用途。

          在使用 Jrockit 時(shí),使用 -verbose 來代替 -verbosegc,因?yàn)檫@可以提供 codegen 信息以及 GC 信息。
          定期記錄進(jìn)程虛擬內(nèi)存大小,從啟動(dòng)應(yīng)用程序時(shí)起直到 JVM 用完本地內(nèi)存。這樣將有助于了解此進(jìn)程是否確實(shí)達(dá)到該操作系統(tǒng)的大小限
          值。在 Windows 環(huán)境下,使用下列步驟來監(jiān)視虛擬進(jìn)程大小:
          在“開始” -> “運(yùn)行”對(duì)話框中,輸入“perfmon”并單擊“確定”。
          在彈出的“性能”窗口中,單擊“+”按鈕(圖表上部)。
          在顯示的對(duì)話框中選擇下列選項(xiàng):
          性能對(duì)象:進(jìn)程(不是缺省的處理器)
          從列表中選擇計(jì)數(shù)器:虛擬字節(jié)數(shù)
          從列表中選擇實(shí)例:選擇 JVM (java) 實(shí)例
          單擊“添加”,然后單擊“關(guān)閉”
          在 Unix 或 Linux 環(huán)境下,對(duì)于一個(gè)給定 PID,可以使用以下命令來查找虛擬內(nèi)存大小 - ps -p <PID> -o vsz。

          在 Linux 環(huán)境下,單個(gè) JVM 實(shí)例內(nèi)的每個(gè) java 線程都顯示為一個(gè)獨(dú)立的進(jìn)程。如果我們獲得根 java 進(jìn)程的 PID,那么這就足夠了。
          可以使用 ps 命令的 .forest 選項(xiàng)來找到根 java 進(jìn)程。例如,ps lU <user> --forest 將提供一個(gè)由指定用戶啟動(dòng)的所有進(jìn)程的 ASCII
          樹圖。您可以從該樹圖中找到根 java。

          計(jì)算機(jī)中的內(nèi)存可用性
          如果計(jì)算機(jī)沒有足夠的 RAM 和交換空間,則操作系統(tǒng)將不能為此進(jìn)程提供更多內(nèi)存,這樣也會(huì)導(dǎo)致內(nèi)存不足。請(qǐng)確保 RAM 與磁盤中的交換
          空間之和足以滿足該計(jì)算機(jī)中正在運(yùn)行的所有進(jìn)程的需要。

          調(diào)整 java 堆
          如果 java 堆使用量完全在最大堆范圍內(nèi),則減小 java 最大堆將為 JVM 提供更多的本地內(nèi)存。這不是一個(gè)解決辦法,而是一個(gè)可嘗試的變
          通方法。由于操作系統(tǒng)限制進(jìn)程大小,我們需要在 java 堆和本地堆之間尋求一個(gè)平衡。
          JVM 的本地內(nèi)存使用量
          在加載了所有類并調(diào)用了方法(代碼生成結(jié)束)后,JVM 的本地內(nèi)存用量預(yù)計(jì)將會(huì)幾乎達(dá)到穩(wěn)定。對(duì)于大多數(shù)應(yīng)用程序而言,這通常發(fā)生在
          最初幾小時(shí)內(nèi)。此后,JVM 可能會(huì)因加載運(yùn)行時(shí)類型、生成優(yōu)化代碼等處理而僅使用少量本地內(nèi)存。

          為了縮小問題的范圍,可嘗試禁用運(yùn)行時(shí)優(yōu)化,并檢查這是否會(huì)產(chǎn)生任何效果。

          在使用 Jrockit 時(shí),可使用 -Xnoopt 標(biāo)志來禁用運(yùn)行時(shí)優(yōu)化。

          在使用 SUN hotspot JVM 時(shí),-Xint 標(biāo)志將強(qiáng)迫 JVM 在解釋模式中運(yùn)行(不生成代碼)。
          如果在整個(gè)運(yùn)行過程中,本地內(nèi)存使用量繼續(xù)不斷增加,那么這可能是本地代碼中的內(nèi)存泄漏。

          第三方本地模塊或應(yīng)用程序中的 JNI 代碼
          檢查您是否在使用類似數(shù)據(jù)庫驅(qū)動(dòng)程序的任何第三方本地模塊。這些本地模塊也可以分配本地內(nèi)存,泄漏可能從這些模塊中發(fā)生。為了縮
          小問題的范圍,應(yīng)嘗試在沒有這些第三方模塊的情況下重現(xiàn)問題。例如,可以使用純 java 驅(qū)動(dòng)程序來代替本地?cái)?shù)據(jù)庫驅(qū)動(dòng)程序。

          檢查應(yīng)用程序是否使用一些 JNI 代碼。這也可能造成本地內(nèi)存泄漏,如果可能的話,您可以嘗試在沒有 JNI 代碼的情況下運(yùn)行應(yīng)用程序。

          如果在執(zhí)行上述步驟后還不能找到本地內(nèi)存泄漏的根源,那么您需要與 JVM 供應(yīng)商合作來獲得一個(gè)特殊的編譯版本,它可以跟蹤本地內(nèi)存
          分配調(diào)用,并可提供有關(guān)泄漏的更多信息。

          Jrockit 特定特性
          Jrockit 81SP1 和更高版本支持 JRA 記錄(Java 運(yùn)行時(shí)間分析器)。這對(duì)于收集 JVM 運(yùn)行時(shí)的信息很有用,將提供應(yīng)用程序的有關(guān)信息,
          例如,正在運(yùn)行的 GC 數(shù)、軟/弱/虛引用的數(shù)目、熱方法,等等。如果 JVM 出現(xiàn)性能問題或掛起問題,那么用幾分鐘進(jìn)行記錄和分析數(shù)據(jù)
          就會(huì)很有用。有關(guān)詳細(xì)信息,可以在 Jrockit 文檔中找到。
          http://e-docs.bea.com/wljrockit/docs142/userguide/jra.html


          如果您已經(jīng)理解這個(gè)模式,但仍需要其它幫助,您可以:
          http://support.bea.com 上查詢 AskBEA(例如使用“out of memory”),以查找其它已發(fā)布的解決方案。
          http://support.bea.com 上,向 BEA 的某個(gè)新聞組中提出更詳細(xì)具體的問題。
          如果這還不能解決您的問題,并且您擁有有效的技術(shù)支持合同,您可以通過登錄以下網(wǎng)站來打開支持案例:http://support.bea.com。
           

          posted on 2005-12-21 21:48 java小記 閱讀(410) 評(píng)論(0)  編輯  收藏

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           

          <2005年12月>
          27282930123
          45678910
          11121314151617
          18192021222324
          25262728293031
          1234567

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 临海市| 益阳市| 利川市| 汝南县| 醴陵市| 延寿县| 麻阳| 沈丘县| 特克斯县| 中卫市| 安远县| 泽库县| 辉南县| 靖安县| 凭祥市| 墨江| 靖宇县| 永康市| 京山县| 云安县| 渑池县| 贵阳市| 河源市| 崇州市| 华宁县| 平南县| 慈利县| 射阳县| 漾濞| 临沧市| 玉山县| 闽侯县| 嘉善县| 囊谦县| 浙江省| 鄂温| 广德县| 庄浪县| 综艺| 南昌市| 灵山县|