1. Overview
1.概述
In this short article, we’ll be looking at why we might see an unknown source in our Java exception stack trace and how can we fix it.
在这篇短文中,我们将探讨为什么我们会在Java异常堆栈跟踪中看到一个未知的来源,以及如何解决这个问题。
2. Class Debug Information
2.班级调试信息
A Java class file contains optional debug information to facilitate debugging. We can choose during compile time if and what all debug information is added to the class files. This’ll determine what debug information is available during the runtime.
一个Java类文件包含可选的调试信息以方便调试。我们可以在编译时选择是否以及哪些调试信息被添加到类文件中。这将决定在运行时有哪些调试信息可用。
Let’s investigate the Java compiler’s help documentation to see the various options available:
让我们研究一下Java编译器的帮助文档,看看有哪些不同的选项。
javac -help
Usage: javac <options> <source files>
where possible options include:
-g Generate all debugging info
-g:none Generate no debugging info
-g:{lines,vars,source} Generate only some debugging info
The default behaviour of Java’s compiler is to add the lines and source information to the class files which is equivalent to -g:lines,source.
Java编译器的默认行为是将行和源信息添加到类文件中,这相当于-g:lines,source.的意思。
2.1. Compiling with Debug Option
2.1.使用调试选项进行编译
Let’s see what happens when we compile our Java classes with the options above. We have a Main class that intentionally generates a StringIndexOutOfBoundsException.
让我们看看当我们用上面的选项编译我们的Java类时会发生什么。我们有一个Main类,故意产生一个StringIndexOutOfBoundsException。
Depending on the compiling mechanism used we’ll have to specify the compile option accordingly. Here, we’ll use Maven and its compiler plugin to customise the compiler option:
根据所使用的编译机制,我们必须相应地指定编译选项。这里,我们将使用Maven及其编译器插件来定制编译器选项。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<compilerArgs>
<arg>-g:none</arg>
</compilerArgs>
</configuration>
</plugin>
We’ve set -g to none which means no debugging information will be generated for our compiled classes. Running our buggy Main class generates the stack trace where we see unknown sources instead of the line number where the exception occurred.
我们将-g设置为none,这意味着不会为我们的编译类产生调试信息。运行我们有问题的Main类会产生堆栈跟踪,我们看到的是未知的来源,而不是发生异常的行号。
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: begin 0, end 10, length 5
at java.base/java.lang.String.checkBoundsBeginEnd(String.java:3751)
at java.base/java.lang.String.substring(String.java:1907)
at com.baeldung.unknownsourcestacktrace.Main.getShortenedName(Unknown Source)
at com.baeldung.unknownsourcestacktrace.Main.getGreetingMessage(Unknown Source)
at com.baeldung.unknownsourcestacktrace.Main.main(Unknown Source)
Let’s see what does the generated class file contains. We’ll use javap which is the Java class file disassembler to do this:
让我们看看生成的类文件包含哪些内容。我们将使用javap,它是Java类文件反汇编程序来完成这一工作。
javap -l -p Main.class
public class com.baeldung.unknownsourcestacktrace.Main {
private static final org.slf4j.Logger logger;
private static final int SHORT_NAME_LIMIT;
public com.baeldung.unknownsourcestacktrace.Main();
public static void main(java.lang.String[]);
private static java.lang.String getGreetingMessage(java.lang.String);
private static java.lang.String getShortenedName(java.lang.String);
static {};
}
It might be difficult to know what debug information we should expect here, so let’s change the compile option and see what happens.
可能很难知道我们在这里应该期待什么调试信息,所以让我们改变编译选项,看看会发生什么。
2.3. The Fix
2.3.修复
Let’s now change the compile option to -g:lines,vars,source which will put in LineNumberTable, LocalVariableTable and Source information into our class files. It’s also equivalent to just having -g which puts all the debug information:
现在让我们把编译选项改为-g:lines,vars,source,这将把LineNumberTable,LocalVariableTable和Source信息放入我们的类文件。这也等同于只有-g,它将所有的调试信息。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<compilerArgs>
<arg>-g</arg>
</compilerArgs>
</configuration>
</plugin>
Running our buggy Main class again now produces:
现在再次运行我们有问题的Mainclass,产生了。
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: begin 0, end 10, length 5
at java.base/java.lang.String.checkBoundsBeginEnd(String.java:3751)
at java.base/java.lang.String.substring(String.java:1907)
at com.baeldung.unknownsourcestacktrace.Main.getShortenedName(Main.java:23)
at com.baeldung.unknownsourcestacktrace.Main.getGreetingMessage(Main.java:19)
at com.baeldung.unknownsourcestacktrace.Main.main(Main.java:15)
Voila, we see the line number information in our stack trace. Let’s see what changed in our class file:
瞧,我们在堆栈跟踪中看到了行号信息。让我们看看我们的类文件有什么变化。
javap -l -p Main
Compiled from "Main.java"
public class com.baeldung.unknownsourcestacktrace.Main {
private static final org.slf4j.Logger logger;
private static final int SHORT_NAME_LIMIT;
public com.baeldung.unknownsourcestacktrace.Main();
LineNumberTable:
line 7: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/baeldung/unknownsourcestacktrace/Main;
public static void main(java.lang.String[]);
LineNumberTable:
line 12: 0
line 13: 8
line 15: 14
line 16: 29
LocalVariableTable:
Start Length Slot Name Signature
0 30 0 args [Ljava/lang/String;
8 22 1 user Lcom/baeldung/unknownsourcestacktrace/dto/User;
private static java.lang.String getGreetingMessage(java.lang.String);
LineNumberTable:
line 19: 0
LocalVariableTable:
Start Length Slot Name Signature
0 28 0 name Ljava/lang/String;
private static java.lang.String getShortenedName(java.lang.String);
LineNumberTable:
line 23: 0
LocalVariableTable:
Start Length Slot Name Signature
0 8 0 name Ljava/lang/String;
static {};
LineNumberTable:
line 8: 0
}
Our class file now contains three crucial pieces of information:
我们的类文件现在包含三个关键信息。
- Source, the top header indicating the .java file from which the .class file has been generated. In the context of a stack trace, it provides the class name where the exception occurred.
- LineNumberTable maps the line number in the code that the JVM actually runs to the line number in our source code file. In the context of a stack trace, it provides the line number where the exception occurred. We also need this to be able to use breakpoints in our debuggers.
- LocalVariableTable contains the details to get the value of a local variable. Debuggers may use it to read the value of a local variable. In the context of a stack trace, this doesn’t matter.
3. Conclusion
3.总结
We are now familiar with the debug information generated by the Java compiler. The way to manipulate them, -g compiler option. We saw how we can do that with the Maven compiler plugin.
我们现在已经熟悉了由Java编译器产生的调试信息。操作它们的方法,-g编译器选项。我们看到了如何用Maven编译器插件来做这件事。
So, if we find unknown sources in our stack traces we can investigate our class files to check if the debug information is available or not. Following which we can choose the right compile option based on our build tool to resolve this issue.
因此,如果我们在堆栈跟踪中发现未知的来源,我们可以调查我们的类文件以检查调试信息是否可用。之后,我们可以根据我们的构建工具选择正确的编译选项来解决这个问题。
As always, the complete code and Maven configurations are available over on GitHub.
一如既往,完整的代码和Maven配置可在GitHub上获取< data-saferedirecturl=”https://www.google.com/url?q=http://thelink.com/&source=gmail&ust=1640234878959000&usg=AOvVaw30romyOBJxqLz0Fo0Ry2P-” href=”https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-exceptions-3″ rel=”noopener” target=”_blank” >。