Unknown Source的出現及解決 – JAVA編程語言程序開發技術文章

昨天在公司寫瞭一段代碼,很簡單,就是測試Thread的dumpStack方法的使用。因為Thread的dumpStack方法不是很常用,但它對於如果想看看誰在運行時調用方法還是非常有幫助的。回到正題,看輸出結果:
java.lang.Exception: Stack trace
 at java.lang.Thread.dumpStack(Unknown Source)
 at Common.getInfo(TestDumpStack.java:21)
 at TestDumpStack.main(TestDumpStack.java:7)
    大傢可以看到在輸出的第二行,顯示的是at java.lang.Thread.dumpStack(Unknown Source)。為什麼自己寫的代碼就顯示出瞭源碼文件的名字及所在行數,而jdk的類庫就顯示出瞭Unknown Source?
    相信很多人在調試代碼,用log工具打印堆棧異常信息,查看代碼所在行的相關調試信息時,經常會遇到Unknown Source這個頭痛的問題。那麼這個東西到底如何而來?
    A.Unknown Source從哪來?


     Unknown Source,顧名思義,就是未知的源文件。因為我們最終解釋運行的是class文件,所以出現這個問題的原因很簡單,就是class文件中沒有源文件的相關調試信息。那為什麼class文件會沒有調試信息呢?答案更簡單,當然是我們在用javac命令進行編譯的時候沒有指定調試信息唄。因為現在很多人都習慣用eclipse等一些現成的ide進行編寫代碼,所以很少人熟悉jdk自己的javac,java,jdb等一些命令的詳細參數(jdk的一些命令和eclipse自帶的一些命令可能不同)。哈哈,不過如果你經常在linux下玩java的話,命令肯定會非常熟悉。那麼讓我們看看javac的一些重要參數:
    -g-Generate all debugging information, including local variables. By default, only line number and source file information is generated.在class文件中生成所有調試信息,包括局部變量的信息。默認的話,隻寫入源碼的行號和源文件信息。
    -g:none-Do not generate any debugging information.不生成任何調試信息。
    -g:(lines,vars,source)-隻生成部分調試信息(源碼行號,變量,源文件信息)。那我們在分別介紹下lines,vars,source的含義。
         lines:將源文件中的行號信息寫到Class文件中,此屬性用於在Class文件中生成方法字節碼流偏移量和源代碼行號之間的映射關系。如果我們不指定此屬性的話,我們將在堆棧異常信息中看不到打印的行號。
         vars:Local variable屬性建立瞭方法的棧幀中局部變量部分內容與源代碼中局部變量名稱和描述符之間的映射關系。有瞭這個屬性,調試時,我們才可以看到變量的值。
         source:編譯時指定瞭這個屬性,會把源文件的屬性信息如源文件名稱寫入class文件。
     說瞭這麼多,初學者可能會迷糊,為什麼編譯要指定這些調試信息呢?哈哈,如果編譯不指定這些調試信息的話,你怎麼調試呢?如果你不指定行號信息的話,你在ide中都無法插入斷點。這些調試信息在我們調試程序的時候非常重要。不過這些編譯選項通常在ide中如eclipse中早已默認瞭。有的人可能還不相信,打開eclipse,依次打開菜單選項:Window->Preferences->Java->Compiler,可以看到頁面的下方有一個Classfile Generation,默認是四個選項都選的。
    那這個Unknown Source到底是編譯的時候沒有指定哪一項呢?經過測試,我發現是javac編譯的時候沒有沒有指定source選項,必定出Unknown Source這個問題。
     PS1:linux下,很多人用ant進行javac任務編譯,查看堆棧異常時也經常會遇到Unknown Source的問題。ant編譯時,默認相當於指定-g:none,及不生成任何調試信息的。所以如果要看到日志分析中的源碼和行號信息時,要更改build.xml中的dubug屬性。
    PS2:我覺得看看Log4j的日志操作類源碼包會對這個理解更有幫助。


    B.剛開始的代碼引子中,為什麼自己寫的代碼會有堆棧異常的代碼行數顯示,而jdk的類庫(rt.jar-Runtime Java Archive)代碼會出現Unknown Source?
    答案很簡單,因為我們直接用的是jdk直接編譯好的class文件。而rt.jar源碼編譯打包的時候,是沒有將調試信息放入class文件的。所以才會顯示Unknown Source。其實,道理很簡單,sun的類庫正常的情況下肯定不會有bug的,之前肯定都是調試過很多遍的,所以沒有必要再加入調試信息,你隻負責用就行瞭。所以,出現Unknown Source很正常。
     PS:其實,我覺得這和軟件的開發版本差不多。版本一般都有dubug版本和release版本。debug版本就是包含調試信息的。不過正式發行後,肯定不包含調試信息的。因為如果包含調試信息的話,可能版本占用空間會很大,而且根本就無需調試信息。


    C.如果我們非要對jdk的類庫如rt.jar進行跟蹤調試怎麼辦?
     因為rt.jar編譯打包的時候,是不包含調試信息的。如果你隻是想看看調用的過程,你隻需要在eclipse中rt.jar下的Source attachment指定jdk安裝目錄的src.zip即可。不過如果你想跟蹤jdk類庫的變量值的時候,這樣就不行瞭。除非,隻有一種辦法,你重新編譯一下src.zip,指定好編譯參數,然後用新編譯好的rt.jar覆蓋掉原來的rt.jar。這樣就完全ok瞭。


    D.如果我們想debug其他沒有源代碼的class文件呢?
     其實,也不難,利用jad等反編譯工具編譯出源碼後,在進行調試。不過前提是該class文件有調試信息。


     PS:在網上看到瞭一些打印堆棧異常信息的代碼,發現有的竟然打印出瞭jdk源碼的所在行數。如
at java.lang.Thread.dumpStack(Thread.java:1206)等。我覺得很好奇,原因可能是重新編譯瞭jdk的源碼或者可能用的不同的ide或者不同版本的jdk吧。這個尚需考證。如果有懂的童鞋,可以和我交流。


    終於寫完瞭。可能有很多地方需要改正,希望不吝指教。看瞭看時間,是凌晨1:46分。不早瞭,該睡瞭。天亮說晚安。

發佈留言