invokedynamic
เป็น instruction ที่ถูกอิมพลีเมนต์เพิ่มเข้ามาใน java 7 (JVM) โดยจุดประสงค์หลักจะทำมารองรับกับผู้ทำเฟรมเวิร์คหรือภาษา dynamic บน JVM เช่น JRuby,Groovy (เราเลยไม่ค่อยได้ใช้เท่าไร)ซึ่งแต่ก่อนนี้จะต้องใช้ java Reflection ในการ implement method call ซึ่งจะทำให้ได้ performace ที่ต่ำ เนื่องจากว่า JVM จะ optimize ลำบาก
คอมไพเลอร์ javac version 7 จะยังไม่มีการ compile ที่ให้ผลลัพธ์ที่เป็น invokedynamic
ออกมา ภาษาจาวาเองเริ่มนำ invokedynamic มาใช้ในเวอร์ชัน 8
Background
JVM จะมี instruction ที่ใช้ในการเรียก method ที่แตกต่างกันดังนี้
- invokespecial สำหรับเรียก instance private method หรือ constructor (non-virtual)
- invokestatic สำหรับเรียก static method
- invokevirtual สำหรับเรียก instance method ที่เป็น virtual พวก protected,public,default visibility
- invokeinterface สำหรับเรียก interface virtual method
สาเหตที่มี invokevirtual และ invokeinterface เพราะว่า invokervirtual คอมไพล์เลอร์จะรู้ตำเหน่งของ method ที่จะเรียกใน vitual table ได้ในตอนคอมไพล์ ส่วน invokeinterface เราจะ assume index ใน virtual table(vtable) ในตอนคอมไพล์ไม่ได้
ปล. ผมเข้าใจว่า virtual เป็น term ที่ใช้ใน c++ เป็นภาษาแรกนะ แต่ไม่ชัวร์ครับ
เพื่อให้ invokedynamic สามารถทำงานได้ java bytecode จำเป็นจะต้องมี runtime information อื่นๆเพิ่มเข้ามาใน class file ในส่วนที่เป็น constant pool ดังนี้
-
CONSTANT_MethodHandle_info เป็น data structure ที่เอาไว้ resolve reference ไปยัง invokespectal,invokestatic,invokevirtual,invokeintervace ในภาษาจาวาจะ represent โดย
java.lang.invoke.MethodHandle
-
CONSTANT_MethodType_info เอาไว้เป็น type descriptor ของ method (parameter และ return type) ในภาษาจาวาจะ represent โดย
java.lang.invoke.MethodType
- CONSTANT_InvokeDynamic_info เอาไว้ resolve reference ไปยัง bootstrap method และ optional argument ยังไม่ต้องสนใจในตอนนี้
สำหรับในบทความนี้เราจะมาเขียนจาวาให้ทำการเรียก method String.concat(String) เพื่อจำลองให้เห็นว่า CONSTANT_MethodHandle
และ CONSTANT_MethodType
มีบทบาทยังไงใน constant pool โดยใช้ java api
อ่านคำอธิบายจาก comment เอานะครับ
สำหรับใครที่ยังอ่าน bytecode ไม่ออก ผมมีบทความเก่าอยู่ ลองอ่านดูนะครับ
JVM 1
JVM 2
JVM 3
package th.co.geniustree.indy;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class MethodHandleTest {
public static void main(String[] args) throws Throwable {
//Lookup เป็น Factory สำหรับเอาไว้สร้าง MethodHandle
final MethodHandles.Lookup lookup = MethodHandles.lookup();
// เราจะอธิบายว่าเราต้องการ method ที่มี return type เป็นอะไร และ param เป็นอะไร
//ในที่นี้เราจะเรียก String.concat(String)
//กรณีที่ compiler เป็นคนจัดการให้ ตรงนีจะเป็น CONSTANT_MethodType
//CONSTANT_MethodType --> (Ljava/lang/String;)Ljava/lang/String;
//โดย L className ; เป็น field descriptor L หมายถึง reference type ใดๆ ส่วน className จะแทน . ด้วย /
MethodType returnStringAndStringParamMethosType = MethodType.methodType(String.class, String.class);
// สร้าง MethodHandle เพื่อห่อหุ่มเอา invokevirtual java/lang/String.concat:(Ljava/lang/String;)Ljava/lang/String; เอาไว้สำหรับ invoke
//ขั้นตอนนี้จะทำการ access checking ด้วยว่ามีสิทธิเข้าถึงไหม ซึ่งจะต่างจาก Reflection ตรงที่ reflection จะ check ทุกครั้งที่ invoke method ดังนั้นตรงนี้ MethodHandle จะเร็วกว่า
// เพราะโดยปกติแล้ว lookup.findXxx จะทำครั้งเดียวแล้วเก็บ method handle instance เอาไว้ invoke()
MethodHandle concatMethodHandle = lookup.findVirtual(String.class, "concat",returnStringAndStringParamMethosType);
//ถ้าเป็น bytecode ก็จะเทียบเท่ากับ
//ldc "Hello" --> push "Hello" reference to operand stack
//ldc "World" --> push "World" reference to operand stack
//invokevirtual java/lang/String.concat:(Ljava/lang/String;)Ljava/lang/String;
final String result = (String) concatMethodHandle.invokeExact("Hello", "World");
System.out.println(result); // print "HelloWorld"
}
}
Top comments (0)