Wenden Sie gesunden Menschenverstand auf, was Sie gerade lesen. Wenn Sie die Spezifikation korrekt lesen, d. H. Die erste Zahl, die im Array LineNumberTable
gespeichert ist, ist ein Bytecode-Offset, und die zweite Nummer ist eine Zeilennummer. Dies impliziert nicht, dass der verwendete Disassembler diese auch in dieser Reihenfolge ausgibt.
Es gibt zwei Indikatoren, die die Reihenfolge
- Es ist das Wort „Linie“ gedruckt, bevor die erste Zahl, was darauf hindeutet, dass diese erste Zahl ist eine Zeilennummer
Die erste Zahl Bereiche ausgelagert wurde von 204 bis 253, was für Quelltext-Zeilen einer Methode irgendwo in einer Klassendeklaration sinnvoll ist, während die zweite Zahl von 0
bis reicht, was für Byte-Code-Offsets innerhalb einer Methode sinnvoll ist, die mit Null beginnen.
Im Gegensatz dazu ist es unwahrscheinlich, dass Zeilennummern bei einer Methode mit Null beginnen, da der Quellcode normalerweise mit package
und import
Deklarationen beginnt. Und es wäre auch ungewöhnlich, wenn die ersten 203 Bytes eines Methodencodes keine Quellcodezeilen hätten (obwohl das nicht unmöglich wäre).
Beide Indikatoren zusammen sind ziemlich stark. Dann ist die beobachtete Änderung ziemlich plausibel. Offensichtlich hat sich der generierte Code nicht geändert. Da es jedoch keinen Standard gibt, wie die Zeilennummern und der generierte Code verknüpft sind, können je nach Compiler-Version, z. wenn ein Ausdruck mehrere Zeilen umfasst, aber nur eine Anweisung generiert oder wenn der Compiler versucht, zu große Zeilennummerentabellen zu vermeiden.
z. der Code
foo(
);
erzeugt nur eine Anweisung (wenn foo()
static
ist) und es ist nicht festgelegt, welche der beiden Leitungen mit dieser Instruktion zu assoziieren. Wenn es sich um eine Instanzmethode handelt, besteht sie aus zwei Anweisungen, die jedoch in verschiedenen Zeilennummern dargestellt werden könnten, wäre fraglich, da Zwischenschritte während des Debuggens nicht sehr hilfreich wären. Aber es ist die Entscheidung des Compilers.Auch mit
foo(
null,
1,
true
);
jeden der konstanten Argumenten in den Stapel erfordert einen Byte in der Befehlssequenz drückt, während eine bestimmte Leitungsnummer zu jedem der Befehlen Assoziieren wäre ein weiteres vier Bytes pro Befehl erfordern. Da das Drücken dieser Konstanten wahrscheinlich nicht fehlschlägt, würde die Verfolgung dieser Konstanten wenig Sinn ergeben, da Compiler entscheiden könnten, die gesamte Sequenz der einzigen Zeilennummer der Aufrufanweisung zuzuordnen. Da diese Entscheidung vom tatsächlichen Compiler und möglicherweise sogar von seiner aktuellen Konfiguration abhängt, kann das Neukompilieren die Zuordnung ändern.
Ein weiterer Unterschied ist, wie Compiler mit synthetischen Methoden umgehen, wie Bridge-Methoden und innere Klasse Accessoren. Ich habe bisher gesehen, dass sie nur mit Null assoziiert sind, mit dem Beginn der umgebenden Klassendeklaration und mit dem Beginn des eigentlichen Zielmitglieds, an das sie delegieren.
Siehe [this link] (https://docs.oracle.com/javase/specs/jvms/se6/html/ClassFile.doc.html#22856) - Teil der ersten Seite von googling "Java Line Number Table ". – TripeHound
Danke - Ich habe diese Seite schon gelesen. –
Entschuldigung (obwohl Sie erwähnt haben, dass Sie eine solche Seite gelesen haben (könnte) sarky Kommentare wie meine :-) Die Seite sagt die erste Nummer ist der Offset in den Byte-Code und die zweite ist die "Zeilennummer in der ursprünglichen Quelldatei ". Es besagt auch, dass Einträge in den (möglicherweise mehreren) Tabellen nicht eins zu eins mit den Quellzeilen sein müssen ... obwohl das nicht so klingt, als sollten die Zeilennummern in der Mitte eines mehrzeiligen Kommentars landen. Wenn Sie eine Vermutung riskieren, könnten Sie die Inkonsistenzen sehen, weil _andere_ Klassen zwischenzeitlich neu kompiliert wurden (siehe 2. Absatz in Abschnitt 4.9 auf dieser Seite)? – TripeHound