35 根据 InstanceKlass 查找 itable 的数据

Posted 蓝风9

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了35 根据 InstanceKlass 查找 itable 的数据相关的知识,希望对你有一定的参考价值。

前言

// 呵呵 昨天陷入了没有流量的窘迫境地 

vtable 的相关数据结构介绍 可以参见 根据 InstanceKlass 查找 vtable 的数据 

itable 的数据结构的组合, 这里会介绍 

理解 vtable, itable 对于理解 invokevirtual, invokeinterface 是有帮助的, 进而对于 java 里面多态的具体实现方式, 也是一个基础 

以下调试 vm 部分基于 jdk9, HSDB 部分这里就不在赘述了, 参考 根据 InstanceKlass 查找 vtable 的数据 

测试用例

package com.hx.test07;
 
import java.awt.event.ActionListener;
import java.util.AbstractCollection;
 
/**
 * LookUpVTable
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2020-06-26 11:09
 */
public abstract class Test02LoopUpVTable extends AbstractCollection<String> implements ActionListener 
 
  // identStr
  private String identStr = "identStr";
  int f01;
  int f02;
  int f03;
  int f04;
  int f05;
 
  // Test02LoopUpVTable
  public static void main(String[] args) 
 
//    Test02LoopUpVTable instance = new Test02LoopUpVTable();
//
//    int sz = instance.size();
    int sz = 222;
    System.out.println(" szie : " + sz);
 
  
 
//  @Override
//  public String get(int index) 
//    return null;
//  
 
 
  @Override
  public int size() 
    return 222;
  
 

对应的字节码信息如下, 下面参照可能需要使用到 

master:test07 jerry$ javap -c -v Test02LoopUpVTable.class
Classfile /Users/jerry/IdeaProjects/HelloWorld/target/classes/com/hx/test07/Test02LoopUpVTable.class
  Last modified Jun 26, 2020; size 1171 bytes
  MD5 checksum e3b1a319ed8ec01458c1bc5d08937f60
  Compiled from "Test02LoopUpVTable.java"
public abstract class com.hx.test07.Test02LoopUpVTable extends java.util.AbstractCollection<java.lang.String> implements java.awt.event.ActionListener
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER, ACC_ABSTRACT
Constant pool:
   #1 = Methodref          #13.#41        // java/util/AbstractCollection."<init>":()V
   #2 = String             #15            // identStr
   #3 = Fieldref           #12.#42        // com/hx/test07/Test02LoopUpVTable.identStr:Ljava/lang/String;
   #4 = Fieldref           #43.#44        // java/lang/System.out:Ljava/io/PrintStream;
   #5 = Class              #45            // java/lang/StringBuilder
   #6 = Methodref          #5.#41         // java/lang/StringBuilder."<init>":()V
   #7 = String             #46            //  szie :
   #8 = Methodref          #5.#47         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #9 = Methodref          #5.#48         // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  #10 = Methodref          #5.#49         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #11 = Methodref          #50.#51        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #12 = Class              #52            // com/hx/test07/Test02LoopUpVTable
  #13 = Class              #53            // java/util/AbstractCollection
  #14 = Class              #54            // java/awt/event/ActionListener
  #15 = Utf8               identStr
  #16 = Utf8               Ljava/lang/String;
  #17 = Utf8               f01
  #18 = Utf8               I
  #19 = Utf8               f02
  #20 = Utf8               f03
  #21 = Utf8               f04
  #22 = Utf8               f05
  #23 = Utf8               <init>
  #24 = Utf8               ()V
  #25 = Utf8               Code
  #26 = Utf8               LineNumberTable
  #27 = Utf8               LocalVariableTable
  #28 = Utf8               this
  #29 = Utf8               Lcom/hx/test07/Test02LoopUpVTable;
  #30 = Utf8               main
  #31 = Utf8               ([Ljava/lang/String;)V
  #32 = Utf8               args
  #33 = Utf8               [Ljava/lang/String;
  #34 = Utf8               sz
  #35 = Utf8               size
  #36 = Utf8               ()I
  #37 = Utf8               Signature
  #38 = Utf8               Ljava/util/AbstractCollection<Ljava/lang/String;>;Ljava/awt/event/ActionListener;
  #39 = Utf8               SourceFile
  #40 = Utf8               Test02LoopUpVTable.java
  #41 = NameAndType        #23:#24        // "<init>":()V
  #42 = NameAndType        #15:#16        // identStr:Ljava/lang/String;
  #43 = Class              #55            // java/lang/System
  #44 = NameAndType        #56:#57        // out:Ljava/io/PrintStream;
  #45 = Utf8               java/lang/StringBuilder
  #46 = Utf8                szie :
  #47 = NameAndType        #58:#59        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #48 = NameAndType        #58:#60        // append:(I)Ljava/lang/StringBuilder;
  #49 = NameAndType        #61:#62        // toString:()Ljava/lang/String;
  #50 = Class              #63            // java/io/PrintStream
  #51 = NameAndType        #64:#65        // println:(Ljava/lang/String;)V
  #52 = Utf8               com/hx/test07/Test02LoopUpVTable
  #53 = Utf8               java/util/AbstractCollection
  #54 = Utf8               java/awt/event/ActionListener
  #55 = Utf8               java/lang/System
  #56 = Utf8               out
  #57 = Utf8               Ljava/io/PrintStream;
  #58 = Utf8               append
  #59 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #60 = Utf8               (I)Ljava/lang/StringBuilder;
  #61 = Utf8               toString
  #62 = Utf8               ()Ljava/lang/String;
  #63 = Utf8               java/io/PrintStream
  #64 = Utf8               println
  #65 = Utf8               (Ljava/lang/String;)V

  int f01;
    descriptor: I
    flags:
 
  int f02;
    descriptor: I
    flags:
 
  int f03;
    descriptor: I
    flags:
 
  int f04;
    descriptor: I
    flags:
 
  int f05;
    descriptor: I
    flags:
 
  public com.hx.test07.Test02LoopUpVTable();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/util/AbstractCollection."<init>":()V
         4: aload_0
         5: ldc           #2                  // String identStr
         7: putfield      #3                  // Field identStr:Ljava/lang/String;
        10: return
      LineNumberTable:
        line 13: 0
        line 16: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   Lcom/hx/test07/Test02LoopUpVTable;
 
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=2, args_size=1
         0: sipush        222
         3: istore_1
         4: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         7: new           #5                  // class java/lang/StringBuilder
        10: dup
        11: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        14: ldc           #7                  // String  szie :
        16: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        19: iload_1
        20: invokevirtual #9                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        23: invokevirtual #10                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        26: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        29: return
      LineNumberTable:
        line 29: 0
        line 30: 4
        line 32: 29
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      30     0  args   [Ljava/lang/String;
            4      26     1    sz   I
 
  public int size();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: sipush        222
         3: ireturn
      LineNumberTable:
        line 42: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       4     0  this   Lcom/hx/test07/Test02LoopUpVTable;

Signature: #38                          // Ljava/util/AbstractCollection<Ljava/lang/String;>;Ljava/awt/event/ActionListener;
SourceFile: "Test02LoopUpVTable.java"

基于 vm 的调试

我们先来看一下 itable 的元数据信息  

table_offset 为 80, 看过 根据 InstanceKlass 查找 vtable 的数据 应该知道 vtable 中的 table_offset 为 448, 并且 vtable 有 24 个元素 

那么和这里的 80 有什么关系呢?, 这两个 offset 的单位是不一样的, itable 的单位为字, vtable的单位为字节 

448(InstanceKlass of Test02LoopUpVTable) + 24 * 8(vtable 24个Method*) = 640(字节) 

80 * 8 = 640 (字节),  itable 是跟在 vtable 后面的 

我们再来看一下 itable 的开始区域 和 结束区域的计算方式 

itable 的开始于 vtable 结束的地方, itable 的长度为 itable_len(这里为31, 计算的方式我们待会儿再看)

itable 里面的 table_offset 是这么计算出来的(单位是 intptr_t*, 8字节)

另外从下面的逻辑可以看到, 在 itable 的区域, itableOffsetEntry(4个(三个接口 + 一个dummy)) 在前面, methodEntry(23个) 在后面 

从这里也可以大致的推导出 itable_len 的计算为 4*2 + 23 = 31(字) 

我们再来看一下 initialize 

处理的业务主要是在当前 InstanceKlass 的 itable 上面记录, 给定接口的方法, 如果重写了 则吧 Method 记录在 itable 上面 

我这里 Test02LoopUpVTable 总共实现了三个接口 : "java.util.Iterable", "java.util.Collection", ", "java.awt.event.ActionListener"

并且 顺序和上面一致 

其中 java.util.Iterable 有三个方法, iterator, forEach, spliterator, 后两个是默认接口方法 

其中 java.util.Collection 有十九个方法 

其中 java.awt.event.ActionListener 有一个方法, actionPerformed 

这上述的一些方法, iterator, actionPerformed 这两个很明显 Test02LoopUpVTable 是没有实现的 

forEach, spliterator 有对应的 默认方法实现 

那么我们来看一下 Test02LoopUpVTable 的 itable 的一些数据 

(lldb) x 0x7c008fc30 -c 0x400
# InstanceKlass of Test02LoopUpVTable
0x7c008fc30: 30 27 a3 0f 01 00 00 00 00 00 00 00 29 00 00 00  0'..........)...
0x7c008fc40: 40 00 00 00 00 00 00 00 f0 e9 b1 33 85 7f 00 00  @..........3....
0x7c008fc50: 18 6b 01 c0 07 00 00 00 90 c1 7d 13 01 00 00 00  .k.............
0x7c008fc60: 70 0f 00 c0 07 00 00 00 f8 6e 01 c0 07 00 00 00  p........n......
0x7c008fc70: 30 fc 08 c0 07 00 00 00 00 00 00 00 00 00 00 00  0...............
0x7c008fc80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x7c008fc90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x7c008fca0: 18 8b b7 bf 07 00 00 00 f8 6e 01 c0 07 00 00 00  .........n......
0x7c008fcb0: 00 00 00 00 00 00 00 00 00 77 07 c0 07 00 00 00  .........w......
0x7c008fcc0: 00 00 00 00 00 00 00 00 60 2c 43 32 85 7f 00 00  ........`,C2....
0x7c008fcd0: 01 04 00 00 21 04 20 10 00 00 00 00 00 00 00 00  ....!. .........
0x7c008fce0: 01 00 00 00 00 00 00 00 00 00 00 00 18 00 00 00  ................
0x7c008fcf0: 01 00 ff ff 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x7c008fd00: f0 da 59 32 85 7f 00 00 00 00 00 00 00 00 00 00  ..Y2............
0x7c008fd10: b0 c2 7d 13 01 00 00 00 60 e0 2e 13 01 00 00 00  .......`.......
0x7c008fd20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x7c008fd30: 06 00 00 00 00 00 00 00 26 00 28 00 00 00 06 00  ........&.(.....
0x7c008fd40: 01 00 00 00 1f 00 00 00 00 00 9c 00 00 00 34 00  ..............4.
0x7c008fd50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x7c008fd60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x7c008fd70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x7c008fd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x7c008fd90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x7c008fda0: 04 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x7c008fdb0: 00 00 00 00 00 00 00 00 60 c5 7d 13 01 00 00 00  ........`......
0x7c008fdc0: 88 c5 7d 13 01 00 00 00 e0 be 7d 13 01 00 00 00  ..............
0x7c008fdd0: 90 c1 7d 13 01 00 00 00 48 e0 2e 13 01 00 00 00  .......H.......
0x7c008fde0: 00 c7 7d 13 01 00 00 00 f8 be 7d 13 01 00 00 00  ..............

# vtable of Test02LoopUpVTable
0x7c008fdf0: 38 ee 2e 13 01 00 00 00 28 e8 2e 13 01 00 00 00  8.......(.......
0x7c008fe00: 60 15 41 13 01 00 00 00 40 e7 2e 13 01 00 00 00  `.A.....@.......
0x7c008fe10: c8 e8 2e 13 01 00 00 00 50 0e 41 13 01 00 00 00  ........P.A.....
0x7c008fe20: 58 0f 41 13 01 00 00 00 30 14 41 13 01 00 00 00  X.A.....0.A.....
0x7c008fe30: f0 07 41 13 01 00 00 00 f0 08 41 13 01 00 00 00  ..A.......A.....
0x7c008fe40: 38 c1 7d 13 01 00 00 00 90 0b 41 13 01 00 00 00  8........A.....
0x7c008fe50: 08 0a 41 13 01 00 00 00 b0 06 41 13 01 00 00 00  ..A.......A.....
0x7c008fe60: 48 11 41 13 01 00 00 00 48 10 41 13 01 00 00 00  H.A.....H.A.....
0x7c008fe70: 58 13 41 13 01 00 00 00 50 12 41 13 01 00 00 00  X.A.....P.A.....
0x7c008fe80: c0 c9 40 13 01 00 00 00 70 ca 40 13 01 00 00 00  ..@.....p.@.....
0x7c008fe90: f8 bd 40 13 01 00 00 00 c8 c6 40 13 01 00 00 00  ..@.......@.....
0x7c008fea0: 20 cb 40 13 01 00 00 00 00 c2 7d 13 01 00 00 00   .@............

# itable of Test02LoopUpVTable
0x7c008feb0: 28 69 01 c0 07 00 00 00 c0 02 00 00 00 00 00 00  (i..............
0x7c008fec0: 18 6b 01 c0 07 00 00 00 d8 02 00 00 00 00 00 00  .k..............
0x7c008fed0: e0 d3 08 c0 07 00 00 00 70 03 00 00 00 00 00 00  ........p.......
0x7c008fee0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x7c008fef0: 00 00 00 00 00 00 00 00 c0 c9 40 13 01 00 00 00  ..........@.....
0x7c008ff00: f8 bd 40 13 01 00 00 00 50 0e 41 13 01 00 00 00  ..@.....P.A.....
0x7c008ff10: 58 0f 41 13 01 00 00 00 30 14 41 13 01 00 00 00  X.A.....0.A.....
0x7c008ff20: f0 07 41 13 01 00 00 00 f0 08 41 13 01 00 00 00  ..A.......A.....
0x7c008ff30: 38 c1 7d 13 01 00 00 00 90 0b 41 13 01 00 00 00  8........A.....
0x7c008ff40: 08 0a 41 13 01 00 00 00 00 00 00 00 00 00 00 00  ..A.............
0x7c008ff50: c0 c9 40 13 01 00 00 00 48 11 41 13 01 00 00 00  ..@.....H.A.....
0x7c008ff60: 70 ca 40 13 01 00 00 00 48 10 41 13 01 00 00 00  p.@.....H.A.....
0x7c008ff70: 58 13 41 13 01 00 00 00 50 12 41 13 01 00 00 00  X.A.....P.A.....
0x7c008ff80: c8 c6 40 13 01 00 00 00 20 cb 40 13 01 00 00 00  ..@..... .@.....
0x7c008ff90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x7c008ffa0: 00 c2 7d 13 01 00 00 00 20 00 00 00 01 00 00 00  ....... .......
0x7c008ffb0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7  ................
0x7c008ffc0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7  ................
0x7c008ffd0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7  ................
0x7c008ffe0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7  ................
0x7c008fff0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7  ................
0x7c0090000: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7  ................
0x7c0090010: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7  ................
0x7c0090020: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7  ................

itable 的前面 8 个字为 itableOffsetEntry 

然后 之后的 23 个字为 methodEntry 

我们先来看一下前面 8 个字

0x7c008feb0: 28 69 01 c0 07 00 00 00 c0 02 00 00 00 00 00 00  (i..............
0x7c008fec0: 18 6b 01 c0 07 00 00 00 d8 02 00 00 00 00 00 00  .k..............
0x7c008fed0: e0 d3 08 c0 07 00 00 00 70 03 00 00 00 00 00 00  ........p.......
0x7c008fee0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

然后 运行时查看一下 这几个 itableOffsetEntry 

(lldb) po offset_entry(0)->interface_klass()
0x00000007c0016928
(lldb) po offset_entry(0)->offset()
704
(lldb) po offset_entry(1)->interface_klass()
0x00000007c0016b18
(lldb) po offset_entry(1)->offset()
728
(lldb) po offset_entry(2)->interface_klass()
0x00000007c008d3e0
(lldb) po offset_entry(2)->offset()
880
(lldb) po offset_entry(3)->interface_klass()
<nil>
(lldb) po offset_entry(3)->offset()
<nil>

offset_entry(3) 是 dummy 节点 

然后我们再来看一下 这些 methodEntry 的的信息 

0x7c008fef0: 00 00 00 00 00 00 00 00 c0 c9 40 13 01 00 00 00  ..........@.....
0x7c008ff00: f8 bd 40 13 01 00 00 00 50 0e 41 13 01 00 00 00  ..@.....P.A.....
0x7c008ff10: 58 0f 41 13 01 00 00 00 30 14 41 13 01 00 00 00  X.A.....0.A.....
0x7c008ff20: f0 07 41 13 01 00 00 00 f0 08 41 13 01 00 00 00  ..A.......A.....
0x7c008ff30: 38 c1 7d 13 01 00 00 00 90 0b 41 13 01 00 00 00  8........A.....
0x7c008ff40: 08 0a 41 13 01 00 00 00 00 00 00 00 00 00 00 00  ..A.............
0x7c008ff50: c0 c9 40 13 01 00 00 00 48 11 41 13 01 00 00 00  ..@.....H.A.....
0x7c008ff60: 70 ca 40 13 01 00 00 00 48 10 41 13 01 00 00 00  p.@.....H.A.....
0x7c008ff70: 58 13 41 13 01 00 00 00 50 12 41 13 01 00 00 00  X.A.....P.A.....
0x7c008ff80: c8 c6 40 13 01 00 00 00 20 cb 40 13 01 00 00 00  ..@..... .@.....
0x7c008ff90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x7c008ffa0: 00 c2 7d 13 01 00 00 00 20 00 00 00 01 00 00 00  ....... .......

然后 我们再来看一下 这几个 methodEntry 对应的数据  

(lldb) po ((Method*)0x011340c9c0)->name_and_sig_as_C_string()
"java.util.Collection.spliterator()Ljava/util/Spliterator;"

(lldb) po ((Method*)0x011340bdf8)->name_and_sig_as_C_string()
"java.lang.Iterable.forEach(Ljava/util/function/Consumer;)V"

(lldb) po ((Method*)(0x01137dc200))->name_and_sig_as_C_string()
"com.hx.test07.Test02LoopUpVTable.actionPerformed(Ljava/awt/event/ActionEvent;)V"

Iterable.iterator 方法 Test02LoopUpVTable 没有实现, 因此 对应的是 NULL 

Iterable.spliterator 被 Collection.spliterator 重写, 因此记录的是 Collection.spliterator

Iterable.forEach 记录的是默认方法 Iterable.forEach 

... 省略中间 N 个 Collection 的方法 

ActionListener.actionPerformed 记录的是类加载之后, 创建 instanceKlass 的时候 miranda 方法处理增加的一个 Method, 参见 新增的 miranda 方法 & 新增的常量池 entry

vtable_len 是怎么计算的呢?

可以看到 Test02LoopUpVTable 总共实现了 四个接口, 但是 上面我们只提到了三个 ? 

另外就是 这里的四个接口 和 上面的 四个itableOffsetEntry 是对应的吗 ?

那是因为 EventListener 接口是一个标记接口, 没有方法, 因此处理的时候 就把它过滤掉了 

计算 itable_len 的计算方式为 itableOffsetEntry 的数量(加上一个 dummy) * itableOffsetEntry 占用的空间(2个字), methodEntry 的数量 * methodEntry 占用的空间(1个字)

参考 

根据 InstanceKlass 查找 vtable 的数据

新增的 miranda 方法 & 新增的常量池 entry

以上是关于35 根据 InstanceKlass 查找 itable 的数据的主要内容,如果未能解决你的问题,请参考以下文章

33 根据 InstanceKlass 查找 vtable 的数据

33 根据 InstanceKlass 查找 vtable 的数据

51 java.lang.Class/java.lang.ClassLoader/InstanceKlass/ClassloaderData 的卸载

51 java.lang.Class/java.lang.ClassLoader/InstanceKlass/ClassloaderData 的卸载

34 新增的 miranda 方法 & 新增的常量池 entry

34 新增的 miranda 方法 & 新增的常量池 entry