Hotspot 垃圾回收之oop_iterate(二) 源码解析

  目录

1、java.lang.Class

1.1、Class实例中oop_size、klass等属性是哪来的?

1.2、_offset_of_static_fields

1.3 为什么从_offset_of_static_fields处开始遍历?

2、InstanceRefKlass

3、ObjArrayKlass

4、oopDesc::adjust_pointers / follow_contents

4.1、InstanceKlass

 4.2、InstanceClassLoaderKlass

4.3、InstanceMirrorKlass

4.4、InstanceRefKlass

4.6、TypeArrayKlass

4.7、ObjArrayKlass


本篇博客继续上一篇《Hotspot 垃圾回收之oop_iterate(一) 源码解析》讲解其他Klass子类的oop_oop_iterate方法的实现细节和同样是GC支持的oopDesc::adjust_pointers / follow_contents方法的实现。

1、java.lang.Class

     理解InstanceMirrorKlass的引用遍历逻辑,我们需要逐步弄清楚以下几个问题:

1.1、Class实例中oop_size、klass等属性是哪来的?

   查看java.lang.Class的源码可知,该类并没有oop_size、klass等属性的声明,但是通过HSDB查看该类的Klass确实有该属性,测试用例如下:

package jvmTest;

import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;

class Base{
    public static int a=1;

    public static String s="abc";

    public static Integer a2=6;

    public static Integer a3=8;

    public static int a4=4;

    private int a5=12;

    private Integer a6=13;

    private int a7=13;
}

public class MainTest {

    public static void main(String[] args) {
        Class a=Base.class;
        System.out.println(Base.a);
        while (true){
            try {
                System.out.println(getProcessID());
                Thread.sleep(600*1000);
            } catch (Exception e) {

            }
        }
    }

    public static final int getProcessID() {
        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
        System.out.println(runtimeMXBean.getName());
        return Integer.valueOf(runtimeMXBean.getName().split("@")[0])
                .intValue();
    }
}

在Class Browser中搜索java.lang.Class,第一个便是该类对应的Klass,如下图:

点击该类可以发现该类其实是有很多属性的,如下:

上述private开头的属性在源码中都可以找到,就是比较分散隐蔽,从klass开始的剩余几个属性在源码中都没有,那这些属性是谁加进去的,什么时候加进去的了?答案是JVM,JVM在解析class文件中包含的属性时判断是java.lang.Class就会注入一部分字段放到属性的解析结果Array<u2>中,关键代码在负责字段解析的ClassFileParser::parse_fields方法中,如下图:

其中JavaClasses::get_injected就是返回需要注入的字段数组,其实现如下:

InjectedField* JavaClasses::get_injected(Symbol* class_name, int* field_count) {
  *field_count = 0;

  vmSymbols::SID sid = vmSymbols::find_sid(class_name);
  if (sid == vmSymbols::NO_SID) {
    // Only well known classes can inject fields
    return NULL;
  }

  int count = 0;
  int start = -1;

#define LOOKUP_INJECTED_FIELD(klass, name, signature, may_be_java) \
  if (sid == vmSymbols::VM_SYMBOL_ENUM_NAME(klass)) {              \
    //如果klass一致则增加count
    count++;                                                       \
    //如果start未初始化,则初始化,表示数组的起始位置
    if (start == -1) start = klass##_##name##_enum;                \
  }
  ALL_INJECTED_FIELDS(LOOKUP_INJECTED_FIELD);
#undef LOOKUP_INJECTED_FIELD

  if (start != -1) {
    //如果找到了,将field_count置为count
    *field_count = count;
    //返回_injected_fields数组中start处开始的元素,元素个数就是count
    return _injected_fields + start;
  }
  return NULL;
}

#define ALL_INJECTED_FIELDS(macro)          \
  CLASS_INJECTED_FIELDS(macro)              \
  CLASSLOADER_INJECTED_FIELDS(macro)        \
  MEMBERNAME_INJECTED_FIELDS(macro)

//java.lang.Class中新增的属性
#define CLASS_INJECTED_FIELDS(macro)                                       \
  macro(java_lang_Class, klass,                  intptr_signature,  false) \
  macro(java_lang_Class, array_klass,            intptr_signature,  false) \
  macro(java_lang_Class, oop_size,               int_signature,     false) \
  macro(java_lang_Class, static_oop_field_count, int_signature,     false) \
  macro(java_lang_Class, protection_domain,      object_signature,  false) \
  macro(java_lang_Class, init_lock,              object_signature,  false) \
  macro(java_lang_Class, signers,                object_signature,  false)

//_injected_fields是一个InjectedField数组
InjectedField JavaClasses::_injected_fields[] = {
  ALL_INJECTED_FIELDS(DECLARE_INJECTED_FIELD)
};

#define DECLARE_INJECTED_FIELD(klass, name, signature, may_be_java)           \
  { SystemDictionary::WK_KLASS_ENUM_NAME(klass), vmSymbols::VM_SYMBOL_ENUM_NAME(name##_name), vmSymbols::VM_SYMBOL_ENUM_NAME(signature), may_be_java },

从源码分析可知,处java.lang.Class外还有一些类也会以同样的方式注入新的属性,如下:

#define CLASSLOADER_INJECTED_FIELDS(macro)                            \
  macro(java_lang_ClassLoader, loader_data,  intptr_signature, false)

#define MEMBERNAME_INJECTED_FIELDS(macro)                               \
  macro(java_lang_invoke_MemberName, vmloader, object_signature, false) \
  macro(java_lang_invoke_MemberName, vmindex,  intptr_signature, false) \
  macro(java_lang_invoke_MemberName, vmtarget, intptr_signature, false)

1.2、_offset_of_static_fields

      InstanceMirrorKlass就增加了一个静态属性_offset_of_static_fields,用来描述静态字段的起始偏移量,因为是静态的,无法在HSDB中直接查看该属性。该属性是通过init_offset_of_static_fields方法初始化的,其实现如下:

 static void init_offset_of_static_fields() {
    //_offset_of_static_fields是静态字段,未初始化时系统自动赋值0
    assert(_offset_of_static_fields == 0, "once");
    //SystemDictionary::Class_klass()就是全局唯一的InstanceMirrorKlass实例
    _offset_of_static_fields = InstanceMirrorKlass::cast(SystemDictionary::Class_klass())->size_helper() << LogHeapWordSize;
  }
  
int size_helper() const {
    return layout_helper_to_size_helper(layout_helper());
  }

static int layout_helper_to_size_helper(jint lh) {
    assert(lh > (jint)_lh_neutral_value, "must be instance");
    return lh >> LogHeapWordSize;
  }

int layout_helper() const            { return _layout_helper; }

InstanceMirrorKlass的_layout_helper的取值是105,如下:

因此_offset_of_static_fields的值就是104。从上一节的Class包含的字段属性来看,该类的实例按照对象头和属性算出来的大小应该是100,但是必须按照一个字段对应的字节数即8对齐,因此是104,_layout_helper的值是为了保证通过size_helper方法计算的实例大小能够不低于实际的大小。

该方法的调用链如下:

1.3 为什么从_offset_of_static_fields处开始遍历?

     _offset_of_static_fields描述的是静态字段的起始偏移量,并非静态引用类型字段的起始偏移量,为什么要从这个偏移处开始遍历了?答案是Class实例为了确保通用性,将静态的引用类型属性都放在了一起且是静态字段区域的起始位置,然后通过static_oop_field_count属性就可以准确的定位所包含的oop了,这个跟普通类的实例完全相反,普通类实例的引用类型属性都是放在实例内存区域的最后,下面以上述的测试用例来说明。

先在Stack Memory中找到变量a即Base.class的地址,如下图:

然后在CHSDB中查看该地址对应的对象,如下:

该实例的大小是128,这个大小不是通过sizeof得出来的,而是从该oop的oop_size属性中读取出来的,用mem查看接下来的128字节即16个字段的内存数据,如下:

因为intel存储数据时是按照小端存储的,所以最后4行的真实内存数据应该如下:

0x00000000d69d9d78: 0x00000003 00000000 
0x00000000d69d9d80: 0xd69d9dc0 d687bde0 
0x00000000d69d9d88: 0xd687be00 00000001 
0x00000000d69d9d90: 0x00000004 00000000 

倒数第四行就是偏移量

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值