欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Unknown Source的出现及解决  

程序员文章站 2022-05-19 16:54:56
...

转自:http://www.blogjava.net/landon/archive/2011/02/27/345265.html

昨天在公司写了一段代码,很简单,就是测试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分。不早了,该睡了。天亮说晚安