When using replay compilation I tried using the jar files generated by buildreplayjars command using clhsdb.
However, the replay compilation using those jar files failed with following error:
Error while parsing line 13391 at position 49: constant pool length mismatch: wrong class files?
On debugging I found the runtime adds additional entries to the constant pool of a class during the handling of default methods.
These entries correspond to the overpass methods that throw AbstractMethodError [0]. New entries are created using BytecodeAssembler [1] which maintains its own copy of the newly created constant pool entries (in BytecodeConstantPool). But when it adds new entries it does not search the original constant pool for the presence of such entries. This has two implications:
1. It can potentially result in duplicate entries in the constant pool.
A simple test case for this:
public interface IWriter {
public void write(final Object o);
default public boolean canWrite() { return false; }
}
public interface IntWriter extends IWriter {
public void writeInt(final Object o);
}
public class DefaultMethodTest {
public static void main(String args[]) throws Exception {
for (;;) {
Thread.currentThread().sleep(1000);
}
}
public void foo(IntWriter writer) {
writer.writeInt(new Object());
}
}
Note that IntWriter::writeInt has the same signature as IWriter::write.
After running DefaultMethodTest, attach to the process using jhsdb and dump the application class files using "buildreplayjars" command.
Disassembling the generated class file for IntWriter shows two Utf8 type entries corresponding to the same signature:
#1 = Class #2 // IntWriter
#2 = Utf8 IntWriter
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 = Class #6 // IWriter
#6 = Utf8 IWriter
#7 = Utf8 writeInt
#8 = Utf8 (Ljava/lang/Object;)V
#9 = Utf8 SourceFile
#10 = Utf8 IntWriter.java
#11 = Utf8 java/lang/AbstractMethodError
#12 = Class #11 // java/lang/AbstractMethodError
#13 = Utf8 Method IntWriter.write(Ljava/lang/Object;)V is abstract
#14 = String #13 // Method IntWriter.write(Ljava/lang/Object;)V is abstract
#15 = Utf8 (Ljava/lang/String;)V
#16 = Utf8 <init>
#17 = NameAndType #16:#15 // "<init>":(Ljava/lang/String;)V
#18 = Methodref #12.#17 // java/lang/AbstractMethodError."<init>":(Ljava/lang/String;)V
#19 = Utf8 write
#20 = Utf8 (Ljava/lang/Object;)V
Note that #8 and #20 are the same. #20 gets added during default method processing.
2. Another consequence of this is using the jar files generated by buildreplayjars command for replay compilation results in mismatch in constant pool length.
This happens because the class file created by buildreplayjars already has the extra constant pool entries required for the overpass methods. And when the class is loaded during replay compilation, all such entries get added to the constant pool again, which makes constant pool size different than its recorded value in the replay file.
Currently this can be worked around by using -XX:+ReplayIgnoreInitErrors during replay compilation.
[0] https://github.com/openjdk/jdk/blob/84184f947342fd1adbe4e3f2230ce3de4ae6007e/src/hotspot/share/classfile/defaultMethods.cpp#L868
[1] https://github.com/openjdk/jdk/blob/84184f947342fd1adbe4e3f2230ce3de4ae6007e/src/hotspot/share/classfile/bytecodeAssembler.hpp#L173