.class文件
.class文件是编译.java文件生成的,即所谓的字节码文件。理论上说,不论是什么编程语言,只要经过编译器编译生成的文件符合class文件规范,都可以在JVM上正常运行。这也是如Groovy、Kotlin、Scala……这些语言能够在JVM上运行的本质原因。
想深入学习JVM,字节码文件是避不开的一个知识点。这块知识点相对JVM其他的知识点来说算比较简单的,想深入学习JVM的同学建议补下这块知识。如果你是在看不懂也没关系,我会在VIP课中深入讲解,手动模拟类加载器解析class文件的过程。
instanceKlass类
这是一个C++类,是Java类在JVM中的存在形式,是.class文件经过类加载器子系统加载后生成的。注意:这个类只是Java类在JVM中的存在形式,并不是Class对象。看核心代码
instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name,
ClassLoaderData* loader_data,
Handle protection_domain,
KlassHandle host_klass,
GrowableArray<Handle>* cp_patches,
TempNewSymbol& parsed_name,
bool verify,
TRAPS) {
……
// We can now create the basic Klass* for this klass
_klass = InstanceKlass::allocate_instance_klass(loader_data,
vtable_size,
itable_size,
info.static_field_size,
total_oop_map_size2,
rt,
access_flags,
name,
super_klass(),
!host_klass.is_null(),
CHECK_(nullHandle));
instanceKlassHandle this_klass (THREAD, _klass);
……
instanceKlass是Klass类的子类,除了完全继承了Klass类的非私有属性外,自己又定义了很多属性用来描述Java类的信息,我们来看看这些属性(标成蓝色的是比较常见的):
- _annotations:保存该类的所有注解
- _array_klasses:保存数组元素所关联的klass指针
- _constants:保存该类的常量池指针
- _inner_classes:保存内部类相关的信息
- _array_name:如果该类是数组,就会生成数组类名词,如“[Ljava/lang/String;”
- _nonstatic_field_size:非静态字段数量
- _static_field_size:静态字段数量
- _generic_signature_index:泛型签名在常量池中的索引
- _source_file_name_index:文件名在常量池中的索引
- _static_oop_field_count:该类包含的静态的引用类型字段个数
- _java_fields_count:已声明的Java字段数量
- _nonstatic_oop_map_size:非静态oop映射块的大小(以字为单位)
- _is_marked_dependent:用于刷新和反优化期间打标
- _minor_version:主版本号
- _major_version:次版本号
- _init_thread:初始化此类的线程
- _vtable_len:虚函数表的大小
- _itable_len:接口函数表的大小
- _oop_map_cache:该类所有方法的OopMapCache(延迟分配)
- _member_names:MemberNameTable指针
- _jni_ids:存放jni_id单向链表的首地址(什么是jni_id?)
- _methods_jmethod_ids:与method_idnum对应的jmethodIDs,如果没有,则为NULL
- _dependencies:存放nmethod的Bucket的首地址
- _osr_nmethods_head:栈上替换nmethods的链表的首地址
- _breakpoints:断点链表首地址
- _previous_versions:此实例的前一个版本的有趣部分的数组。请参见下面的PreviousVersionWalker
- _cached_class_file:缓存的类文件
- _idnum_allocated_count:已经分配的idnum的个数
- _init_state:该类的状态,值:allocated(已分配内存但未链接)、loaded(加载并插入到类层次结构中但仍未链接)、linked(验证及链接成功但未初始化)、being_initialized(正在初始化)、fully_initialized(已完成初始化)、initialization_error(初始化出错)
- _reference_type:引用类型
- _methods:存储该类的所有方法对象的指针的数组指针
- _default_methods:存储从接口继承的所有方法对象的指针的数组指针
- _local_interfaces:数组指针,存储所有实现的接口的指针
- _transitive_interfaces:数组,存储直接实现的接口指针+接口间继承实现的接口指针
- _method_ordering:包含类文件中方法的原始顺序的Int数组,JVMTI需要用到
- _default_vtable_indices:默认构造方法在虚表中的索引
- _fields:类的成员属性
Klass类
Klass类是Klass体系的基类,看下继承关系
从类名称上也能看出来,Klass相关类与元空间之间的关系。大多数情况下,Klass的子类是存储在元空间中的。正常情况下在C++中new一个对象,默认是在操作系统内存中的,JVM是如果实现在元空间中分配的呢?答案是重载C++的操作符new,是不是这样呢?看代码
void* MetaspaceObj::operator new(size_t size, ClassLoaderData* loader_data,
size_t word_size, bool read_only,
MetaspaceObj::Type type, TRAPS) throw() {
// Klass has it's own operator new
return Metaspace::allocate(loader_data, word_size, read_only,
type, CHECK_NULL);
}
Klass类有很多属性,我介绍几个重要的吧:
_layout_helper:如果这个Klass既不是instance也不是array,值为0,如果这个Klass是instance,值为对象的大小,如果这个Klass是array,值为负数。因为数组类型是运行时动态创建的,编译期无法知晓大小。
_java_mirror:这个Klass对于的镜像类的指针
Class对象
JVM并没有将描述Java类元信息的instanceKlass直接暴露给Java程序使用,而是又抽象了一层,即所谓的镜像类:instanceMirrorKlass。jdk8以后,类的静态属性也由存储在instanceKlass实例中转为存放在镜像类的实例中。详情见源码
instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name,
ClassLoaderData* loader_data,
Handle protection_domain,
KlassHandle host_klass,
GrowableArray<Handle>* cp_patches,
TempNewSymbol& parsed_name,
bool verify,
TRAPS) {
……
// Allocate mirror and initialize static fields
java_lang_Class::create_mirror(this_klass, protection_domain, CHECK_(nullHandle));
……
oop java_lang_Class::create_mirror(KlassHandle k, Handle protection_domain, TRAPS) {
……
Handle mirror = InstanceMirrorKlass::cast(SystemDictionary::Class_klass())->allocate_instance(k, CHECK_0);
……
// Initialize static fields
InstanceKlass::cast(k())->do_local_static_fields(&initialize_static_field, CHECK_NULL);
……
先看下第六行代码:初始化静态字段
再看下第三行代码调用的方法:allocate_instance
instanceOop InstanceMirrorKlass::allocate_instance(KlassHandle k, TRAPS) {
// Query before forming handle.
int size = instance_size(k);
KlassHandle h_k(THREAD, this);
instanceOop i = (instanceOop) CollectedHeap::Class_obj_allocate(h_k, size, k, CHECK_NULL);
return i;
}
从代码中可以看出来,是从堆区分配内存的。所以说Class对象是分配在堆上的。
以上就是Android虚拟机中内存结构klass内存解析;这是Android开发中的基础必备;更多Android进阶核心技术学习《Android核心进阶技术》可私:“手册”参与获取免费方式。
结语
总结一下,类加载器将.class文件载入JVM中,parse后生成的是instanceKlass对象,之后会生成这个Klass类对应的镜像类实例,并将Java类中的静态变量初始化后存储在镜像类实例中。这个镜像类就是我们Java代码中的Class对象。
网友评论