在Java中处理(未知源Unknown Source)堆栈跟踪
1.概述
在这篇短文中,我们将研究为什么在Java异常堆栈跟踪中可能会看到未知源Unknown Source,以及如何修复它。
2.类调试信息
Java类文件包含可选的调试信息,以便于调试。我们可以在编译期间选择是否将所有调试信息添加到类文件中。这将确定运行时有哪些调试信息可用。
让我们研究一下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
Java编译器的默认行为是将行和源信息添加到类文件中,这相当于 -g:lines,source。
2.1.使用调试选项编译
让我们看看当我们用上面的选项编译Java类时会发生什么。我们有一个 Main类 有意生成 StringIndexOutOfBoundsException.
根据使用的编译机制,我们必须相应地指定编译选项。这里,我们将使用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>
我们已经准备好了 -g 为 none 这意味着不会为我们编译的类生成调试信息。运行我们的 Main 类生成堆栈跟踪,我们在其中看到Unknown Source,而不是异常发生的行号。
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)
让我们看看生成的类文件包含什么。我们将使用 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 {};
}
可能很难知道我们应该在这里得到什么调试信息,所以让我们更改编译选项,看看会发生什么。
2.3.修复
现在让我们将编译选项更改为 -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>
运行我们的 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(Main.java:23)
at com.baeldung.unknownsourcestacktrace.Main.getGreetingMessage(Main.java:19)
at com.baeldung.unknownsourcestacktrace.Main.main(Main.java:15)
瞧,我们在堆栈跟踪中看到了行号信息。让我们看看类文件中发生了什么变化:
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
}
我们的类文件现在包含三条关键信息:
- Source 顶部标头指示 Java语言 从中 Class 文件已生成。在堆栈跟踪的上下文中,它提供发生异常的类名。
- LinNumberTable 将JVM实际运行的代码的行号映射到源代码文件中的行号。在堆栈跟踪的上下文中,它提供发生异常的行号。我们还需要这样才能在调试器中使用断点。
- LocalVariableTable 包含获取局部变量值的详细信息。调试器可以使用它来读取局部变量的值。在堆栈跟踪的上下文中,这并不重要。
3.结论
我们现在熟悉了Java编译器生成的调试信息。操纵它们的方法, -g编译器选项。我们看到了如何使用Maven编译器插件实现这一点。
因此,如果我们在堆栈跟踪中发现未知源,我们可以调查类文件,以检查调试信息是否可用。接下来,我们可以根据构建工具选择正确的编译选项来解决这个问题。
一如既往,完整的代码和Maven配置可用 在GitHub上.
- 本文标签: Java
- 本文链接: https://www.v8en.com/article/282
- 版权声明: 本文由SIMON原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权