Java LineNumberTable:条目说明
Java LineNumberTable: Entry explanation
如果我反汇编 class 文件,我会得到形式为
的 LineNumberTables
LineNumberTable:
line 204: 0
line 205: 9
line 208: 57
line 209: 63
line 210: 72
line 211: 75
line 212: 78
line 213: 87
line 216: 90
line 218: 118
line 221: 126
line 222: 131
line 223: 138
line 224: 143
line 227: 150
line 230: 157
line 231: 160
line 232: 170
line 235: 194
line 237: 228
line 240: 249
line 241: 259
line 243: 266
line 245: 269
line 246: 292
line 248: 295
line 249: 301
line 250: 308
line 251: 315
line 252: 322
line 253: 329
我知道这些表包含调试信息,第一个条目是 class 文件中的某种位置,而第二个是源代码中的一个位置。我想知道:
源代码行号是相对的还是绝对的?如果我绝对解释它们,有些指向多行注释的中间,这似乎很奇怪。
同一源代码的两个不同编译仅在一个字节上有所不同:条目 "line 216: 90" 被 "line 215: 90" 替换。我试图找出造成这种情况的原因。有什么想法吗?
将常识应用到您正在阅读的内容中。当您正确阅读规范时,即存储在 LineNumberTable
属性数组中的第一个数字是字节码偏移量,第二个是行号,这并不意味着您使用的反汇编程序也会打印它们按照这个顺序。
有两个指标表明订单已调换
- 第一个数字前面印有“Line”字样,说明这个第一个数字是行号
第一个数字的范围是 204 到 253,这对于 class 声明中某个方法的源代码行是合理的,而第二个数字的范围是 0
到 329
,这对于从零开始的方法中的字节码偏移量是合理的。
相比之下,不太可能看到方法的行号以零开头,因为源代码通常以 package
和 import
声明开头。如果方法代码的前 203 个字节没有关联的源代码行(尽管这并非不可能),这也是不寻常的。
这两个指标加起来都很强。那么,观察到的变化是相当合理的。显然,生成的代码没有改变。但是由于没有关于行号和生成代码如何关联的标准,因此可能存在细微差异,具体取决于编译器版本,例如当一个表达式跨越多行,但只生成一条指令时,或者当编译器试图避免过多的行号表时。
例如代码
foo(
);
只生成一条指令(如果 foo()
是 static
)并且没有定义,两行中的哪一行与该指令相关联。当它是一个实例方法时,它将由两条指令组成,但将它们表示为不同的行号是值得商榷的,因为在调试期间介于两者之间不会有太大帮助。但这是编译器的决定。还有
foo(
null,
1,
true
);
将每个常量参数压入堆栈需要指令序列中的一个字节,而将不同的行号关联到每个指令将需要每个指令另外四个字节。由于推送这些常量不太可能失败,因此跟踪它们没有什么意义,编译器可能决定将整个序列与调用指令的唯一行号相关联。由于此决定取决于实际编译器,甚至可能取决于其当前配置,重新编译可能会更改关联。
另一个区别是编译器如何处理合成方法,例如桥接方法和内部 class 访问器。到目前为止,我已经看到,它们仅与零、周围 class 声明的开头以及它们委托的实际目标成员的开头相关联。
如果我反汇编 class 文件,我会得到形式为
的 LineNumberTables LineNumberTable:
line 204: 0
line 205: 9
line 208: 57
line 209: 63
line 210: 72
line 211: 75
line 212: 78
line 213: 87
line 216: 90
line 218: 118
line 221: 126
line 222: 131
line 223: 138
line 224: 143
line 227: 150
line 230: 157
line 231: 160
line 232: 170
line 235: 194
line 237: 228
line 240: 249
line 241: 259
line 243: 266
line 245: 269
line 246: 292
line 248: 295
line 249: 301
line 250: 308
line 251: 315
line 252: 322
line 253: 329
我知道这些表包含调试信息,第一个条目是 class 文件中的某种位置,而第二个是源代码中的一个位置。我想知道:
源代码行号是相对的还是绝对的?如果我绝对解释它们,有些指向多行注释的中间,这似乎很奇怪。
同一源代码的两个不同编译仅在一个字节上有所不同:条目 "line 216: 90" 被 "line 215: 90" 替换。我试图找出造成这种情况的原因。有什么想法吗?
将常识应用到您正在阅读的内容中。当您正确阅读规范时,即存储在 LineNumberTable
属性数组中的第一个数字是字节码偏移量,第二个是行号,这并不意味着您使用的反汇编程序也会打印它们按照这个顺序。
有两个指标表明订单已调换
- 第一个数字前面印有“Line”字样,说明这个第一个数字是行号
第一个数字的范围是 204 到 253,这对于 class 声明中某个方法的源代码行是合理的,而第二个数字的范围是
0
到329
,这对于从零开始的方法中的字节码偏移量是合理的。相比之下,不太可能看到方法的行号以零开头,因为源代码通常以
package
和import
声明开头。如果方法代码的前 203 个字节没有关联的源代码行(尽管这并非不可能),这也是不寻常的。
这两个指标加起来都很强。那么,观察到的变化是相当合理的。显然,生成的代码没有改变。但是由于没有关于行号和生成代码如何关联的标准,因此可能存在细微差异,具体取决于编译器版本,例如当一个表达式跨越多行,但只生成一条指令时,或者当编译器试图避免过多的行号表时。
例如代码
foo(
);
只生成一条指令(如果 foo()
是 static
)并且没有定义,两行中的哪一行与该指令相关联。当它是一个实例方法时,它将由两条指令组成,但将它们表示为不同的行号是值得商榷的,因为在调试期间介于两者之间不会有太大帮助。但这是编译器的决定。还有
foo(
null,
1,
true
);
将每个常量参数压入堆栈需要指令序列中的一个字节,而将不同的行号关联到每个指令将需要每个指令另外四个字节。由于推送这些常量不太可能失败,因此跟踪它们没有什么意义,编译器可能决定将整个序列与调用指令的唯一行号相关联。由于此决定取决于实际编译器,甚至可能取决于其当前配置,重新编译可能会更改关联。
另一个区别是编译器如何处理合成方法,例如桥接方法和内部 class 访问器。到目前为止,我已经看到,它们仅与零、周围 class 声明的开头以及它们委托的实际目标成员的开头相关联。