对于这样的java代码
javac之后会生成invoke_interface 的字节码.相关的实现位于templateTable::invokeInterface(int)生成的代码中.
java对象的的内存布局.
首先,变量i指向的内存布局是这样的.
[size=medium][oopDesc][长度与类相关的内存区域][/size]
oopDesc 包含2个成员, markWord和 klass.其中klass在未启用指针压缩时,指向该对象的类信息.
这个"类信息"不是java.lang.Class,而是虚拟机使用的内部数据.
klass指向的内存区域布局,参见(instanceKlass.hpp)
[size=medium][klassOopDesc][instanceKlass][javaVtable][javaItable][静态成员][oop map][/size]
klassOopDesc继承于oopDesc,添加了方法,但是没有新成员变量,所以内存结构一样.instanceKlass包含大量的类信息.其中,这里需要用到的就有 vtable_length, 其值就是javaVtable的长度.
[javaVtable] ==> vtable_length 个 method指针.
对于这里的例子,应该是以这个顺序
(Object类的方法)================
finalize
equals
toString
hashCode
clone
(TestClass类的方法)==
foo
foo1
java Itable内存布局,首先是该类说实现的所有接口类,以及该接口类的方法的偏移位置.最后以0表示接口类信息结束, 然后尾随的就是所有接口类的每个方法
interface0, offset
interface1, offset
.
.
.
interfacen, offset
0 0
========================
interface0_方法0
interface0_方法1
.
.
.
interface1_方法0
interface1_方法1
.
.
.
在上面的额示例代码里.itable就是
指向TestInterface指针, 偏移量[color=red]---[/color]
0, 0 [color=red] |[/color]
指向foo方法的指针[color=red]<--------------[/color]
指向foo1方法的指针
来看看invokeinterface的源码,
首先是和其他方法调用一样.
prepare_invoke()先去缓存找方法, 找不到就解析.连接
重点在这里
lookup_interface_method
movl(scan_temp, Address(recv_klass, instanceKlass::vtable_length_offset() * wordSize));
//scan_temp = (instanceKlass*)(recv_klass+ sizeof(klassOopDesc) )->_vtable_len
lea(scan_temp, Address(recv_klass, scan_temp, times_vte_scale, vtable_base));
//scan_temp = recv_klass + vtable_base + scan_temp * sizeof(vtableEntry); // end of vtable, vtable的后面就是itable了
lea(recv_klass, Address(recv_klass, itable_index, Address::times_ptr, itentry_off));
//recv_klass = recv_klass + itable_index * sizeof(itableMethodEntry) + offset_of(itableMethodEntry, _method) @1
最后顺便提一句.vtable里是有接口方法的的.
所以类似
[quote]TestClass t = new TestClass();//或者是TestClass的子类
t.foo();
[/quote]
这种事走vtable的,具体的javap看一下,他生成的是invokeVirtual
pubic interface TestInterface {
int foo();
int foo1();
}
pubic interface TestClass implement TestInterface{
public int foo(){
int a = 0;
reurn a;
}
public int foo1(){
int a = 1;
reurn a;
}
}
TestInterface i = new TestClass();
i.foo();
javac之后会生成invoke_interface 的字节码.相关的实现位于templateTable::invokeInterface(int)生成的代码中.
java对象的的内存布局.
首先,变量i指向的内存布局是这样的.
[size=medium][oopDesc][长度与类相关的内存区域][/size]
oopDesc 包含2个成员, markWord和 klass.其中klass在未启用指针压缩时,指向该对象的类信息.
这个"类信息"不是java.lang.Class,而是虚拟机使用的内部数据.
klass指向的内存区域布局,参见(instanceKlass.hpp)
[size=medium][klassOopDesc][instanceKlass][javaVtable][javaItable][静态成员][oop map][/size]
klassOopDesc继承于oopDesc,添加了方法,但是没有新成员变量,所以内存结构一样.instanceKlass包含大量的类信息.其中,这里需要用到的就有 vtable_length, 其值就是javaVtable的长度.
[javaVtable] ==> vtable_length 个 method指针.
对于这里的例子,应该是以这个顺序
(Object类的方法)================
finalize
equals
toString
hashCode
clone
(TestClass类的方法)==
foo
foo1
java Itable内存布局,首先是该类说实现的所有接口类,以及该接口类的方法的偏移位置.最后以0表示接口类信息结束, 然后尾随的就是所有接口类的每个方法
interface0, offset
interface1, offset
.
.
.
interfacen, offset
0 0
========================
interface0_方法0
interface0_方法1
.
.
.
interface1_方法0
interface1_方法1
.
.
.
在上面的额示例代码里.itable就是
指向TestInterface指针, 偏移量[color=red]---[/color]
0, 0 [color=red] |[/color]
指向foo方法的指针[color=red]<--------------[/color]
指向foo1方法的指针
来看看invokeinterface的源码,
首先是和其他方法调用一样.
prepare_invoke()先去缓存找方法, 找不到就解析.连接
重点在这里
lookup_interface_method
movl(scan_temp, Address(recv_klass, instanceKlass::vtable_length_offset() * wordSize));
//scan_temp = (instanceKlass*)(recv_klass+ sizeof(klassOopDesc) )->_vtable_len
lea(scan_temp, Address(recv_klass, scan_temp, times_vte_scale, vtable_base));
//scan_temp = recv_klass + vtable_base + scan_temp * sizeof(vtableEntry); // end of vtable, vtable的后面就是itable了
lea(recv_klass, Address(recv_klass, itable_index, Address::times_ptr, itentry_off));
//recv_klass = recv_klass + itable_index * sizeof(itableMethodEntry) + offset_of(itableMethodEntry, _method) @1
for (int peel = 1; peel >= 0; peel--) {
movptr(method_result, Address(scan_temp, itableOffsetEntry::interface_offset_in_bytes()));
cmpptr(intf_klass, method_result);
if (peel) {
jccb(Assembler::equal, found_method);
} else {
jccb(Assembler::notEqual, search);
// (invert the test to fall through to found_method...)
}
if (!peel) break;
bind(search);
// Check that the previous entry is non-null. A null entry means that
// the receiver class doesn't implement the interface, and wasn't the
// same as when the caller was compiled.
testptr(method_result, method_result);
jcc(Assembler::zero, L_no_such_interface);
addptr(scan_temp, scan_step);
}
bind(found_method);
//上面的代码简直坑爹展开一下
movptr(method_result, Address(scan_temp, itableOffsetEntry::interface_offset_in_bytes()));
//method_result = scan_temp->_interface
cmpptr(intf_klass, method_result);
jccb(Assembler::equal, found_method);
//if(intf_klass == method_result)
// goto found_method;
bind(search);
testptr(method_result, method_result);
jcc(Assembler::zero, L_no_such_interface);
//if(method_result == 0)
// goto L_no_such_interface;
addptr(scan_temp, scan_step);
// scan_temp += sizeof(itableOffsetEntry);
movptr(method_result, Address(scan_temp, itableOffsetEntry::interface_offset_in_bytes()));
//method_result = scan_temp->_interface
cmpptr(intf_klass, method_result);
jccb(Assembler::notEqual, search);
//if(intf_klass != result)
// goto search;
bind(found_method);
// Got a hit.
movl(scan_temp, Address(scan_temp, itableOffsetEntry::offset_offset_in_bytes()));
//scan_temp = ((itableOffsetEntry*)scan_temp)->_offset
movptr(method_result, Address(recv_klass, scan_temp, Address::times_1));
//method_result= recv_klass + scan_temp->_offset @2
//@1+@2 ==>
//method_result = ((itableMethodEntry*)(recv_klass + scan_temp->_offset))[itable_index]._method
for (scan = klass->itable(); scan->interface() != NULL; scan += sizeof(itableOffsetEntry)) {
if (scan->interface() == intf) {
result = (klass + scan->offset() + itable_index);
}
}
最后顺便提一句.vtable里是有接口方法的的.
所以类似
[quote]TestClass t = new TestClass();//或者是TestClass的子类
t.foo();
[/quote]
这种事走vtable的,具体的javap看一下,他生成的是invokeVirtual