<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    CONAN ZONE

    你越掙扎我就越興奮

    BlogJava 首頁 新隨筆 聯系 聚合 管理
      0 Posts :: 282 Stories :: 0 Comments :: 0 Trackbacks
    轉自:http://blog.bluedavy.com/?p=91

    Java的自動內存管理機制給開發人員帶來了很多的便利,在設計、開發時可以完全不用考慮要 分配多少內存,要記得回收內存等,但同時也帶來了各種各樣的問題,其中最典型的問題就是OOM,大部分Java開發人員估計都看到過 java.lang.OutOfMemoryError這樣的錯誤信息,在這篇文章中,就來介紹下Sun JDK中有哪幾種OOM、OOM示例、造成OOM的原因的查找、解決以及Sun JDK代碼中處理OOM的方式。

    PDF版本請從此下載:http://blog.bluedavy.com/open/Sun-JDK-OOM.pdf

    1 OOM的種類
    在Sun JDK中運行時,Java程序有可能出現如下幾種OOM錯誤:
    ? java.lang.OutOfMemoryError: unable to create new native thread
    當調用new Thread時,如已創建不了線程了,則會拋出此錯誤,如果是JDK內部必須創建成功的線程,那么會造成Java進程退出,如果是用戶線程,則僅拋出 OOM,創建不了的原因通常是創建了太多線程,耗盡了內存,通常可通過減少創建的線程數,或通過-Xss調小線程所占用的棧大小來減少對Java 對外內存的消耗。

    ? java.lang.OutOfMemoryError: request bytes for . Out of swap space?
    當JNI模塊或JVM內部進行malloc操作(例如GC時做mark)時,需要消耗堆外的內存,如此時Java進程所占用的地址空間超過限制(例如 windows: 2G,linux: 3G),或物理內存、swap區均使用完畢,那么則會出現此錯誤,當出現此錯誤時,Java進程將會退出。

    ? java.lang.OutOfMemoryError: Java heap space
    這是最常見的OOM錯誤,當通過new創建對象或數組時,如Java Heap空間不足(新生代不足,觸發minor GC,還是不夠,觸發Full GC,還是不夠),則拋出此錯誤。

    ? java.lang.OutOfMemoryError: GC overhead limit execeeded
    當通過new創建對象或數組時,如Java Heap空間不足,且GC所使用的時間占了程序總時間的98%,且Heap剩余空間小于2%,則拋出此錯誤,以避免Full GC一直執行,可通過UseGCOverheadLimit來決定是否開啟這種策略,可通過GCTimeLimit和GCHeapFreeLimit來控 制百分比。

    ? java.lang.OutOfMemoryError: PermGen space
    當加載class時,在進行了Full GC后如PermGen空間仍然不足,則拋出此錯誤。
    對于以上幾種OOM錯誤,其中容易造成嚴重后果的是Out of swap space這種,因為這種會造成Java進程退出,而其他幾種只要不是在main線程拋出的,就不會造成Java進程退出。

    2 OOM示例、原因查找和解決
    這些示例的class以及源碼請從http://blog.bluedavy.com/jvm/cases/oom/OOMCases.zip下載,建議在運行前不要看源碼,畢竟源碼是簡單的例子,如果直接看源碼的話,可能會少了查找原因的樂趣。

    當Java程序運行時,會有很多種造成OOM的現象,這里面有些會比較容易查找出原因,而有些會非常困難,下面是來看一些OOM的Example。
    ? Example 1
    以-Xms20m -Xmx20m -Xmn10m -XX:+UseParallelGC參數運行com.bluedavy.oom.JavaHeapSpaceCase1
    運行后在輸出的日志中可看到大量的Full GC信息,以及:
    java.lang.OutOfMemoryError: GC overhead limit exceeded
    和java.lang.OutOfMemoryError: Java heap space
    根據上面所說的OOM種類,可以知道這是在new對象或數組時Java Heap Space不足造成的,對于這種OOM,需要知道的是程序中哪些部分占用了Java Heap。
    要知道程序中哪些部分占用了Java Heap,首先必須拿到Java Heap中的信息,尤其是OOM時的內存信息,在Sun JDK中可通過在啟動參數上加上-XX:+ HeapDumpOnOutOfMemoryError來獲得OOM時的Java Heap中的信息,當出現OOM時,會自動生成一個java_pid[pid].hprof的文件。
    于是在啟動參數上加上上面的參數,再次運行JavaHeapSpaceCase1,可看到在當前運行的路徑下生成了一個 java_pid10852.hprof(由于pid不同,你看到的可能是不一樣的文件名)的文件,在拿到這個文件后,就可通過 mat(http://www.eclipse.org/mat/)來進行分析了。
    用mat打開上面的文件(默認情況下mat認為heap dump文件應以.bin結尾,因此請改名或以open file方式打開),打開后點擊dominator_tree,可看到sun.misc.Launcher$AppClassLoader占據了大部分的 內存,繼續點開看,則可看到是由于com.bluedavy.oom.Caches中有一個ArrayList,其中存放的對象占據了大部分的內存,因此 解決這個OOM的辦法是,讓Caches類中放的對象總大小是有限制的,或者限制放入Caches的ArrayList中的對象個數。
    這種情況造成的OOM,在實際的場景中當使用緩存時很容易產生,對于所有的緩存、集合大小都應給定限制的最大大小,以避免出現緩存或集合太大,導致消耗了過多的內存,從而導致OOM。

    ? Example 2
    以-Xms20m -Xmx20m -Xmn10m -XX:+HeapDumpOnOutOfMemoryError執行com.bluedavy.oom.JavaHeapSpaceCase2
    運行后在輸出的日志中可看到大量的Full GC和java.lang.OutOfMemoryError: Java heap space。
    同樣,首先用mat打開需要分析的hprof文件,很驚訝的發現什么都看不出來,Java Heap Space還很充足,這就奇怪了,還好除了能在OOM時自動dump出Heap的信息外,還可通過jmap命令手工dump,于是,在運行期出現頻繁 Full GC、OOM的時候,手工通過jmap –dump:file=heap.bin,format=b [pid]生成一個heap.bin文件,把這個heap.bin文件也拿到mat中分析,杯具,還是什么都看不出來,還好,還有一招,就是直接用 jmap –histo看看到底什么對象占用了大多數的內存,執行一次,看到[I占用了最多的內存,沒用,因為沒法知道這個[I到底是代碼中哪個部分創建的,不甘 心,多執行幾次,很幸運,突然看到com.bluedavy.oom.Case2Object占據了最大的內存,于是查找代碼中哪些地方創建了這個對象, 發現了代碼中有一個線程創建了大量的Case2Object,修改即可。
    從這個例子中,可以看到,在分析OOM問題時,一方面是依賴OOM時dump出來的文件,但這個文件其實只會在Java進程中第一次出現OOM時生成,之 后再OOM就不會生成了,這有可能出現真實的OOM的原因被假的OOM原因給掩蓋掉;另一方面是依賴在出現OOM時的人工操作,這種人肉方式其實比較杯 具,因為只能先等到頻繁Full GC、OOM,首先通過jmap –histo來看看到底什么對象占用了大部分的內存(需要多執行幾次,以確保正確性),上面的例子比較幸運,因為剛好是自定義的對象,如果是原生的類型, 那就只能借助dump文件來分析了,通過jmap –dump手工dump出Heap文件,然后用MAT分析,但有可能會出現上面例子中的狀況,就是mat分析上也看不出什么,頂多只能看到 unreachable objects里某些對象占用了大部分的內存,而通常情況看到的可能都是原生類型,一旦真的碰到jmap –histo看到的是原生類型占用較多,jmap dump看到的是Java Heap Space也不滿的話,那只能說杯具了,在這種情況下,唯一能做的是捕捉所有的異常,然后打印,從而判斷OOM是在哪行代碼拋出的。

    ? Example 3
    以-Xms20m -Xmx20m -Xmn10m -XX:+HeapDumpOnOutOfMemoryError執行com.bluedavy.oom.JavaHeapSpaceCase3
    在控制臺中可看到大量的java.lang.OutOfMemoryError: Java heap space,把生成的hprof文件放入MAT中進行分析,還好看到確實是Java Heap Space滿了,這就好辦了,點開Dominator Tree視圖,可看到有一堆的線程,每個都占用了一定的內存,從而導致了OOM,要解決這個例子中的OOM,有四種辦法:一是減少處理的線程數;二是處理 線程需要消耗的內存;三是提升線程的處理速度;四是增加機器,減少單臺機器所需承擔的請求量。
    上面這種狀況在系統處理慢的時候比較容易出現。

    ? Example 4
    以-Xms20m -Xmx20m -Xmn10m -XX:+HeapDumpOnOutOfMemoryError執行com.bluedavy.oom.JavaHeapSpaceCase4
    在控制臺可看到大量的java.lang.OutOfMemoryError: Java heap space,把生成的hprof文件放入MAT中進行分析,可看到TaskExecutor中的handlers占據了大量的內存,分析代碼,發現是由于 在task處理完后沒有清除掉對應的handler造成的,修改即可解決此OOM問題。
    這是個典型的內存泄露的例子,如果不小心持有了本應釋放引用的對象,那么就會造成內存泄露,這是在編寫Java程序時需要特別注意的地方。

    ? Example 5
    以-Xms1536m -Xmx1536m -Xss100m執行com.bluedavy.oom.CannotCreateThreadCase
    在控制臺可看到java.lang.OutOfMemoryError: unable to create new native thread。
    對于這種情況,一需要查看目前-Xss的大小,例如在這個例子中-Xss太大,導致連20個線程都無法創建,因此可解決的方法是降低-Xss的大小;如果 Xss使用的是默認值,那么可通過jstack來查看下目前Java進程中是不是創建了過多的線程,或者是java heap太大,導致os沒剩多少內存,從而創建不出線程。

    ? Example 6
    Out of swap的例子實在太難舉了,就沒在此列出了,對于Out of swap的OOM,需要首先觀察是否因為Java Heap設置太大,導致物理內存+swap區不夠用;如果不是的話,則需關注到底是哪些地方占用了堆外的內存,可通過google-perftools來 跟蹤是哪些代碼在調用malloc,以及所耗的內存比例,在跟蹤到后可繼續查找原因。
    總體來說,Out of swap通常是最難查的OOM,由于其是直接退出java進程的,因此需要結合core dump文件和hs_err_pid[pid].log進行分析,最關鍵的還是像查java heap那樣,要查出到底是什么代碼造成了native heap的消耗。

    ? Example 7
    PermGen空間滿造成OOM的情況通常采取的解決方法是簡單的擴大PermSize。

    總結上面的例子來看,對于OOM的情況,最重要的是根據OOM的種類查找到底是代碼中的什么部分造成的消耗。

    對于Java Heap Space OOM和GC overhead limit exceeded這兩種類型,可通過heap dump文件以及jmap –histo來進行分析,多數情況下可通過heap dump分析出原因,但也有少數情況會出現heap dump分析不出原因,而jmap –histo看到某原生類型占據了大部分的內存,這種情況就非常復雜了,只能是仔細查看代碼,并捕捉OutOfMemoryError,從而來逐漸定位到 代碼中哪部分拋出的。

    對于Out of swap這種類型,其主要是地址空間超過了限制或者對外內存不夠用了造成的,首先需要查看Java Heap設置是否過大,然后可結合google-perftools來查看到底是哪些代碼調用了malloc,在堆外分配內存。

    3 Sun JDK代碼中處理OOM的方式
    在Sun JDK代碼中,在不同的OOM時,會調用不同的處理方式來進行處理,下面就來看看JDK中幾個典型的處理OOM的代碼。
    ? 創建線程失敗
    compiler_thread = new CompilerThread(queue, counters);
    if (compiler_thread == NULL || compiler_thread->osthread() == NULL){
    vm_exit_during_initialization(“java.lang.OutOfMemoryError”,
    “unable to create new native thread”);
    }
    對于JDK中必須創建成功的線程,如失敗會通過調用vm_exit_during_initialization打印出OOM錯誤,并退出Java進程。
    對于非必須創建成功的線程,通常會調用
    THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
    “unable to create new native thread”);
    拋出OOM錯誤信息。

    ? 調用os:malloc失敗
    void *p = os::malloc(bytes);
    if (p == NULL)
    vm_exit_out_of_memory(bytes, “Chunk::new”);
    return p;
    當os:malloc或os:commit_memory失敗時,會直接輸出錯誤信息,并退出Java進程。

    ? Java Heap上分配失敗后
    report_java_out_of_memory(“Java heap space”);
    調用這個就表明了不會退出vm,而只是拋出OOM錯誤。
    例如PermGen分配失敗時的代碼更為直觀:
    HeapWord* result = Universe::heap()->permanent_mem_allocate(size);
    if (result != NULL) {
    NOT_PRODUCT(Universe::heap()->
    check_for_non_bad_heap_word_value(result, size));
    assert(!HAS_PENDING_EXCEPTION,
    “Unexpected exception, will result in uninitialized storage”);
    return result;
    }
    // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support
    report_java_out_of_memory(“PermGen space”);

    總體而言,對于一個大型系統而言,通常OOM是難以避免的現象,最重要的還是一旦出現OOM,要掌握排查的方法,另外就是,隨著現在內存越來越便 宜,CMS GC越來越成熟,采用64 bit操作系統,開啟大內存也是一種可選方式,基本上可以避免內存成為大問題,畢竟在Java中完全可能隨便寫幾行代碼就不知不覺消耗了很多內存。

    ps: 感興趣的同學還可參考sun官方的這篇關于OOM的文章:
    http://java.sun.com/javase/6/webnotes/trouble/TSG-VM/html/memleaks.html



    posted on 2010-08-16 18:21 CONAN 閱讀(628) 評論(0)  編輯  收藏 所屬分類: JVM
    主站蜘蛛池模板: 中文字幕亚洲一区| 女人与禽交视频免费看| 国产一级大片免费看| 亚洲视频在线观看2018| 日韩精品免费一级视频| 亚洲久本草在线中文字幕| 一级毛片不卡片免费观看| 亚洲av日韩av不卡在线观看| 免费精品99久久国产综合精品| 久久国产成人精品国产成人亚洲 | 免费国产成人高清在线观看麻豆| 性色av极品无码专区亚洲| 国产精品视_精品国产免费| 亚洲av日韩综合一区二区三区| 日本一道在线日本一道高清不卡免费| 亚洲色大网站WWW永久网站| 在线中文高清资源免费观看| 亚洲av无码专区在线电影天堂 | 免费h成人黄漫画嘿咻破解版| 老湿机一区午夜精品免费福利| 四虎影视精品永久免费| 一级毛片免费播放男男| 亚洲国产另类久久久精品黑人 | 日本亚洲免费无线码| 亚洲乱码国产乱码精华| 国产自产拍精品视频免费看| 香港特级三A毛片免费观看| 青青草原亚洲视频| 久久国产色AV免费看| 亚洲av无码国产综合专区 | 不卡精品国产_亚洲人成在线| 黄色片免费在线观看| 亚洲一区在线视频| 四虎AV永久在线精品免费观看| 中文字幕在线免费播放| 亚洲无线一二三四区| 在线日韩av永久免费观看| 精品一区二区三区免费观看| 亚洲六月丁香六月婷婷蜜芽| 青青青国产色视频在线观看国产亚洲欧洲国产综合| 一级视频在线免费观看|