JVM篇-oop-klass模型对类的描叙及类加载&实例化内存申请过程
作者:互联网
一、oop-klass描叙
1、介绍
在JVM内存用到了oop-klass模型来描叙对应的类及对象:oop(ordinary object ponter,普通对象指针),其是用来描叙对象的实例信息。klass,其是JVM内部用来描叙类的信息的,例如Java类的继承信息,成员方法等信息。同时JVM还有一种类型来封装对oop类型的行为-handle。
2、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、oop
class 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)、oopDesc
class 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、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 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)、InstanceKlass
class 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_tlab
HeapWord* 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_allocation
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;
}
可以看到这里会使用锁。
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::allocate
HeapWord* 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
的空间申请创建的过程这里流程的梳理。
标签:return,及类,对类,allocate,JVM,klass,NULL,class,size 来源: https://blog.csdn.net/qq_25179481/article/details/114495140