jvm内存模型设计原理(OOP-klass模型对类的描叙及类加载)
jvm内存模型设计原理(OOP-klass模型对类的描叙及类加载)class Klass : public Metadata { friend class VMStructs; friend class JVMCIVMStructs; .......... // Class name. Instance classes: java/lang/String etc. Array classes: [I // [Ljava/lang/String; etc. Set to zero for all other kinds of classes. Symbol* _name; ......... // Array of all secondary supertypes Array<Klass*>* _secondary_supers; // Ordered list of all prima
一、oop-klass描叙1、介绍在JVM内存用到了oop-klass模型来描叙对应的类及对象:oop(ordinary object ponter,普通对象指针),其是用来描叙对象的实例信息。klass,其是JVM内部用来描叙类的信息的,例如java类的继承信息,成员方法等信息。同时JVM还有一种类型来封装对oop类型的行为-handle。
class Handle VALUE_OBJ_CLASS_SPEC {
private:
oop* _handle;
protected:
oop obj() const { return _handle == NULL ? (oop)NULL : *_handle; }
oop non_null_obj() const { assert(_handle != NULL "resolving NULL handle"); return *_handle; }
public:
// Constructors
Handle() { _handle = NULL; }
Handle(oop obj);
Handle(Thread* thread oop obj);
// General access
oop operator () () const { return obj(); }
oop operator -> () const { return non_null_obj(); }
bool operator == (oop o) const { return obj() == o; }
bool operator == (const Handle& h) const { return obj() == h.obj(); }
// Null checks
bool is_null() const { return _handle == NULL; }
bool not_null() const { return _handle != NULL; }
// Debugging
void print() { obj()->print(); }
Handle(oop *handle bool dummy) { _handle = handle; }
oop* raw_value() { return _handle; }
static oop raw_resolve(oop *handle) { return handle == NULL ? (oop)NULL : *handle; }
};
复制代码
可以看到其持有指向oop的指针,同时其的一些方法内部都是去处理的oop。
3、oopclass oop {
oopDesc* _o;
void register_oop();
void unregister_oop();
// friend class markOop;
public:
void set_obj(const void* p) {
raw_set_obj(p);
if (CheckUnhandledOops) register_oop();
}
void raw_set_obj(const void* p) { _o = (oopDesc*)p; }
oop() { set_obj(NULL); }
oop(const oop& o) { set_obj(o.obj()); }
......
oopDesc* obj() const volatile { return _o; }
// General access
oopDesc* operator->() const { return obj(); }
bool operator==(const oop o) const { return obj() == o.obj(); }
bool operator==(void *p) const { return obj() == p; }
........
复制代码
oop是持有指向oopDesc的指针,oopDesc就是描叙类信息。
1)、oopDescclass oopDesc {
friend class VMStructs;
friend class JVMCIVMStructs;
private:
volatile markOop _mark;
union _metadata {
Klass* _klass;
narrowKlass _compressed_klass;
} _metadata;
......
复制代码
这个就是描叙一个对象的基本结构。其中一个是对象头,就是我们一般看并发锁的书的时候说的需要获取的对象头,然后还有一个就是元数据结构_metadata,可以看到其有两个类型_klass&_compressed_klass。
Klass* oopDesc::klass() const {
if (UseCompressedClassPointers) {
return Klass::decode_klass_not_null(_metadata._compressed_klass);
} else {
return _metadata._klass;
}
}
复制代码
这里一个就是一般类型,一个就是压缩类型,通过UseCompressedClassPointers参数开启。
typedef class markOopDesc* markOop;
typedef class oopDesc* oop;
typedef class instanceOopDesc* instanceOop;
typedef class arrayOopDesc* arrayOop;
typedef class objArrayOopDesc* objArrayOop;
typedef class typeArrayOopDesc* typeArrayOop;
复制代码
这些就是oopDesc的子类。这里我们主要关注两个类型markOopDesc描叙对象的头信息、instanceOopDesc描叙类的实例信息。
2)、markOopDesc// The markOop describes the header of an object.
//
// Note that the mark is not a real oop but just a word.
// It is placed in the oop hierarchy for historical reasons.
//
// Bit-format of an object header (most significant first big endian layout below):
//
// 32 bits:
// --------
// hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object)
// JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object)
// size:32 ------------------------------------------>| (CMS free block)
// PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//
// 64 bits:
// --------
// unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object)
// JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object)
// PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
// size:64 ----------------------------------------------------->| (CMS free block)
//
// unused:25 hash:31 -->| cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && normal object)
// JavaThread*:54 epoch:2 cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && biased object)
// narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
// unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)
..........
class markOopDesc: public oopDesc {
private:
// Conversion
uintptr_t value() const { return (uintptr_t) this; }
public:
// Constants
enum { age_bits = 4
lock_bits = 2
biased_lock_bits = 1
max_hash_bits = BitsPerWord - age_bits - lock_bits - biased_lock_bits
hash_bits = max_hash_bits > 31 ? 31 : max_hash_bits
cms_bits = LP64_ONLY(1) NOT_LP64(0)
epoch_bits = 2
};
// The biased locking code currently requires that the age bits be
// contiguous to the lock bits.
enum { lock_shift = 0
biased_lock_shift = lock_bits
age_shift = lock_bits biased_lock_bits
cms_shift = age_shift age_bits
hash_shift = cms_shift cms_bits
epoch_shift = hash_shift
};
............
复制代码
通过32位的注释可以看到主要有四种类型normal object一般对象、biased object偏向锁,可以看到其有一个参数JavaThread,指向对应偏向的对象,然后就是CMS free block这个应该就是释放的对象,可以看到其不含任何内容、CMS promoted object这个从名称看猜测其应该是晋升对象,应该是指的晋升到了老年低?(可以看到其已经没有表示年龄age这个属性了)。同时我们可以看到表示年龄的是4位,也就是最大是为15。
3)、instanceOopDesc// An instanceOop is an instance of a Java Class
// Evaluating "new HashTable()" will create an instanceOop.
class instanceOopDesc : public oopDesc {
public:
// aligned header size.
static int header_size() { return sizeof(instanceOopDesc)/HeapWordSize; }
// If compressed the offset of the fields of the instance may not be aligned.
static int base_offset_in_bytes() {
// offset computation code breaks if UseCompressedClassPointers
// only is true
return (UseCompressedOops && UseCompressedClassPointers) ?
klass_gap_offset_in_bytes() :
sizeof(instanceOopDesc);
}
static bool contains_field_offset(int offset int nonstatic_field_size) {
int base_in_bytes = base_offset_in_bytes();
return (offset >= base_in_bytes &&
(offset-base_in_bytes) < nonstatic_field_size * heapOopSize);
}
};
复制代码
这个就是表示对应类的实例对象,可以看到这个没有什么额外的内容,然后有个header_size()方法,这个通过注释就是看其头部的大小,因为可以是32位的一个可以是64位的。
4、klassclass Klass : public Metadata {
friend class VMStructs;
friend class JVMCIVMStructs;
..........
// Class name. Instance classes: java/lang/String etc. Array classes: [I
// [Ljava/lang/String; etc. Set to zero for all other kinds of classes.
Symbol* _name;
.........
// Array of all secondary supertypes
Array<Klass*>* _secondary_supers;
// Ordered list of all primary supertypes
Klass* _primary_supers[_primary_super_limit];
// java/lang/Class instance mirroring this class
oop _java_mirror;
// Superclass
Klass* _super;
..........
// The VM's representation of the ClassLoader used to load this class.
// Provide access the corresponding instance java.lang.ClassLoader.
ClassLoaderData* _class_loader_data;
复制代码
这个就是在JVM内部描叙Class信息的类,_name就是用来描叙类名称等的内容,这里还有一个oop的类型的_java_mirror,这里在<<揭秘Java虚拟机-JVM设计原理与实现>>这本书的解释是,klass对于类的描叙是提供给JVM内部访问的,而这个_java_mirror是提供给java的。
同时我们可以看到这个类是继承的Metadata元数据:
// This is the base class for an internal Class related metadata
class Metadata : public MetaspaceObj {
复制代码
class MetaspaceObj {
public:
bool is_metaspace_object() const;
bool is_shared() const;
void print_address_on(outputStream* st) const; // nonvirtual address printing
#define METASPACE_OBJ_TYPES_DO(f) \
f(Unknown) \
f(Class) \
f(Symbol) \
f(TypeArrayU1) \
f(TypeArrayU2) \
f(TypeArrayU4) \
f(TypeArrayU8) \
f(TypeArrayOther) \
f(Method) \
f(ConstMethod) \
f(MethodData) \
f(ConstantPool) \
f(ConstantPoolCache) \
f(Annotation) \
f(MethodCounters) \
f(Deallocated)
复制代码
可以看到其又是继承的MetaspaceObj,所以其是在分配Metaspace中,同时属于Metaspace还有ConstantPool、ConstMethod这些也是在Metaspace。
同时Klass类似前面的oopDesc其也有对应的子类,例如InstanceKlass、ArrayKlass。
1)、InstanceKlassclass InstanceKlass: public Klass {
..........
protected:
InstanceKlass(const ClassFileParser& parser unsigned kind);
......
// See "The Java Virtual Machine Specification" section 2.16.2-5 for a detailed description
// of the class loading & initialization procedure and the use of the states.
enum ClassState {
allocated // allocated (but not yet linked)
loaded // loaded and inserted in class hierarchy (but not linked yet)
linked // successfully linked/verified (but not initialized yet)
being_initialized // currently running class initializer
fully_initialized // initialized (successfull final state)
initialization_error // error happened during initialization
};
protected:
// Annotations for this class
Annotations* _annotations;
// Package this class is defined in
PackageEntry* _package_entry;
// Array classes holding elements of this class.
Klass* volatile _array_klasses;
// Constant pool for this class.
ConstantPool* _constants;
.....
Array<jushort>* _inner_classes;
..........
u2 _static_oop_field_count;// number of static oop fields in this klass
u2 _java_fields_count; // The number of declared Java fields
int _nonstatic_oop_map_size;// size in words of nonstatic oop map blocks
int _itable_len; // length of Java itable (in words)
// _is_marked_dependent can be set concurrently thus cannot be part of the
// _misc_flags.
bool _is_marked_dependent; // used for marking during flushing and deoptimization
bool _is_being_redefined; // used for locking redefinition
.......
u2 _misc_flags;
u2 _minor_version; // minor version number of class file
u2 _major_version; // major version number of class file
Thread* _init_thread; // Pointer to current thread doing initialization (to handle recusive initialization)
OopMapCache* volatile _oop_map_cache; // OopMapCache for all methods in the klass (allocated lazily)
MemberNameTable* _member_names; // Member names
JNIid* _jni_ids; // First JNI identifier for static fields in this class
jmethodID* volatile _methods_jmethod_ids; // jmethodIDs corresponding to method_idnum or NULL if none
复制代码
这个就是用来描叙类信息的,我们可以看到定义了一个枚举ClassState来表示当前类的加载情况,例如:loaded(加载)、linked(链接)、fully_initialized(完成了类的初始化)。然后还有一些属性例如_annotations注解信息、_constants常量池信息等。
二、类的解析
1、起点 (openjdk\hotspot\src\share\vm\classfile\classLoader.cpp)
instanceKlassHandle ClassLoader::load_class(Symbol* name bool search_append_only TRAPS) {
.......
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
const char* const class_name = name->as_C_string();
EventMark m("loading class %s" class_name);
ThreadProfilerMark tpm(ThreadProfilerMark::classLoaderRegion);
const char* const file_name = file_name_for_class_name(class_name
name->utf8_length());
assert(file_name != NULL "invariant");
ClassLoaderExt::Context context(class_name file_name THREAD);
// Lookup stream for parsing .class file
ClassFileStream* stream = NULL;
s2 classpath_index = 0;
ClassPathEntry* e = NULL;
.........
// Load Attempt #2: [jimage | exploded build]
if (!search_append_only && (NULL == stream)) {
if (has_jrt_entry()) {
e = _jrt_entry;
stream = _jrt_entry->open_stream(file_name CHECK_NULL);
if (!context.check(stream classpath_index)) {
return NULL;
}
} else {
// Exploded build - attempt to locate class in its defining module's location.
assert(_exploded_entries != NULL "No exploded build entries present");
stream = search_module_entries(_exploded_entries class_name file_name CHECK_NULL);
}
}
.........
if (NULL == stream) {
if (DumpSharedSpaces) {
tty->print_cr("Preload Warning: Cannot find %s" class_name);
}
return NULL;
}
stream->set_verify(context.should_verify(classpath_index));
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
Handle protection_domain;
instanceKlassHandle result = KlassFactory::create_from_stream(stream
name
loader_data
protection_domain
NULL // host_klass
NULL // cp_patches
........
return context.record_result(name e classpath_index result THREAD);
}
复制代码
这个就是去加载解析.class文件:
可以看到我们目前处理的是BitSet,然后将其读取为stream(ClassFileStream)。
class instanceKlassHandle : public KlassHandle {
public:
/* Constructors */
instanceKlassHandle () : KlassHandle() {}
instanceKlassHandle (const Klass* k) : KlassHandle(k) {
assert(k == NULL || is_instanceKlass(k) "illegal type");
}
instanceKlassHandle (Thread* thread const Klass* k) : KlassHandle(thread k) {
assert(k == NULL || is_instanceKlass(k) "illegal type");
}
/* Access to klass part */
InstanceKlass* operator () () const { return (InstanceKlass*)obj(); }
InstanceKlass* operator -> () const { return (InstanceKlass*)obj(); }
debug_only(bool is_instanceKlass(const Klass* k));
};
复制代码
2)、然后再通过create_from_stream方法来解析:
instanceKlassHandle KlassFactory::create_from_stream(ClassFileStream* stream
Symbol* name
ClassLoaderData* loader_data
Handle protection_domain
const InstanceKlass* host_klass
GrowableArray<Handle>* cp_patches
TRAPS) {
assert(stream != NULL "invariant");
assert(loader_data != NULL "invariant");
assert(THREAD->is_Java_thread() "must be a JavaThread");
ResourceMark rm;
HandleMark hm;
JvmtiCachedClassFileData* cached_class_file = NULL;
.........
ClassFileParser parser(stream
name
loader_data
protection_domain
host_klass
cp_patches
ClassFileParser::BROADCAST // publicity level
CHECK_NULL);
instanceKlassHandle result = parser.create_instance_klass(old_stream != stream CHECK_NULL);
if (result.is_null()) {
return NULL;
}
.......
#if INCLUDE_CDS && INCLUDE_JVMTI
if (DumpSharedSpaces) {
assert(cached_class_file == NULL "Sanity");
// Archive the class stream data into the optional data section
JvmtiCachedClassFileData *p;
int len;
const unsigned char *bytes;
// event based tracing might set cached_class_file
if ((bytes = result->get_cached_class_file_bytes()) != NULL) {
len = result->get_cached_class_file_len();
} else {
len = stream->length();
bytes = stream->buffer();
}
p = (JvmtiCachedClassFileData*)MetaspaceShared::optional_data_space_alloc(
offset_of(JvmtiCachedClassFileData data) len);
p->length = len;
memcpy(p->data bytes len);
result->set_archived_class_data(p);
}
#endif
return result;
}
复制代码
这里先解析获取parser,再通过其创建instanceKlassHandle(parser.create_instance_klass(old_stream != stream CHECK_NULL))。
3)、解析创建InstanceKlass
InstanceKlass* ClassFileParser::create_instance_klass(bool changed_by_loadhook TRAPS) {
.........
InstanceKlass* const ik =
InstanceKlass::allocate_instance_klass(*this CHECK_NULL);
fill_instance_klass(ik changed_by_loadhook CHECK_NULL);
......
return ik;
}
复制代码
InstanceKlass* InstanceKlass::allocate_instance_klass(const ClassFileParser& parser TRAPS) {
const int size = InstanceKlass::size(parser.vtable_size()
parser.itable_size()
nonstatic_oop_map_size(parser.total_oop_map_count())
parser.is_interface()
parser.is_anonymous()
should_store_fingerprint());
const Symbol* const class_name = parser.class_name();
assert(class_name != NULL "invariant");
ClassLoaderData* loader_data = parser.loader_data();
assert(loader_data != NULL "invariant");
InstanceKlass* ik;
// Allocation
if (REF_NONE == parser.reference_type()) {
if (class_name == vmSymbols::java_lang_Class()) {
// mirror
ik = new (loader_data size THREAD) InstanceMirrorKlass(parser);
}
else if (is_class_loader(class_name parser)) {
// class loader
ik = new (loader_data size THREAD) InstanceClassLoaderKlass(parser);
}
else {
// normal
ik = new (loader_data size THREAD) InstanceKlass(parser InstanceKlass::_misc_kind_other);
}
}
else {
// reference
ik = new (loader_data size THREAD) InstanceRefKlass(parser);
}
......
// Add all classes to our internal class loader list here
// including classes in the bootstrap (NULL) class loader.
loader_data->add_class(ik publicize);
Atomic::inc(&_total_instanceKlass_count);
return ik;
}
复制代码
这里就是创建实例KlassInstanceKlass。本次会调用new (loader_data size THREAD) InstanceKlass(parser InstanceKlass::_misc_kind_other)。
void* Klass::operator new(size_t size ClassLoaderData* loader_data size_t word_size TRAPS) throw() {
return Metaspace::allocate(loader_data word_size /*read_only*/false
MetaspaceObj::ClassType THREAD);
}
复制代码
同时用于这里的类型入参是MetaspaceObj::ClassType 所以是会分配在_class_vsm。
然后再来看下fill_instance_klass(ik changed_by_loadhook CHECK_NULL):
void ClassFileParser::fill_instance_klass(InstanceKlass* ik bool changed_by_loadhook TRAPS) {
.........
// Fill in information already parsed
ik->set_should_verify_class(_need_verify);
// Not yet: supers are done below to support the new subtype-checking fields
ik->set_class_loader_data(_loader_data);
ik->set_nonstatic_field_size(_field_info->nonstatic_field_size);
ik->set_has_nonstatic_fields(_field_info->has_nonstatic_fields);
ik->set_static_oop_field_count(_fac->count[STATIC_OOP]);
..........
ik->set_name(_class_name);
if (is_anonymous()) {
// I am well known to myself
ik->constants()->klass_at_put(_this_class_index ik); // eagerly resolve
}
ik->set_minor_version(_minor_version);
ik->set_major_version(_major_version);
ik->set_has_nonstatic_concrete_methods(_has_nonstatic_concrete_methods);
ik->set_declares_nonstatic_concrete_methods(_declares_nonstatic_concrete_methods);
if (_host_klass != NULL) {
assert (ik->is_anonymous() "should be the same");
ik->set_host_klass(_host_klass);
}
// Set PackageEntry for this_klass
oop cl = ik->class_loader();
Handle clh = Handle(THREAD java_lang_ClassLoader::non_reflection_class_loader(cl));
ClassLoaderData* cld = ClassLoaderData::class_loader_data_or_null(clh());
ik->set_package(cld CHECK);
....
// check if this class can access its superinterfaces
check_super_interface_access(ik CHECK);
// check if this class overrides any final method
check_final_method_override(ik CHECK);
.....
// Obtain java.lang.Module
Handle module_handle(THREAD JNIHandles::resolve(module_entry->module()));
// Allocate mirror and initialize static fields
// The create_mirror() call will also call compute_modifiers()
java_lang_Class::create_mirror(ik
_loader_data->class_loader()
module_handle
_protection_domain
CHECK);
......
// it's official
set_klass(ik);
debug_only(ik->verify();)
}
复制代码
这个就是填充InstanceKlass的信息,然后我们看下create_mirror方法:
void java_lang_Class::create_mirror(KlassHandle k Handle class_loader
Handle module Handle protection_domain TRAPS) {
.....
int computed_modifiers = k->compute_modifier_flags(CHECK);
k->set_modifier_flags(computed_modifiers);
// Class_klass has to be loaded because it is used to allocate
// the mirror.
if (SystemDictionary::Class_klass_loaded()) {
// Allocate mirror (java.lang.Class instance)
Handle mirror = InstanceMirrorKlass::cast(SystemDictionary::Class_klass())->allocate_instance(k CHECK);
// Setup indirection from mirror->klass
if (!k.is_null()) {
java_lang_Class::set_klass(mirror() k());
}
InstanceMirrorKlass* mk = InstanceMirrorKlass::cast(mirror->klass());
...........
}
复制代码
3)、parser方法解析:
ClassFileParser::ClassFileParser(ClassFileStream* stream
Symbol* name
ClassLoaderData* loader_data
Handle protection_domain
const InstanceKlass* host_klass
GrowableArray<Handle>* cp_patches
Publicity pub_level
TRAPS) :
.......
// Figure out whether we can skip format checking (matching classic VM behavior)
if (DumpSharedSpaces) {
// verify == true means it's a 'remote' class (i.e. non-boot class)
// Verification decision is based on BytecodeVerificationRemote flag
// for those classes.
_need_verify = (stream->need_verify()) ? BytecodeVerificationRemote :
BytecodeVerificationLocal;
}
else {
_need_verify = Verifier::should_verify_for(_loader_data->class_loader()
stream->need_verify());
}
// synch back verification state to stream
stream->set_verify(_need_verify);
// Check if verification needs to be relaxed for this class file
// Do not restrict it to jdk1.0 or jdk1.1 to maintain backward compatibility (4982376)
_relax_verify = relax_format_check_for(_loader_data);
parse_stream(stream CHECK);
post_process_parsed_stream(stream _cp CHECK);
}
复制代码
void ClassFileParser::parse_stream(const ClassFileStream* const stream
TRAPS) {
// BEGIN STREAM PARSING
stream->guarantee_more(8 CHECK); // magic major minor
// Magic value
const u4 magic = stream->get_u4_fast();
// Version numbers
_minor_version = stream->get_u2_fast();
_major_version = stream->get_u2_fast();
..........
_cp = ConstantPool::allocate(_loader_data
cp_size
CHECK);
ConstantPool* const cp = _cp;
parse_constant_pool(stream cp cp_size CHECK);
// ACCESS FLAGS
stream->guarantee_more(8 CHECK); // flags this_class super_class infs_len
// Access flags
jint flags;
// JVM_ACC_MODULE is defined in JDK-9 and later.
if (_major_version >= JAVA_9_VERSION) {
flags = stream->get_u2_fast() & (JVM_RECOGNIZED_CLASS_MODIFIERS | JVM_ACC_MODULE);
} else {
flags = stream->get_u2_fast() & JVM_RECOGNIZED_CLASS_MODIFIERS;
}
........
_access_flags.set_flags(flags);
// This class and superclass
_this_class_index = stream->get_u2_fast();
.....
Symbol* const class_name_in_cp = cp->klass_name_at(_this_class_index);
assert(class_name_in_cp != NULL "class_name can't be null");
// Update _class_name which could be null previously
// to reflect the name in the constant pool
_class_name = class_name_in_cp;
.........
// SUPERKLASS
_super_class_index = stream->get_u2_fast();
_super_klass = parse_super_class(cp
_super_class_index
_need_verify
CHECK);
// Interfaces
_itfs_len = stream->get_u2_fast();
parse_interfaces(stream
_itfs_len
cp
&_has_nonstatic_concrete_methods
CHECK);
.......
// Fields (offsets are filled in later)
_fac = new FieldAllocationCount();
parse_fields(stream
_access_flags.is_interface()
_fac
cp
cp_size
&_java_fields_count
CHECK);
......
// Methods
AccessFlags promoted_flags;
parse_methods(stream
_access_flags.is_interface()
&promoted_flags
&_has_final_method
&_declares_nonstatic_concrete_methods
CHECK);
........
// Additional attributes/annotations
_parsed_annotations = new ClassAnnotationCollector();
parse_classfile_attributes(stream cp _parsed_annotations CHECK);
......
}
复制代码
这里先会通过ConstantPool::allocate申请常量此的空间,再通过parse_constant_pool(stream cp cp_size CHECK)具体解析,然后解析Methods、Fields这些。
ConstantPool* ConstantPool::allocate(ClassLoaderData* loader_data int length TRAPS) {
.......
int size = ConstantPool::size(length);
return new (loader_data size false MetaspaceObj::ConstantPoolType THREAD) ConstantPool(tags);
}
复制代码
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 THREAD);
}
复制代码
MetaWord* Metaspace::allocate(ClassLoaderData* loader_data size_t word_size
bool read_only MetaspaceObj::Type type TRAPS) {
.......
MetadataType mdtype = (type == MetaspaceObj::ClassType) ? ClassType : NonClassType;
// Try to allocate metadata.
MetaWord* result = loader_data->metaspace_non_null()->allocate(word_size mdtype);
.......
// Zero initialize. 空间清零
Copy::fill_to_words((HeapWord*)result word_size 0);
return result;
}
复制代码
public:
enum MetadataType {
ClassType
NonClassType
MetadataTypeCount
};
enum MetaspaceType {
StandardMetaspaceType
BootMetaspaceType
ROMetaspaceType
ReadWriteMetaspaceType
AnonymousMetaspaceType
ReflectionMetaspaceType
};
复制代码
可以看到这里MetadataType有ClassType、NonClassType、MetadataTypeCount,这里主要是因为在JVM中又会将MetaspaceType空间再分为两种:
Metaspace* ClassLoaderData::metaspace_non_null() {
Metaspace* metaspace = load_ptr_acquire(&_metaspace);
if (metaspace == NULL) {
MutexlockerEx ml(_metaspace_lock Mutex::_no_safepoint_check_flag);
// Check if _metaspace got allocated while we were waiting for this lock.
if ((metaspace = _metaspace) == NULL) {
if (this == the_null_class_loader_data()) {
assert (class_loader() == NULL "Must be");
metaspace = new Metaspace(_metaspace_lock Metaspace::BootMetaspaceType);
} else if (is_anonymous()) {
if (class_loader() != NULL) {
log_trace(class loader data)("is_anonymous: %s" class_loader()->klass()->internal_name());
}
metaspace = new Metaspace(_metaspace_lock Metaspace::AnonymousMetaspaceType);
} else if (class_loader()->is_a(SystemDictionary::reflect_DelegatingClassLoader_klass())) {
if (class_loader() != NULL) {
log_trace(class loader data)("is_reflection: %s" class_loader()->klass()->internal_name());
}
metaspace = new Metaspace(_metaspace_lock Metaspace::ReflectionMetaspaceType);
} else {
metaspace = new Metaspace(_metaspace_lock Metaspace::StandardMetaspaceType);
}
// Ensure _metaspace is stable since it is examined without a lock
OrderAccess::release_store_ptr(&_metaspace metaspace);
}
}
return metaspace;
}
复制代码
这里可以看到会创建对应的空间类型new Metaspace
Metaspace::Metaspace(Mutex* lock MetaspaceType type) {
initialize(lock type);
}
复制代码
void Metaspace::initialize(Mutex* lock MetaspaceType type) {
verify_global_initialization();
// Allocate SpaceManager for metadata objects.
_vsm = new SpaceManager(NonClassType lock);
if (using_class_space()) {
// Allocate SpaceManager for classes.
_class_vsm = new SpaceManager(ClassType lock);
}
MutexLockerEx cl(SpaceManager::expand_lock() Mutex::_no_safepoint_check_flag);
// Allocate chunk for metadata objects
initialize_first_chunk(type NonClassType);
// Allocate chunk for class metadata objects
if (using_class_space()) {
initialize_first_chunk(type ClassType);
}
_alloc_record_head = NULL;
_alloc_record_tail = NULL;
}
复制代码
可以看到,这里会创建两种空间管理_vsm、如果是Class的话会使用_class_vsm
// SpaceManager - used by Metaspace to handle allocations
class SpaceManager : public CHeapObj<mtClass> {
friend class Metaspace;
friend class Metadebug;
private:
// protects allocations
Mutex* const _lock;
// Type of metadata allocated.
Metaspace::MetadataType _mdtype;
复制代码
使用这个SpaceManager来进行分配管理,可以看到这里是锁的Mutex,用于竞争分配空间。下面我们再来看空间分配:
MetaWord* Metaspace::allocate(size_t word_size MetadataType mdtype) {
// DumpSharedSpaces doesn't use class metadata area (yet)
// Also don't use class_vsm() unless UseCompressedClassPointers is true.
if (is_class_space_allocation(mdtype)) {
return class_vsm()->allocate(word_size);
} else {
return vsm()->allocate(word_size);
}
}
复制代码
可以看到这里会根据类型从不同的vsm中分配:
MetaWord* SpaceManager::allocate(size_t word_size) {
MutexLockerEx cl(lock() Mutex::_no_safepoint_check_flag);
size_t raw_word_size = get_allocation_word_size(word_size);
BlockFreelist* fl = block_freelists();
MetaWord* p = NULL;
// Allocation from the dictionary is expensive in the sense that
// the dictionary has to be searched for a size. Don't allocate
// from the dictionary until it starts to get fat. Is this
// a reasonable policy? Maybe an skinny dictionary is fast enough
// for allocations. Do some profiling. JJJ
if (fl != NULL && fl->total_size() > allocation_from_dictionary_limit) {
p = fl->get_block(raw_word_size);
}
if (p == NULL) {
p = allocate_work(raw_word_size);
}
return p;
}
复制代码
MetaWord* SpaceManager::allocate_work(size_t word_size) {
.....
// Is there space in the current chunk?
MetaWord* result = NULL;
........
if (current_chunk() != NULL) {
result = current_chunk()->allocate(word_size);
}
.......
return result;
}
复制代码
MetaWord* Metachunk::allocate(size_t word_size) {
MetaWord* result = NULL;
// If available bump the pointer to allocate.
if (free_word_size() >= word_size) {
result = _top;
_top = _top word_size;
}
return result;
}
复制代码
二、类实例的创建1、起点
openjdk\hotspot\src\share\vm\interpreter\interpreterRuntime.cpp
// Allocation
IRT_ENTRY(void InterpreterRuntime::_new(JavaThread* thread ConstantPool* pool int index))
Klass* k_oop = pool->klass_at(index CHECK);
instanceKlassHandle klass (THREAD k_oop);
// Make sure we are not instantiating an abstract klass
klass->check_valid_for_instantiation(true CHECK);
// Make sure klass is initialized
klass->initialize(CHECK);
// At this point the class may not be fully initialized
// because of recursive initialization. If it is fully
// initialized & has_finalized is not set we rewrite
// it into its fast version (Note: no locking is needed
// here since this is an atomic byte write and can be
// done more than once).
//
// Note: In case of classes with has_finalized we don't
// rewrite since that saves us an extra check in
// the fast version which then would call the
// slow version anyway (and do a call back into
// Java).
// If we have a breakpoint then we don't rewrite
// because the _breakpoint bytecode would be lost.
oop obj = klass->allocate_instance(CHECK);
thread->set_vm_result(obj);
IRT_END
复制代码
我们以这个为起点,看下类实例oop对象是怎样申请空间的:
2、这里首先是klass->initialize(CHECK)// See "The Virtual Machine Specification" section 2.16.5 for a detailed explanation of the class initialization
// process. The step comments refers to the procedure described in that section.
// Note: implementation moved to static method to expose the this pointer.
void InstanceKlass::initialize(TRAPS) {
if (this->should_be_initialized()) {
HandleMark hm(THREAD);
instanceKlassHandle this_k(THREAD this);
initialize_impl(this_k CHECK);
// Note: at this point the class may be initialized
// OR it may be in the state of being initialized
// in case of recursive initialization!
} else {
assert(is_initialized() "sanity check");
}
}
复制代码
这里首先是类加载,即instanceKlassHandle对象的创建初始化initialize_impl(this_k CHECK):
void InstanceKlass::initialize_impl(instanceKlassHandle this_k TRAPS) {
// Make sure klass is linked (verified) before initialization
// A class could already be verified since it has been reflected upon.
this_k->link_class(CHECK);
......
bool wait = false;
// refer to the JVM book page 47 for description of steps
// Step 1
{
oop init_lock = this_k->init_lock();
ObjectLocker ol(init_lock THREAD init_lock != NULL);
Thread *self = THREAD; // it's passed the current thread
// Step 2
// If we were to use wait() instead of waitInterruptibly() then
// we might end up throwing IE from link/symbol resolution sites
// that aren't expected to throw. This would wreak havoc. See 6320309.
while(this_k->is_being_initialized() && !this_k->is_reentrant_initialization(self)) {
wait = true;
ol.waitUninterruptibly(CHECK);
}
// Step 3
if (this_k->is_being_initialized() && this_k->is_reentrant_initialization(self)) {
DTRACE_CLASSINIT_PROBE_WAIT(recursive this_k() -1 wait);
return;
}
// Step 4
if (this_k->is_initialized()) {
DTRACE_CLASSINIT_PROBE_WAIT(concurrent this_k() -1 wait);
return;
}
// Step 5
if (this_k->is_in_error_state()) {
...........
}
// Step 6
this_k->set_init_state(being_initialized);
this_k->set_init_thread(self);
}
// Step 7
// Next if C is a class rather than an interface initialize it's super class and super
// interfaces.
if (!this_k->is_interface()) {
Klass* super_klass = this_k->super();
if (super_klass != NULL && super_klass->should_be_initialized()) {
super_klass->initialize(THREAD);
}
// If C implements any interface that declares a non-static concrete method
// the initialization of C triggers initialization of its super interfaces.
// Only need to recurse if has_nonstatic_concrete_methods which includes declaring and
// having a superinterface that declares non-static concrete methods
if (!HAS_PENDING_EXCEPTION && this_k->has_nonstatic_concrete_methods()) {
this_k->initialize_super_interfaces(this_k THREAD);
}
.........
}
// Look for aot compiled methods for this klass including class initializer.
AOTLoader::load_for_klass(this_k THREAD);
// Step 8
{
......
this_k->call_class_initializer(THREAD);
}
// Step 9
if (!HAS_PENDING_EXCEPTION) {
this_k->set_initialization_state_and_notify(fully_initialized CHECK);
......
}
else {
// Step 10 and 11
Handle e(THREAD PENDING_EXCEPTION);
CLEAR_PENDING_EXCEPTION;
// JVMTI has already reported the pending exception
// JVMTI internal flag reset is needed in order to report ExceptionInInitializerError
.........
}
DTRACE_CLASSINIT_PROBE_WAIT(end this_k() -1 wait);
}
复制代码
这里对应前面的枚举类型就是类的加载过程。加载后,接下来就是oop对象的实例化创建oop obj = klass->allocate_instance(CHECK);:
3、klass->allocate_instance(CHECK)instanceOop InstanceKlass::allocate_instance(TRAPS) {
bool has_finalizer_flag = has_finalizer(); // Query before possible GC
int size = size_helper(); // Query before forming handle.
KlassHandle h_k(THREAD this);
instanceOop i;
i = (instanceOop)CollectedHeap::obj_allocate(h_k size CHECK_NULL);
if (has_finalizer_flag && !RegisterFinalizersAtInit) {
i = register_finalizer(i CHECK_NULL);
}
return i;
}
复制代码
typedef class oopDesc* oop;
typedef class instanceOopDesc* instanceOop;
typedef class arrayOopDesc* arrayOop;
typedef class objArrayOopDesc* objArrayOop;
typedef class typeArrayOopDesc* typeArrayOop;
复制代码
这里是通过obj_allocate创建instanceOop实例对象。
4、obj_allocate openjdk\hotspot\src\share\vm\gc\shared\collectedHeap.inline.hpp
oop CollectedHeap::obj_allocate(KlassHandle klass int size TRAPS) {
debug_only(check_for_valid_allocation_state());
assert(!Universe::heap()->is_gc_active() "Allocation during gc not allowed");
assert(size >= 0 "int won't convert to size_t");
HeapWord* obj = common_mem_allocate_init(klass size CHECK_NULL);
post_allocation_setup_obj(klass obj size);
NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj size));
return (oop)obj;
}
复制代码
然后再通过common_mem_allocate_init方法进行空间分配及初始化:
HeapWord* CollectedHeap::common_mem_allocate_init(KlassHandle klass size_t size TRAPS) {
HeapWord* obj = common_mem_allocate_noinit(klass size CHECK_NULL);
init_obj(obj size);
return obj;
}
复制代码
5、common_mem_allocate_noinit
HeapWord* CollectedHeap::common_mem_allocate_noinit(KlassHandle klass size_t size TRAPS) {
HeapWord* result = NULL;
if (UseTLAB) {
result = allocate_from_tlab(klass THREAD size);
if (result != NULL) {
assert(!HAS_PENDING_EXCEPTION
"Unexpected exception will result in uninitialized storage");
return result;
}
}
bool gc_overhead_limit_was_exceeded = false;
result = Universe::heap()->mem_allocate(size
&gc_overhead_limit_was_exceeded);
if (result != NULL) {
NOT_PRODUCT(Universe::heap()->
check_for_non_bad_heap_word_value(result size));
assert(!HAS_PENDING_EXCEPTION
"Unexpected exception will result in uninitialized storage");
THREAD->incr_allocated_bytes(size * HeapWordSize);
AllocTracer::send_allocation_outside_tlab_event(klass size * HeapWordSize);
return result;
}
.......
}
复制代码
这里首先是判断UseTLAB,即是否分配先分配在与线程相关的空间TLAB上(能更快分配,因为如果分配在堆上,由于堆随便那个线程都能申请,所以会加锁,而如果分配在与线程相关的TLAB上,就没有锁竞争,就能更快的分配)
6、allocate_from_tlabHeapWord* CollectedHeap::allocate_from_tlab(KlassHandle klass Thread* thread size_t size) {
assert(UseTLAB "should use UseTLAB");
HeapWord* obj = thread->tlab().allocate(size);
if (obj != NULL) {
return obj;
}
// Otherwise...
return allocate_from_tlab_slow(klass thread size);
}
复制代码
// Thread-Local Allocation Buffer (TLAB) support
ThreadLocalAllocBuffer& tlab() { return _tlab; }
复制代码
inline HeapWord* ThreadLocalAllocBuffer::allocate(size_t size) {
invariants();
HeapWord* obj = top();
if (pointer_delta(end() obj) >= size) {
// successful thread-local allocation
#ifdef ASSERT
// Skip mangling the space corresponding to the object header to
// ensure that the returned space is not considered parsable by
// any concurrent GC thread.
size_t hdr_size = oopDesc::header_size();
Copy::fill_to_words(obj hdr_size size - hdr_size badHeapWordVal);
#endif // ASSERT
// This addition is safe because we know that top is
// at least size below end so the add can't wrap.
set_top(obj size);
invariants();
return obj;
}
return NULL;
}
复制代码
HeapWord* top() const { return _top; }
复制代码
这里首先是通过pointer_delta方法看有没有足够的空间分配,有的话就分配,再通过set_top(obj size);设置新的_top位置。
如果上面这个分配失败,再进入慢分配allocate_from_tlab_slow(KlassHandle klass Thread* thread size_t size)
HeapWord* CollectedHeap::allocate_from_tlab_slow(KlassHandle klass Thread* thread size_t size) {
.....
// Allocate a new TLAB...
HeapWord* obj = Universe::heap()->allocate_new_tlab(new_tlab_size);
if (obj == NULL) {
return NULL;
}
AllocTracer::send_allocation_in_new_tlab_event(klass new_tlab_size * HeapWordSize size * HeapWordSize);
if (ZeroTLAB) {
// ..and clear it.
Copy::zero_to_words(obj new_tlab_size);
} else {
// ...and zap just allocated object.
#ifdef ASSERT
// Skip mangling the space corresponding to the object header to
// ensure that the returned space is not considered parsable by
// any concurrent GC thread.
size_t hdr_size = oopDesc::header_size();
Copy::fill_to_words(obj hdr_size new_tlab_size - hdr_size badHeapWordVal);
#endif // ASSERT
}
thread->tlab().fill(obj obj size new_tlab_size);
return obj;
}
复制代码
可以看到这里还有一个参数ZeroTLAB来表示是否将分配到空间清零。
下面我们再通过将UseTLAB设置为false来从堆上分配 Universe::heap()->mem_allocate。
7、Universe::heap()->mem_allocate // The particular choice of collected heap.
static CollectedHeap* heap() { return _collectedHeap; }
复制代码
HeapWord* GenCollectedHeap::mem_allocate(size_t size
bool* gc_overhead_limit_was_exceeded) {
return gen_policy()->mem_allocate_work(size
false /* is_tlab */
gc_overhead_limit_was_exceeded);
}
复制代码
可以看到目前的策略是标记清除。
HeapWord* GenCollectorPolicy::mem_allocate_work(size_t size
bool is_tlab
bool* gc_overhead_limit_was_exceeded) {
GenCollectedHeap *gch = GenCollectedHeap::heap();
.............
HeapWord* result = NULL;
// Loop until the allocation is satisfied or unsatisfied after GC.
for (uint try_count = 1 gclocker_stalled_count = 0; /* return or throw */; try_count = 1) {
HandleMark hm; // Discard any handles allocated in each iteration.
// First allocation attempt is lock-free.
Generation *young = gch->young_gen();
if (young->should_allocate(size is_tlab)) {
result = young->par_allocate(size is_tlab);
if (result != NULL) {
assert(gch->is_in_reserved(result) "result not in heap");
return result;
}
}
uint gc_count_before; // Read inside the Heap_lock locked region.
{
MutexLocker ml(Heap_lock);
log_trace(gc alloc)("GenCollectorPolicy::mem_allocate_work: attempting locked slow path allocation");
// Note that only large objects get a shot at being
// allocated in later generations.
bool first_only = ! should_try_older_generation_allocation(size);
result = gch->attempt_allocation(size is_tlab first_only);
if (result != NULL) {
assert(gch->is_in_reserved(result) "result not in heap");
return result;
}
if (GCLocker::is_active_and_needs_gc()) {
if (is_tlab) {
return NULL; // Caller will retry allocating individual object.
}
if (!gch->is_maximal_no_gc()) {
// Try and expand heap to satisfy request.
result = expand_heap_and_allocate(size is_tlab);
// Result could be null if we are out of space.
if (result != NULL) {
return result;
}
}
....... }
}
复制代码
1)、这里首先是判断是不能再年轻代分配
// First allocation attempt is lock-free.
Generation *young = gch->young_gen();
if (young->should_allocate(size is_tlab)) {
result = young->par_allocate(size is_tlab);
if (result != NULL) {
assert(gch->is_in_reserved(result) "result not in heap");
return result;
}
}
复制代码
如果年轻代能分配就通过par_allocate方法去分配空间:
HeapWord* DefNewGeneration::par_allocate(size_t word_size
bool is_tlab) {
HeapWord* res = eden()->par_allocate(word_size);
if (CMSEdenChunksRecordAlways && _old_gen != NULL) {
_old_gen->sample_eden_chunk();
}
return res;
}
复制代码
// Accessing spaces
ContiguousSpace* eden() const { return _eden_space; }
ContiguousSpace* from() const { return _from_space; }
ContiguousSpace* to() const { return _to_space; }
复制代码
// This version is lock-free.
inline HeapWord* ContiguousSpace::par_allocate_impl(size_t size) {
do {
HeapWord* obj = top();
if (pointer_delta(end() obj) >= size) {
HeapWord* new_top = obj size;
HeapWord* result = (HeapWord*)Atomic::cmpxchg_ptr(new_top top_addr() obj);
// result can be one of two:
// the old top value: the exchange succeeded
// otherwise: the new value of the top is returned.
if (result == obj) {
assert(is_aligned(obj) && is_aligned(new_top) "checking alignment");
return obj;
}
} else {
return NULL;
}
} while (true);
}
复制代码
这里就是在_eden_space区分配,可以看到这里有个注释,现在是没有使用锁的,其是使用Atomic::cmpxchg_ptr操作,即CAS去修改申请的,然后通过while循环。同时上面的DefNewGeneration::par_allocate我们需要注意,其是在eden()申请,并不会如果eden()不够再去from()或to()申请。
下面我们看下如果young->should_allocate(size is_tlab)不满足,通过gch->attempt_allocation(size is_tlab first_only)方法申请的过程。
2)、attempt_allocationMutexLocker ml(Heap_lock);
log_trace(gc alloc)("GenCollectorPolicy::mem_allocate_work: attempting locked slow path allocation");
// Note that only large objects get a shot at being
// allocated in later generations.
bool first_only = ! should_try_older_generation_allocation(size);
result = gch->attempt_allocation(size is_tlab first_only);
if (result != NULL) {
assert(gch->is_in_reserved(result) "result not in heap");
return result;
}
复制代码
可以看到这里会使用锁。
HeapWord* GenCollectedHeap::attempt_allocation(size_t size
bool is_tlab
bool first_only) {
HeapWord* res = NULL;
if (_young_gen->should_allocate(size is_tlab)) {
res = _young_gen->allocate(size is_tlab);
if (res != NULL || first_only) {
return res;
}
}
if (_old_gen->should_allocate(size is_tlab)) {
res = _old_gen->allocate(size is_tlab);
}
return res;
}
复制代码
这里会再次判断_young_gen是否能分配,再次尝试分配。
HeapWord* DefNewGeneration::allocate(size_t word_size bool is_tlab) {
// This is the slow-path allocation for the DefNewGeneration.
// Most allocations are fast-path in compiled code.
// We try to allocate from the eden. If that works we are happy.
// Note that since DefNewGeneration supports lock-free allocation we
// have to use it here as well.
HeapWord* result = eden()->par_allocate(word_size);
if (result != NULL) {
if (CMSEdenChunksRecordAlways && _old_gen != NULL) {
_old_gen->sample_eden_chunk();
}
} else {
// If the eden is full and the last collection bailed out we are running
// out of heap space and we try to allocate the from-space too.
// allocate_from_space can't be inlined because that would introduce a
// circular dependency at compile time.
result = allocate_from_space(word_size);
}
return result;
}
复制代码
HeapWord* DefNewGeneration::allocate_from_space(size_t size) {
bool should_try_alloc = should_allocate_from_space() || GCLocker::is_active_and_needs_gc();
// If the Heap_lock is not locked by this thread this will be called
// again later with the Heap_lock held.
bool do_alloc = should_try_alloc && (Heap_lock->owned_by_self() || (SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread()));
HeapWord* result = NULL;
if (do_alloc) {
result = from()->allocate(size);
}
return result;
}
复制代码
可以看到这里如果在eden()区分配失败,是可能再在from()区分配的。现在我们看下在老年代的分配:
3)、HeapWord* TenuredGeneration::allocateHeapWord* TenuredGeneration::allocate(size_t word_size
bool is_tlab) {
assert(!is_tlab "TenuredGeneration does not support TLAB allocation");
return _the_space->allocate(word_size);
}
复制代码
inline HeapWord* OffsetTableContigSpace::allocate(size_t size) {
HeapWord* res = ContiguousSpace::allocate(size);
if (res != NULL) {
_offsets.alloc_block(res size);
}
return res;
}
复制代码
// This version requires locking. 这种分配是需要锁的
inline HeapWord* ContiguousSpace::allocate_impl(size_t size) {
assert(Heap_lock->owned_by_self() ||
(SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread())
"not locked");
HeapWord* obj = top();
if (pointer_delta(end() obj) >= size) {
HeapWord* new_top = obj size;
set_top(new_top);
assert(is_aligned(obj) && is_aligned(new_top) "checking alignment");
return obj;
} else {
return NULL;
}
}
复制代码
以上就是我们对JVM类对象oopp的空间申请创建的过程这里流程的梳理。
作者:Feverasa
链接:https://juejin.cn/post/6936877801128198174
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。