Java Troubleshooting 整理

Troubleshooting 整理

  • kill -3 pid
    • 發(fā)送一個(gè)SIGQUIT信號(hào)給Java應(yīng)用, 通常會(huì)有當(dāng)前的Thread Dump輸出
    • 假定這個(gè)程序在JVM初始化之后沒有別的代碼注冊(cè)了新的SIGQUIT的signal handler,那么HotSpot VM在收到SIGQUIT之后會(huì)在一個(gè)專門的signal handler thread處理。該線程的入口函數(shù)為signal_thread_entry()
    • http://hg.openjdk.java.net/jdk7u/jdk7u/hotspot/file/tip/src/share/vm/runtime/os.cpp
    • 打印線程棧的動(dòng)作由VM_PrintThreads實(shí)現(xiàn),在VM thread上執(zhí)行。于是問題就來了:如果這個(gè)JVM實(shí)例已經(jīng)hang了,那它將無法響應(yīng)任何外部請(qǐng)求,對(duì)它發(fā)SIGQUIT當(dāng)然也得不到處理。所以有時(shí)候kill -3看不到線程棧是正常的。
    • 至于時(shí)效性,所有VM operation都是先被放到一個(gè)隊(duì)列里,然后由VM thread逐個(gè)處理。如果當(dāng)前該隊(duì)列是空的,那kill -3就可以幾乎“實(shí)時(shí)”執(zhí)行爬棧動(dòng)作,否則得等前面的VM operation先完成,那就會(huì)延遲一會(huì)兒。

  • Linux core dump
    • concept
      • A core dump is the recorded state of the working memory of a computer program at a specific time, generally when the program has terminated abnormally (crashed). In practice, other key pieces of program state are usually dumped at the same time, including the processor registers, which may include the program counter and stack pointer, memory management information, and other processor and operating system flags and information. The name comes from the once-standard memory technology core memory. Core dumps are often used to diagnose or debug errors in computer programs.
      • On many operating systems, a fatal error in a program automatically triggers a core dump, and by extension the phrase "to dump core" has come to mean, in many cases, any fatal error, regardless of whether a record of the program memory is created.
    • 檢查是否可以core dump
      • ulimit -a (or unlimit -c只查看core file size)
        core file size (blocks, -c) 0
        ...
      • 修改限制
        • 臨時(shí) 可以使用參數(shù)unlimited,取消該限制ulimit -c unlimited
        • 永久 echo "ulimit -c 1024" >> /etc/profile (1024 限制產(chǎn)生的 core 文件的大小不能超過 1024kb)
    • 在一個(gè)程序崩潰時(shí),它一般會(huì)在指定目錄下生成一個(gè) core 文件。 core 文件僅僅是一個(gè)內(nèi)存映象 ( 同時(shí)加上調(diào)試信息 ) ,主要是用來調(diào)試的。
    • 設(shè)置 Core Dump 的核心轉(zhuǎn)儲(chǔ)文件目錄和命名規(guī)則
      • /proc/sys/kernel/core_uses_pid 可以控制產(chǎn)生的 core 文件的文件名中是否添加 pid 作為擴(kuò)展 ,如果添加則文件內(nèi)容為 1 ,否則為 0
      • /proc/sys/kernel/core_pattern 可以設(shè)置格式化的 core 文件保存位置或文件名 ,比如原來文件內(nèi)容是 core-%e 可以這樣修改 : echo "/corefile/core-%e-%p-%t" > core_pattern; 將會(huì)控制所產(chǎn)生的 core 文件會(huì)存放到 /corefile 目錄下,產(chǎn)生的文件名為 core- 命令名 -pid- 時(shí)間戳
    • kill -l 查看信號(hào)
      • 看到SIGSEGV在其中,一般數(shù)組越界或是訪問空指針都會(huì)產(chǎn)生這個(gè)信號(hào)
      • 指示進(jìn)程進(jìn)行了一次無效的存儲(chǔ)訪問。名字SEGV表示“段違例(segmentation violation)
    • gdb core調(diào)試
    • HSDB R大-HSDB

  • JVM調(diào)優(yōu)-標(biāo)準(zhǔn)參數(shù)-的一些陷阱
    • 各參數(shù)的默認(rèn)值
      • 1.參考HotSpot VM里的各個(gè)globals.hpp文件 本帖子列出一些
      • 2.-XX:+PrintCommandLineFlags
        • 顯示出VM初始化完畢后所有跟最初的默認(rèn)值不同的參數(shù)及它們的值
      • 3.-XX:+PrintFlagsFinal (這個(gè)參數(shù)本身只從JDK 6 update 21開始才可以用)
        • 前一個(gè)參數(shù)只顯示跟默認(rèn)值不同的,而這個(gè)參數(shù)則可以顯示所有可設(shè)置的參數(shù)及它們的值
        • 可以設(shè)置的參數(shù)默認(rèn)是不包括diagnostic或experimental系的。要在-XX:+PrintFlagsFinal的輸出里看到這兩種參數(shù)的信息,分別需要顯式指定-XX:+UnlockDiagnosticVMOptions / -XX:+UnlockExperimentalVMOptions
      • 4,-XX:+PrintFlagsInitial
        • 這個(gè)參數(shù)顯示在處理參數(shù)之前所有可設(shè)置的參數(shù)及它們的值,然后直接退出程序?!皡?shù)處理”包括許多步驟,例如說檢查參數(shù)之間是否有沖突,通過ergonomics調(diào)整某些參數(shù)的值,之類的
        • 結(jié)合-XX:+PrintFlagsInitial與-XX:+PrintFlagsFinal,對(duì)比兩者的差異,就可以知道ergonomics對(duì)哪些參數(shù)做了怎樣的調(diào)整
        • $ java -XX:+PrintFlagsInitial | grep UseCompressedOops
          bool UseCompressedOops = false {lp64_product}
          $ java -XX:+PrintFlagsFinal | grep UseCompressedOops
          bool UseCompressedOops = true {lp64_product}
          Oracle JDK從6 update 23開始在64位系統(tǒng)上會(huì)默認(rèn)開啟壓縮指針
      • 5.jinfo -flag 可以用來查看某個(gè)參數(shù)的值,也可以用來設(shè)定manageable系參數(shù)的值
      • PrintFlagsInitial->ergonomics->命令指定->PrintFlagsFinal->jinfo

  • 陷阱1
    • -XX:+DisableExplicitGC 與 NIO的direct memory
      • System.gc()的默認(rèn)效果是引發(fā)一次stop-the-world的full GC,對(duì)整個(gè)GC堆做收集。有幾個(gè)參數(shù)可以改變默認(rèn)行為
      • 關(guān)鍵點(diǎn)是,用了-XX:+DisableExplicitGC參數(shù)后,System.gc()的調(diào)用就會(huì)變成一個(gè)空調(diào)用,完全不會(huì)觸發(fā)任何GC,可以看看native源碼, Runtime.c里面;
      • 為什么要關(guān)閉顯示調(diào)用呢?
        • 避免濫用System.gc()
        • 防止一些第三方庫濫調(diào)用System.gc()
      • NIO的DirectByteBuffer里面有用到System.gc(),用來觸發(fā)回收DirectByteBuffer對(duì)象,從而間接回收堆外內(nèi)存(reference handler里調(diào)用了Cleaner.cleanup() ),如果System.gc()被關(guān)閉了,導(dǎo)致DirectByteBuffer對(duì)象回收不了,最后直接內(nèi)存也回收不了; java.lang.OutOfMemoryError: Direct buffer memory
      • public class DisableExplicitGCDemo { public static void main(String[] args) { for (int i = 0; i < 100000; i++) { ByteBuffer.allocateDirect(128); } System.out.println("Done"); } }
      • java -XX:MaxDirectMemorySize=10m -XX:+PrintGC -XX:+DisableExplicitGC DisableExplicitGCDemo
        Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
    • DirectByteBuffer有幾處值得注意的地方
      • DirectByteBuffer沒有finalizer,它的native memory的清理工作是通過sun.misc.Cleaner自動(dòng)完成的
      • sun.misc.Cleaner是一種基于PhantomReference的清理工具,比普通的finalizer輕量些
      • A cleaner tracks a referent object and encapsulates a thunk of arbitrary cleanup code. Some time after the GC detects that a cleaner's referent has become phantom-reachable, the reference-handler thread will run the cleaner
      • Oracle/Sun JDK 6中的HotSpot VM只會(huì)在old gen GC(full GC/major GC或者concurrent GC都算)的時(shí)候才會(huì)對(duì)old gen中的對(duì)象做reference processing,而在young GC/minor GC時(shí)只會(huì)對(duì)young gen里的對(duì)象做reference processing; 死在young gen中的DirectByteBuffer對(duì)象會(huì)在young GC時(shí)被處理; 也就是說,做full GC的話會(huì)對(duì)old gen做reference processing,進(jìn)而能觸發(fā)Cleaner對(duì)已死的DirectByteBuffer對(duì)象做清理工作。而如果很長一段時(shí)間里沒做過GC或者只做了young GC的話則不會(huì)在old gen觸發(fā)Cleaner的工作,那么就可能讓本來已經(jīng)死了的、但已經(jīng)晉升到old gen的DirectByteBuffer關(guān)聯(lián)的native memory得不到及時(shí)釋放
      • 為DirectByteBuffer分配空間過程中會(huì)顯式調(diào)用System.gc(),以期通過full GC來強(qiáng)迫已經(jīng)無用的DirectByteBuffer對(duì)象釋放掉它們關(guān)聯(lián)的native memory
      • 這幾個(gè)實(shí)現(xiàn)特征使得Oracle/Sun JDK 6依賴于System.gc()觸發(fā)GC來保證DirectByteMemory的清理工作能及時(shí)完成。如果打開了-XX:+DisableExplicitGC,清理工作就可能得不到及時(shí)完成,于是就有機(jī)會(huì)見到direct memory的OOM
      • 教訓(xùn)是:如果你在使用Oracle/Sun JDK 6,應(yīng)用里有任何地方用了direct memory,那么使用-XX:+DisableExplicitGC要小心。如果用了該參數(shù)而且遇到direct memory的OOM,可以嘗試去掉該參數(shù)看是否能避開這種OOM。如果擔(dān)心System.gc()調(diào)用造成full GC頻繁,可以嘗試下面提到 -XX:+ExplicitGCInvokesConcurrent 參數(shù) 結(jié)合笨神的堆外內(nèi)存完全解讀

  • 陷阱2
    • -XX:+DisableExplicitGC 與 Remote Method Invocation (RMI) 與 -Dsun.rmi.dgc.{server|client}.gcInterval=
    • 實(shí)際上這里在做的是分布式GC。Sun JDK的分布式GC是用純Java實(shí)現(xiàn)的,為RMI服務(wù)
    • 前面gcCauses整理有整理這個(gè)dgc

  • 陷阱3
    • -XX:+ExplicitGCInvokesConcurrent 或 -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses
    • product(bool, ExplicitGCInvokesConcurrent, false, "A System.gc() request invokes a concurrent collection;" " (effective only when UseConcMarkSweepGC)")
    • product(bool, ExplicitGCInvokesConcurrentAndUnloadsClasses, false, "A System.gc() request invokes a concurrent collection and also unloads classes during such a concurrent gc cycle " "(effective only when UseConcMarkSweepGC)")
    • 跟上面的第一個(gè)例子的-XX:+DisableExplicitGC一樣,這兩個(gè)參數(shù)也是用來改變System.gc()的默認(rèn)行為用的;不同的是這兩個(gè)參數(shù)只能配合CMS使用(-XX:+UseConcMarkSweepGC),而且System.gc()還是會(huì)觸發(fā)GC的,只不過不是觸發(fā)一個(gè)完全stop-the-world的full GC,而是一次并發(fā)GC周期
    • CMS GC周期中也會(huì)做reference processing。所以如果用這兩個(gè)參數(shù)的其中一個(gè),而不是用-XX:+DisableExplicitGC的話,就避開了由full GC帶來的長GC pause,同時(shí)NIO direct memory的OOM也不會(huì)那么容易發(fā)生

  • 陷阱4
    • -XX:+GCLockerInvokesConcurrent
    • product(bool, GCLockerInvokesConcurrent, false, "The exit of a JNI CS necessitating a scavenge also kicks off a bkgrd concurrent collection")
    • jni critical release時(shí), 觸發(fā)gc; gcCauses整理有詳細(xì)

  • 陷阱5
    • MaxDirectMemorySize 與 NIO direct memory 的默認(rèn)上限
    • product(intx, MaxDirectMemorySize, -1, "Maximum total size of NIO direct-buffer allocations")
    • 但如果不配置它的話,direct memory默認(rèn)最多能申請(qǐng)多少內(nèi)存呢?這個(gè)參數(shù)默認(rèn)值是-1,顯然不是一個(gè)“有效值”。所以真正的默認(rèn)值肯定是從別的地方來的
    • 當(dāng)MaxDirectMemorySize參數(shù)沒被顯式設(shè)置時(shí)它的值就是-1,在Java類庫初始化時(shí)maxDirectMemory()被java.lang.System的靜態(tài)構(gòu)造器調(diào)用,走的路徑就是這條;
    • 結(jié)論:MaxDirectMemorySize沒顯式配置的時(shí)候,NIO direct memory可申請(qǐng)的空間的上限就是-Xmx減去一個(gè)survivor space的預(yù)留大小
    • 這里建議讀讀源碼看看

  • 陷阱6
    • -verbose:gc 與 -XX:+PrintGCDetails
    • 經(jīng)常能看到在推薦的標(biāo)準(zhǔn)參數(shù)里這兩個(gè)參數(shù)一起出現(xiàn)。實(shí)際上它們有啥關(guān)系?
      在Oracle/Sun JDK 6里,"java"這個(gè)啟動(dòng)程序遇到"-verbosegc"會(huì)將其轉(zhuǎn)換為"-verbose:gc",將啟動(dòng)參數(shù)傳給HotSpot VM后,HotSpot VM遇到"-verbose:gc"則會(huì)當(dāng)作"-XX:+PrintGC"來處理。
      也就是說 -verbosegc、-verbose:gc、-XX:+PrintGC 三者的作用是完全一樣的。
      而當(dāng)HotSpot VM遇到 -XX:+PrintGCDetails 參數(shù)時(shí),會(huì)順帶把 -XX:+PrintGC 給設(shè)置上。
      也就是說 -XX:+PrintGCDetails 包含 -XX:+PrintGC,進(jìn)而也就包含 -verbose:gc。
      既然 -verbose:gc 都被包含了,何必在命令行參數(shù)里顯式設(shè)置它呢?

  • -XX:+UseFastEmptyMethods 與 -XX:+UseFastAccessorMethods
    • 這個(gè)用的少, 只看下結(jié)論就行了
    • 為了適應(yīng)多層編譯模式,JDK 7里這兩個(gè)參數(shù)的默認(rèn)值就被改為false了

  • -XX:+UseCMSCompactAtFullCollection
    • CMSFullGCsBeforeCompaction
    • CMSParallelRemarkEnabled
    • CMSScavengeBeforeRemark
      • Attempt scavenge before the CMS remark step
      • 如果一個(gè)應(yīng)用統(tǒng)計(jì)到的young GC時(shí)間都比較短而CMS remark的時(shí)間比較長,那么可以試試打開這個(gè)參數(shù),在做remark之前先做一次young GC。是否能有效縮短remark的時(shí)間視應(yīng)用情況而異,所以開這個(gè)參數(shù)的話請(qǐng)一定做好測試
  • -Xss 與 -XX:ThreadStackSize
  • -Xmn 與 -XX:NewSize、-XX:MaxNewSize
    • 如果同時(shí)設(shè)置了-XX:NewSize與-XX:MaxNewSize遇到“Could not reserve enough space for object heap”錯(cuò)誤的話,請(qǐng)看看是不是這帖所說的問題。早期JDK 6似乎都受這問題影響,一直到JDK 6 update 14才修復(fù)
  • -Xmn 與 -XX:NewRatio
  • -XX:NewRatio 與 -XX:NewSize、-XX:OldSize
  • jmap -heap看到的參數(shù)值與實(shí)際起作用的參數(shù)的關(guān)系?
    • jmap -heap顯示的部分參數(shù)是以MB為單位來顯示的,而MaxNewSize的單位是byte
    • 要注意的是,HotSpot VM有大量可調(diào)節(jié)的參數(shù),并不是所有參數(shù)在某次運(yùn)行的時(shí)候都有效。
  • -XX:MaxTenuringThreshold 的默認(rèn)值?
    • Oracle/Sun JDK 6中,選擇CMS之外的GC時(shí),MaxTenuringThreshold(以下簡稱MTT)的默認(rèn)值是15;而選擇了CMS的時(shí)候,MTT的默認(rèn)值是4而不是15。設(shè)定是在 Arguments::set_cms_and_parnew_gc_flags() 里做的
    • 在Sun JDK 6之前(1.4.2、5),選擇CMS的時(shí)候MTT的默認(rèn)值則是0,也就是等于設(shè)定了-XX:+AlwaysTenure——所有eden里的活對(duì)象在經(jīng)歷第一次minor GC的時(shí)候就會(huì)直接晉升到old gen,而survivor space直接就沒用了
    • java -XX:+PrintFlagsFinal | egrep 'MaxTenuringThreshold|UseParallelGC|UseConcMarkSweepGC' (15)
    • java -XX:+PrintFlagsFinal -XX:+UseConcMarkSweepGC | egrep 'MaxTenuringThreshold|UseParallelGC|UseConcMarkSweepGC' (本機(jī)是6)
    • 建議這樣查看vm參數(shù)值:java 我們的vm配置參數(shù) -XX:PrintFlagsFinal | grep ''selective parameter (原因在于有的參數(shù)的值,會(huì)受到其他參數(shù)影響導(dǎo)致與自己的默認(rèn)值不一樣!)
  • -XX:+CMSClassUnloadingEnabled
    • CMS remark暫停時(shí)間會(huì)增加,所以如果類加載并不頻繁、String的intern也沒有大量使用的話,這個(gè)參數(shù)還是不開的好
  • -XX:+UseCompressedOops 有益?有害?
    • 我能做的建議是如果在64位Oracle/Sun JDK 6/7上,那個(gè)參數(shù)不要顯式設(shè)置
    • 有些庫比較“聰明”,會(huì)自行讀取VM參數(shù)來調(diào)整自己的一些參數(shù),例如Berkeley DB Java Edition。但這些庫實(shí)現(xiàn)得不好的時(shí)候反而會(huì)帶來一些麻煩:BDB JE要求顯式指定-XX:+UseCompressedOops才能有效的調(diào)整它的緩存大小。所以在用BDB JE并且Java堆+PermGen大小小于32GB的時(shí)候,請(qǐng)顯式指定-XX:+UseCompressedOops吧
  • -XX:+AlwaysPreTouch
    • 會(huì)把commit的空間跑循環(huán)賦值為0以達(dá)到“pretouch”的目的。開這個(gè)參數(shù)會(huì)增加VM初始化時(shí)的開銷,但后面涉及虛擬內(nèi)存的開銷可能降低
  • -XX:+ParallelRefProcEnabled
    • 這個(gè)功能可以加速reference processing,但在JDK6u25和6u26上不要使用,有bug:
      Bug ID 7028845: CMS: 6984287 broke parallel reference processing in CMS
  • -XX:+UseConcMarkSweepGC 與 -XX:+UseAdaptiveSizePolicy
    • 這兩個(gè)選項(xiàng)在現(xiàn)有的Oracle/Sun JDK 6和Oracle JDK 7上都不要搭配在一起使用——CMS用的adaptive size policy還沒實(shí)現(xiàn)完,用的話可能會(huì)crash。
      目前HotSpot VM上只有ParallelScavenge系的GC才可以配合-XX:+UseAdaptiveSizePolicy使用;也就是只有-XX:+UseParallelGC或者-XX:+UseParallelOldGC。Jon Masamitsu在郵件列表上提到過。
      題外話:開著UseAdaptiveSizePolicy的ParallelScavenge會(huì)動(dòng)態(tài)調(diào)整各空間的大小,有可能會(huì)造成兩個(gè)survivor space的大小被調(diào)整得不一樣大。Jon Masamitsu在這封郵件里解釋了原因。
      追加:JDK9里CMS終于要徹底不支持adaptive size policy了:https://bugs.openjdk.java.net/browse/JDK-8034246
  • -XX:HeapDumpPath 與 -XX:+HeapDumpBeforeFullGC、-XX:+HeapDumpAfterFullGC、-XX:+HeapDumpOnOutOfMemoryError
    • 但很多人都會(huì)疑惑:做出來的heap dump存到哪里去了?
      如果不想費(fèi)神去摸索到底各種環(huán)境被配置成什么樣、“working directory”到底在哪里的話,就在VM啟動(dòng)參數(shù)里加上 -XX:HeapDumpPath=一個(gè)絕對(duì)路徑 吧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容