文章目录
class文件
主要是classFileParser.cpp里面的parse_stream方法
MagicNumber
classFileParser.cpp
u4:众所周知的CAFEBABE,如果不是则虚拟机拒绝加载.这个常量定义在classFileParser.cpp中
#define JAVA_CLASSFILE_MAGIC 0xCAFEBABE
至于使用则是在parse_stream方法(该方法在ClassParser的构造方法中调用)中
void ClassFileParser::parse_stream(const ClassFileStream* const stream,
TRAPS) {
assert(stream != NULL, "invariant");
assert(_class_name != NULL, "invariant");
// BEGIN STREAM PARSING
stream->guarantee_more(8, CHECK); // magic, major, minor
// Magic value
const u4 magic = stream->get_u4_fast();
guarantee_property(magic == JAVA_CLASSFILE_MAGIC,
"Incompatible magic value %u in class file %s",
magic, CHECK);
大端和小端
Little-Endian:低位字节排在内存的低地址端,高位字节排在内存的高地址端(书写中左边是高位例如12中1是高位)
Big-Endian:
网络字节序:TCP/IP各层使用Big-Endian
JVM:Big-Endian,所以class文件是遵循Big-Endian的,但是C/C++一般是追随环境的,所以在读取class文件时需要转换.
在整个流的读取过程中都会遇到平台相关问题,这里以cafebabe的读取为例,则就需要深入get_u4_fase(),下面代码来自classFileStream.hpp,至于为什么带上fast则是因为这里没有做校验(u1和u2有做校验的非fast版本,但是u4只有fast版本)
// Read u4 from stream
u4 get_u4_fast() const {
u4 res = Bytes::get_Java_u4((address)_current);
_current += 4;
return res;
}
至于get_Java_u4则在不同的CPU下有所不同,以下是几种示例(这些代码来自不同的hpp文件)
// arm
static inline u4 get_Java_u4(address p) {
return u4(p[0]) << 24 |
u4(p[1]) << 16 |
u4(p[2]) << 8 |
u4(p[3]);
}
//aarch64
static inline u4 get_Java_u4(address p) { return swap_u4(get_native_u4(p)); }
static inline u4 get_native_u4(address p) { return *(u4*)p; }
static inline u4 swap_u4(u4 x); // compiler-dependent implementation
//linux
inline u4 Bytes::swap_u4(u4 x) {
return bswap_32(x);
}
inline static u4 bswap_32(u4 x) {
return ((x & 0xFF) << 24) |
((x & 0xFF00) << 8) |
((x >> 8) & 0xFF00) |
((x >> 24) & 0xFF);
}
//bytes_linux_x86.inline.hpp
inline u4 Bytes::swap_u4(u4 x) {
#ifdef AMD64
return bswap_32(x);
#else
u4 ret;
__asm__ __volatile__ (
"bswap %0"
:"=r" (ret) // output : register 0 => ret
:"0" (x) // input : x => register 0
:"0" // clobbered register
);
return ret;
#endif // AMD64
}
//x86
static inline u4 get_Java_u4(address p) { return get_Java<u4>(p); }
注意这里的每一步调用都和平台(包括CPU&OS)相关,不同的平台调用的是不同代码,不过我个人认为最后bswap_32还可以再优化一下.
Version
classFileParser.cpp
依次是两个u2,分别是minor_version和major_version
// Version numbers
_minor_version = stream->get_u2_fast();
_major_version = stream->get_u2_fast();
if (DumpSharedSpaces && _major_version < JAVA_6_VERSION) {
ResourceMark rm;
warning("Pre JDK 6 class not supported by CDS: %u.%u %s",
_major_version, _minor_version, _class_name->as_C_string());
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_UnsupportedClassVersionError(),
"Unsupported major.minor version for dump time %u.%u",
_major_version,
_minor_version);
}
// Check version numbers - we check this even with verifier off
verify_class_version(_major_version, _minor_version, _class_name, CHECK);
其中真正执行的代码检查的verify_class_version代码如下
// A legal major_version.minor_version must be one of the following:
// Major_version >= 45 and major_version < 56, any minor_version.
// Major_version >= 56 and major_version <= JVM_CLASSFILE_MAJOR_VERSION and minor_version = 0.
// Major_version = JVM_CLASSFILE_MAJOR_VERSION and minor_version = 65535 and --enable-preview is present.
static void verify_class_version(u2 major, u2 minor, Symbol* class_name, TRAPS){
ResourceMark rm(THREAD);
const u2 max_version = JVM_CLASSFILE_MAJOR_VERSION;
if (major < JAVA_MIN_SUPPORTED_VERSION) {
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_UnsupportedClassVersionError(),
"%s (class file version %u.%u) was compiled with an invalid major version",
class_name->as_C_string(), major, minor);
return;
}
if (major > max_version) {
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_UnsupportedClassVersionError(),
"%s has been compiled by a more recent version of the Java Runtime (class file version %u.%u), "
"this version of the Java Runtime only recognizes class file versions up to %u.0",
class_name->as_C_string(), major, minor, JVM_CLASSFILE_MAJOR_VERSION);
return;
}
if (major < JAVA_12_VERSION || minor == 0) {
return;
}
if (minor == JAVA_PREVIEW_MINOR_VERSION) {
if (major != max_version) {
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_UnsupportedClassVersionError(),
"%s (class file version %u.%u) was compiled with preview features that are unsupported. "
"This version of the Java Runtime only recognizes preview features for class file version %u.%u",
class_name->as_C_string(), major, minor, JVM_CLASSFILE_MAJOR_VERSION, JAVA_PREVIEW_MINOR_VERSION);
return;
}
if (!Arguments::enable_preview()) {
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_UnsupportedClassVersionError(),
"Preview features are not enabled for %s (class file version %u.%u). Try running with '--enable-preview'",
class_name->as_C_string(), major, minor);
return;
}
} else { // minor != JAVA_PREVIEW_MINOR_VERSION
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_UnsupportedClassVersionError(),
"%s (class file version %u.%u) was compiled with an invalid non-zero minor version",
class_name->as_C_string(), major, minor);
}
}
常见version
- 49:1.5
- 50:1.6
- 51:1.7
Constant_pool
classFileParser.cpp
先读取一个u2类型的变量,作为constant_pool_size(代码中的cp_size)
stream->guarantee_more(3, CHECK); // length, first cp tag
u2 cp_size = stream->get_u2_fast();
guarantee_property(
cp_size >= 1, "Illegal constant pool size %u in class file %s",
cp_size, CHECK);
_orig_cp_size = cp_size;
if (is_hidden()) { // Add a slot for hidden class name.
assert(_max_num_patched_klasses == 0, "Sanity check");
cp_size++;
} else {
if (int(cp_size) + _max_num_patched_klasses > 0xffff) {
THROW_MSG(vmSymbols::java_lang_InternalError(), "not enough space for patched classes");
}
cp_size += _max_num_patched_klasses;
}
其中*is_hidden()*是读取了classFileParser.hpp中的_is_hidden变量,至于这个变量貌似在ClassFileParser的构造函数中来自ClassLoader
接下来才是读取constant_pool[cp_size]
_cp = ConstantPool::allocate(_loader_data,cp_size,CHECK);
ConstantPool* const cp = _cp;
parse_constant_pool(stream, cp, _orig_cp_size, CHECK);
assert(cp_size == (const u2)cp->length(), "invariant");
可以看出来这里是调用了ConstantPool里面的allocate来获得cp,然后在使用parse_constant_pool来逐个读取,相关代码如下
ConstantPool* ConstantPool::allocate(ClassLoaderData* loader_data, int length, TRAPS) {
Array<u1>* tags = MetadataFactory::new_array<u1>(loader_data, length, 0, CHECK_NULL);
int size = ConstantPool::size(length);
return new (loader_data, size, MetaspaceObj::ConstantPoolType, THREAD) ConstantPool(tags);
}
上面的size方法主要是做了对齐的操作
注意:这里最大的一个坑是class文件里cp_entries的实际长度是cp_size-1,因为第0个cp_entries不存在
Constant Pool Entries
这些常量名称都是JVM_CONSTANT_*格式,下面统一把前后缀去掉,其中tag长度为u1,length为u2,index为u2,bytes的长度根据下面表确定(这些定义来自于classfile_constants.h.template),另外注意到injector.h里也有类似定义,但是根据说明injector.h是用于classFile转换
类型 | tag | length | bytes | index(指向…的索引项) | index(指向…的索引项) |
---|---|---|---|---|---|
Utf8 | 1 | u2 | 长度length的字符串 | ||
Unicode | 2 | ||||
Integer | 3 | u4 高位在前的int值 | |||
Float | 4 | u4 高位在前的float | |||
Long | 5 | u8 高位在前的long | |||
Double | 6 | u8 高位在前的double | |||
Class | 7 | 全限定名常量项 | |||
String | 8 | 字符串字面量 | |||
Fieldref | 9 | 声明字段的Class | 字段描述符NameAndType | ||
Methodref | 10 | 声明方法的类Class | 名称及类型NameAndType | ||
InterfaceMethodref | 11 | 声明方法的接口Class | NameAndType | ||
NameAndType | 12 | 字段/方法名称常量项 | 字段/方法描述符常量项 | ||
MethodHandle | 15 | ||||
MethodType | 16 | ||||
Dynamic | 17 | ||||
InvokeDynamic | 18 | ||||
Module | 19 | ||||
Package | 20 | ||||
ExternalMax | 20 |
对于一个java中声明为int的变量,在class文件中它的类型也将对应一个String,并且值是I,如果用16位表示则是0x49,没想到jvm没有对这些基本类型做出区别于对象的处理.另外对于类的字符串,很多是以L开头.
Access_flag
classFileParser.cpp
// 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;
}
if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) {
// Set abstract bit for old class files for backward compatibility
flags |= JVM_ACC_ABSTRACT;
}
verify_legal_class_modifiers(flags, CHECK);
short bad_constant = class_bad_constant_seen();
if (bad_constant != 0) {
// Do not throw CFE until after the access_flags are checked because if
// ACC_MODULE is set in the access flags, then NCDFE must be thrown, not CFE.
classfile_parse_error("Unknown constant tag %u in class file %s", bad_constant, CHECK);
}
_access_flags.set_flags(flags);
其中用到的JVM_RECOGNIZED_CLASS_MODIFIERS来自jvm.h
#define JVM_RECOGNIZED_CLASS_MODIFIERS (JVM_ACC_PUBLIC | \
JVM_ACC_FINAL | \
JVM_ACC_SUPER | \
JVM_ACC_INTERFACE | \
JVM_ACC_ABSTRACT | \
JVM_ACC_ANNOTATION | \
JVM_ACC_ENUM | \
JVM_ACC_SYNTHETIC)
其中JVM_ACC_SYNTHETIC用来标识这个类并非用户代码产生.另外从java1.2开始都会有JVM_ACC_SUPER
This_class
首先读取一个u2的值用于指向constant_pool中的位置,然后就是各种校验,详细代码如下
// This class and superclass
_this_class_index = stream->get_u2_fast();
check_property(
valid_cp_range(_this_class_index, cp_size) &&
cp->tag_at(_this_class_index).is_unresolved_klass(),
"Invalid this class index %u in constant pool in class file %s",
_this_class_index, CHECK);
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");
// Don't need to check whether this class name is legal or not.
// It has been checked when constant pool is parsed.
// However, make sure it is not an array type.
if (_need_verify) {
guarantee_property(class_name_in_cp->char_at(0) != JVM_SIGNATURE_ARRAY,
"Bad class name in class file %s",
CHECK);
}
#ifdef ASSERT
// Basic sanity checks
assert(!(_is_hidden && (_unsafe_anonymous_host != NULL)), "mutually exclusive variants");
if (_unsafe_anonymous_host != NULL) {
assert(_class_name == vmSymbols::unknown_class_name(), "A named anonymous class???");
}
if (_is_hidden) {
assert(_class_name != vmSymbols::unknown_class_name(), "hidden classes should have a special name");
}
#endif
// Update the _class_name as needed depending on whether this is a named,
// un-named, hidden or unsafe-anonymous class.
if (_is_hidden) {
assert(_class_name != NULL, "Unexpected null _class_name");
#ifdef ASSERT
if (_need_verify) {
verify_legal_class_name(_class_name, CHECK);
}
#endif
// NOTE: !_is_hidden does not imply "findable" as it could be an old-style
// "hidden" unsafe-anonymous class
// If this is an anonymous class fix up its name if it is in the unnamed
// package. Otherwise, throw IAE if it is in a different package than
// its host class.
} else if (_unsafe_anonymous_host != NULL) {
update_class_name(class_name_in_cp);
fix_unsafe_anonymous_class_name(CHECK);
} else {
// Check if name in class file matches given name
if (_class_name != class_name_in_cp) {
if (_class_name != vmSymbols::unknown_class_name()) {
ResourceMark rm(THREAD);
Exceptions::fthrow(THREAD_AND_LOCATION,
vmSymbols::java_lang_NoClassDefFoundError(),
"%s (wrong name: %s)",
class_name_in_cp->as_C_string(),
_class_name->as_C_string()
);
return;
} else {
// The class name was not known by the caller so we set it from
// the value in the CP.
update_class_name(class_name_in_cp);
}
// else nothing to do: the expected class name matches what is in the CP
}
}
// Verification prevents us from creating names with dots in them, this
// asserts that that's the case.
assert(is_internal_format(_class_name), "external class name format used internally");
if (!is_internal()) {
LogTarget(Debug, class, preorder) lt;
if (lt.is_enabled()){
ResourceMark rm(THREAD);
LogStream ls(lt);
ls.print("%s", _class_name->as_klass_external_name());
if (stream->source() != NULL) {
ls.print(" source: %s", stream->source());
}
ls.cr();
}
#if INCLUDE_CDS
if (DumpLoadedClassList != NULL && stream->source() != NULL && classlist_file->is_open()) {
if (!ClassLoader::has_jrt_entry()) {
warning("DumpLoadedClassList and CDS are not supported in exploded build");
DumpLoadedClassList = NULL;
} else if (SystemDictionaryShared::is_sharing_possible(_loader_data) &&
!_is_hidden &&
_unsafe_anonymous_host == NULL) {
// Only dump the classes that can be stored into CDS archive.
// Hidden and unsafe anonymous classes such as generated LambdaForm classes are also not included.
oop class_loader = _loader_data->class_loader();
ResourceMark rm(THREAD);
bool skip = false;
if (class_loader == NULL || SystemDictionary::is_platform_class_loader(class_loader)) {
// For the boot and platform class loaders, skip classes that are not found in the
// java runtime image, such as those found in the --patch-module entries.
// These classes can't be loaded from the archive during runtime.
if (!stream->from_boot_loader_modules_image() && strncmp(stream->source(), "jrt:", 4) != 0) {
skip = true;
}
if (class_loader == NULL && ClassLoader::contains_append_entry(stream->source())) {
// .. but don't skip the boot classes that are loaded from -Xbootclasspath/a
// as they can be loaded from the archive during runtime.
skip = false;
}
}
if (skip) {
tty->print_cr("skip writing class %s from source %s to classlist file",
_class_name->as_C_string(), stream->source());
} else {
classlist_file->print_cr("%s", _class_name->as_C_string());
classlist_file->flush();
}
}
}
#endif
}
Super_class
和上面代码类似,同样是通过一个u2来获取在constant_pool中的位置,代码如下
// SUPERKLASS
_super_class_index = stream->get_u2_fast();
_super_klass = parse_super_class(cp,
_super_class_index,
_need_verify,
CHECK);
可以看出来实际通过parse_super_class来完成,代码如下
const InstanceKlass* ClassFileParser::parse_super_class(ConstantPool* const cp,
const int super_class_index,const bool need_verify,TRAPS) {
assert(cp != NULL, "invariant");
const InstanceKlass* super_klass = NULL;
if (super_class_index == 0) {
check_property(_class_name == vmSymbols::java_lang_Object(),
"Invalid superclass index %u in class file %s",
super_class_index,
CHECK_NULL);
} else {
check_property(valid_klass_reference_at(super_class_index),
"Invalid superclass index %u in class file %s",
super_class_index,
CHECK_NULL);
// The class name should be legal because it is checked when parsing constant pool.
// However, make sure it is not an array type.
bool is_array = false;
if (cp->tag_at(super_class_index).is_klass()) {
super_klass = InstanceKlass::cast(cp->resolved_klass_at(super_class_index));
if (need_verify)
is_array = super_klass->is_array_klass();
} else if (need_verify) {
is_array = (cp->klass_name_at(super_class_index)->char_at(0) == JVM_SIGNATURE_ARRAY);
}
if (need_verify) {
guarantee_property(!is_array,
"Bad superclass name in class file %s", CHECK_NULL);
}
}
return super_klass;
}
上面的第一个if分支是为了java.lang.Object做了专门处理,它可以没有父类(super_class_index==0),其他都需要检查父类的合法性.另外就是对是否是数组做了一些检查.
Interfaces
// Interfaces
_itfs_len = stream->get_u2_fast();
parse_interfaces(stream,_itfs_len,cp,&_has_nonstatic_concrete_methods,CHECK);
assert(_local_interfaces != NULL, "invariant");
具体干活的parse_interfaces函数代码如下,其中我对于assert(itfs_len > 0, “only called for len>0”);这一行代码也是惊呆了.
// Side-effects: populates the _local_interfaces field
void ClassFileParser::parse_interfaces(const ClassFileStream* const stream,
const int itfs_len,
ConstantPool* const cp,
bool* const has_nonstatic_concrete_methods,
TRAPS) {
assert(stream != NULL, "invariant");
assert(cp != NULL, "invariant");
assert(has_nonstatic_concrete_methods != NULL, "invariant");
if (itfs_len == 0) {
_local_interfaces = Universe::the_empty_instance_klass_array();
} else {
assert(itfs_len > 0, "only called for len>0");
_local_interfaces = MetadataFactory::new_array<InstanceKlass*>(_loader_data, itfs_len, NULL, CHECK);
int index;
for (index = 0; index < itfs_len; index++) {
const u2 interface_index = stream->get_u2(CHECK);
Klass* interf;
check_property(
valid_klass_reference_at(interface_index),
"Interface name has bad constant pool index %u in class file %s",
interface_index, CHECK);
if (cp->tag_at(interface_index).is_klass()) {
interf = cp->resolved_klass_at(interface_index);
} else {
Symbol* const unresolved_klass = cp->klass_name_at(interface_index);
// Don't need to check legal name because it's checked when parsing constant pool.
// But need to make sure it's not an array type.
guarantee_property(unresolved_klass->char_at(0) != JVM_SIGNATURE_ARRAY,
"Bad interface name in class file %s", CHECK);
// Call resolve_super so classcircularity is checked
interf = SystemDictionary::resolve_super_or_fail(
_class_name,
unresolved_klass,
Handle(THREAD, _loader_data->class_loader()),
_protection_domain,
false,
CHECK);
}
if (!interf->is_interface()) {
THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(),
err_msg("class %s can not implement %s, because it is not an interface (%s)",
_class_name->as_klass_external_name(),
interf->external_name(),
interf->class_in_module_of_loader()));
}
if (InstanceKlass::cast(interf)->has_nonstatic_concrete_methods()) {
*has_nonstatic_concrete_methods = true;
}
_local_interfaces->at_put(index, InstanceKlass::cast(interf));
}
if (!_need_verify || itfs_len <= 1) {
return;
}
// Check if there's any duplicates in interfaces
ResourceMark rm(THREAD);
NameSigHash** interface_names = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD,
NameSigHash*,
HASH_ROW_SIZE);
initialize_hashtable(interface_names);
bool dup = false;
const Symbol* name = NULL;
{
debug_only(NoSafepointVerifier nsv;)
for (index = 0; index < itfs_len; index++) {
const InstanceKlass* const k = _local_interfaces->at(index);
name = k->name();
// If no duplicates, add (name, NULL) in hashtable interface_names.
if (!put_after_lookup(name, NULL, interface_names)) {
dup = true;
break;
}
}
}
if (dup) {
classfile_parse_error("Duplicate interface name \"%s\" in class file %s",
name->as_C_string(), CHECK);
}
}
}
Fields
classFileParser.cpp
// Fields (offsets are filled in later)
_fac = new FieldAllocationCount();
parse_fields(stream,
_access_flags.is_interface(),
_fac,
cp,
cp_size,
&_java_fields_count,
CHECK);
assert(_fields != NULL, "invariant");
其中FieldAllocationCount代码如下,看上去最多只能有65535个field
class ClassFileParser::FieldAllocationCount : public ResourceObj {
public:
u2 count[MAX_FIELD_ALLOCATION_TYPE];
FieldAllocationCount() {
for (int i = 0; i < MAX_FIELD_ALLOCATION_TYPE; i++) {
count[i] = 0;
}
}
FieldAllocationType update(bool is_static, BasicType type) {
FieldAllocationType atype = basic_type_to_atype(is_static, type);
if (atype != BAD_ALLOCATION_TYPE) {
// Make sure there is no overflow with injected fields.
assert(count[atype] < 0xFFFF, "More than 65535 fields");
count[atype]++;
}
return atype;
}
};
下面是parse_fields的代码,可以看出你里面有很多防御性代码
// Side-effects: populates the _fields, _fields_annotations,
// _fields_type_annotations fields
void ClassFileParser::parse_fields(const ClassFileStream* const cfs,
bool is_interface,
FieldAllocationCount* const fac,
ConstantPool* cp,
const int cp_size,
u2* const java_fields_count_ptr,
TRAPS) {
assert(cfs != NULL, "invariant");
assert(fac != NULL, "invariant");
assert(cp != NULL, "invariant");
assert(java_fields_count_ptr != NULL, "invariant");
assert(NULL == _fields, "invariant");
assert(NULL == _fields_annotations, "invariant");
assert(NULL == _fields_type_annotations, "invariant");
cfs->guarantee_more(2, CHECK); // length
const u2 length = cfs->get_u2_fast();
*java_fields_count_ptr = length;
int num_injected = 0;
const InjectedField* const injected = JavaClasses::get_injected(_class_name,
&num_injected);
const int total_fields = length + num_injected;
// The field array starts with tuples of shorts
// [access, name index, sig index, initial value index, byte offset].
// A generic signature slot only exists for field with generic
// signature attribute. And the access flag is set with
// JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE for that field. The generic
// signature slots are at the end of the field array and after all
// other fields data.
//
// f1: [access, name index, sig index, initial value index, low_offset, high_offset]
// f2: [access, name index, sig index, initial value index, low_offset, high_offset]
// ...
// fn: [access, name index, sig index, initial value index, low_offset, high_offset]
// [generic signature index]
// [generic signature index]
// ...
//
// Allocate a temporary resource array for field data. For each field,
// a slot is reserved in the temporary array for the generic signature
// index. After parsing all fields, the data are copied to a permanent
// array and any unused slots will be discarded.
ResourceMark rm(THREAD);
u2* const fa = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD,
u2,
total_fields * (FieldInfo::field_slots + 1));
// The generic signature slots start after all other fields' data.
int generic_signature_slot = total_fields * FieldInfo::field_slots;
int num_generic_signature = 0;
for (int n = 0; n < length; n++) {
// access_flags, name_index, descriptor_index, attributes_count
cfs->guarantee_more(8, CHECK);
AccessFlags access_flags;
const jint flags = cfs->get_u2_fast() & JVM_RECOGNIZED_FIELD_MODIFIERS;
verify_legal_field_modifiers(flags, is_interface, CHECK);
access_flags.set_flags(flags);
const u2 name_index = cfs->get_u2_fast();
check_property(valid_symbol_at(name_index),
"Invalid constant pool index %u for field name in class file %s",
name_index, CHECK);
const Symbol* const name = cp->symbol_at(name_index);
verify_legal_field_name(name, CHECK);
const u2 signature_index = cfs->get_u2_fast();
check_property(valid_symbol_at(signature_index),
"Invalid constant pool index %u for field signature in class file %s",
signature_index, CHECK);
const Symbol* const sig = cp->symbol_at(signature_index);
verify_legal_field_signature(name, sig, CHECK);
u2 constantvalue_index = 0;
bool is_synthetic = false;
u2 generic_signature_index = 0;
const bool is_static = access_flags.is_static();
FieldAnnotationCollector parsed_annotations(_loader_data);
const u2 attributes_count = cfs->get_u2_fast();
if (attributes_count > 0) {
parse_field_attributes(cfs,
attributes_count,
is_static,
signature_index,
&constantvalue_index,
&is_synthetic,
&generic_signature_index,
&parsed_annotations,
CHECK);
if (parsed_annotations.field_annotations() != NULL) {
if (_fields_annotations == NULL) {
_fields_annotations = MetadataFactory::new_array<AnnotationArray*>(
_loader_data, length, NULL,
CHECK);
}
_fields_annotations->at_put(n, parsed_annotations.field_annotations());
parsed_annotations.set_field_annotations(NULL);
}
if (parsed_annotations.field_type_annotations() != NULL) {
if (_fields_type_annotations == NULL) {
_fields_type_annotations =
MetadataFactory::new_array<AnnotationArray*>(_loader_data,
length,
NULL,
CHECK);
}
_fields_type_annotations->at_put(n, parsed_annotations.field_type_annotations());
parsed_annotations.set_field_type_annotations(NULL);
}
if (is_synthetic) {
access_flags.set_is_synthetic();
}
if (generic_signature_index != 0) {
access_flags.set_field_has_generic_signature();
fa[generic_signature_slot] = generic_signature_index;
generic_signature_slot ++;
num_generic_signature ++;
}
}
FieldInfo* const field = FieldInfo::from_field_array(fa, n);
field->initialize(access_flags.as_short(),
name_index,
signature_index,
constantvalue_index);
const BasicType type = cp->basic_type_for_signature_at(signature_index);
// Remember how many oops we encountered and compute allocation type
const FieldAllocationType atype = fac->update(is_static, type);
field->set_allocation_type(atype);
// After field is initialized with type, we can augment it with aux info
if (parsed_annotations.has_any_annotations()) {
parsed_annotations.apply_to(field);
if (field->is_contended()) {
_has_contended_fields = true;
}
}
}
int index = length;
if (num_injected != 0) {
for (int n = 0; n < num_injected; n++) {
// Check for duplicates
if (injected[n].may_be_java) {
const Symbol* const name = injected[n].name();
const Symbol* const signature = injected[n].signature();
bool duplicate = false;
for (int i = 0; i < length; i++) {
const FieldInfo* const f = FieldInfo::from_field_array(fa, i);
if (name == cp->symbol_at(f->name_index()) &&
signature == cp->symbol_at(f->signature_index())) {
// Symbol is desclared in Java so skip this one
duplicate = true;
break;
}
}
if (duplicate) {
// These will be removed from the field array at the end
continue;
}
}
// Injected field
FieldInfo* const field = FieldInfo::from_field_array(fa, index);
field->initialize(JVM_ACC_FIELD_INTERNAL,
injected[n].name_index,
injected[n].signature_index,
0);
const BasicType type = Signature::basic_type(injected[n].signature());
// Remember how many oops we encountered and compute allocation type
const FieldAllocationType atype = fac->update(false, type);
field->set_allocation_type(atype);
index++;
}
}
assert(NULL == _fields, "invariant");
_fields =
MetadataFactory::new_array<u2>(_loader_data,
index * FieldInfo::field_slots + num_generic_signature,
CHECK);
// Sometimes injected fields already exist in the Java source so
// the fields array could be too long. In that case the
// fields array is trimed. Also unused slots that were reserved
// for generic signature indexes are discarded.
{
int i = 0;
for (; i < index * FieldInfo::field_slots; i++) {
_fields->at_put(i, fa[i]);
}
for (int j = total_fields * FieldInfo::field_slots;
j < generic_signature_slot; j++) {
_fields->at_put(i++, fa[j]);
}
assert(_fields->length() == i, "");
}
if (_need_verify && length > 1) {
// Check duplicated fields
ResourceMark rm(THREAD);
NameSigHash** names_and_sigs = NEW_RESOURCE_ARRAY_IN_THREAD(
THREAD, NameSigHash*, HASH_ROW_SIZE);
initialize_hashtable(names_and_sigs);
bool dup = false;
const Symbol* name = NULL;
const Symbol* sig = NULL;
{
debug_only(NoSafepointVerifier nsv;)
for (AllFieldStream fs(_fields, cp); !fs.done(); fs.next()) {
name = fs.name();
sig = fs.signature();
// If no duplicates, add name/signature in hashtable names_and_sigs.
if (!put_after_lookup(name, sig, names_and_sigs)) {
dup = true;
break;
}
}
}
if (dup) {
classfile_parse_error("Duplicate field name \"%s\" with signature \"%s\" in class file %s",
name->as_C_string(), sig->as_klass_external_name(), CHECK);
}
}
}
fields的结构
类型 | 名称 | 数量 | 说明 |
---|---|---|---|
u2 | access_flags | 1 | 访问标识 |
u2 | name_index | 1 | 简单名称引用 |
u2 | descriptor_index | 1 | 类型引用信息 |
u2 | attribute_count | 1 | |
attribute_info | attributes | attribute_count |
标识字符
标识字符 | 含义 |
---|---|
B | byte |
C | char |
D | double |
F | float |
I | int |
J | long |
S | short |
Z | boolean |
V | void |
L* | 对象 |
如果是数组则会在前面加上左中括号,例如[I标识int[]
Methods
classFileParser.cpp
// Methods
AccessFlags promoted_flags;
parse_methods(stream,
_access_flags.is_interface(),
&promoted_flags,
&_has_final_method,
&_declares_nonstatic_concrete_methods,
CHECK);
assert(_methods != NULL, "invariant");
// promote flags from parse_methods() to the klass' flags
_access_flags.add_promoted_flags(promoted_flags.as_int());
if (_declares_nonstatic_concrete_methods) {
_has_nonstatic_concrete_methods = true;
}
// The promoted_flags parameter is used to pass relevant access_flags
// from the methods back up to the containing klass. These flag values
// are added to klass's access_flags.
// Side-effects: populates the _methods field in the parser
void ClassFileParser::parse_methods(const ClassFileStream* const cfs,
bool is_interface,
AccessFlags* promoted_flags,
bool* has_final_method,
bool* declares_nonstatic_concrete_methods,
TRAPS) {
assert(cfs != NULL, "invariant");
assert(promoted_flags != NULL, "invariant");
assert(has_final_method != NULL, "invariant");
assert(declares_nonstatic_concrete_methods != NULL, "invariant");
assert(NULL == _methods, "invariant");
cfs->guarantee_more(2, CHECK); // length
const u2 length = cfs->get_u2_fast();
if (length == 0) {
_methods = Universe::the_empty_method_array();
} else {
_methods = MetadataFactory::new_array<Method*>(_loader_data,
length,
NULL,
CHECK);
for (int index = 0; index < length; index++) {
Method* method = parse_method(cfs,
is_interface,
_cp,
promoted_flags,
CHECK);
if (method->is_final()) {
*has_final_method = true;
}
// declares_nonstatic_concrete_methods: declares concrete instance methods, any access flags
// used for interface initialization, and default method inheritance analysis
if (is_interface && !(*declares_nonstatic_concrete_methods)
&& !method->is_abstract() && !method->is_static()) {
*declares_nonstatic_concrete_methods = true;
}
_methods->at_put(index, method);
}
if (_need_verify && length > 1) {
// Check duplicated methods
ResourceMark rm(THREAD);
NameSigHash** names_and_sigs = NEW_RESOURCE_ARRAY_IN_THREAD(
THREAD, NameSigHash*, HASH_ROW_SIZE);
initialize_hashtable(names_and_sigs);
bool dup = false;
const Symbol* name = NULL;
const Symbol* sig = NULL;
{
debug_only(NoSafepointVerifier nsv;)
for (int i = 0; i < length; i++) {
const Method* const m = _methods->at(i);
name = m->name();
sig = m->signature();
// If no duplicates, add name/signature in hashtable names_and_sigs.
if (!put_after_lookup(name, sig, names_and_sigs)) {
dup = true;
break;
}
}
}
if (dup) {
classfile_parse_error("Duplicate method name \"%s\" with signature \"%s\" in class file %s",
name->as_C_string(), sig->as_klass_external_name(), CHECK);
}
}
}
}
结构
method的结构与field类似,但是其access_flags的可选性显然与field不同
参数&返回
用描述符描述方法时,按照先参数列表,后返回值的顺序描述,参数列表按照参数的严格顺序放在一组"()“之内,如方法"String getAll(int id,String name)“的描述符为”(I,Ljava/lang/String;)Ljava/lang/String”
void <clinit >()
这个是由编译器自动添加的方法,用于初始化static变量和执行static块
<init>
构造方法也是方法,会有编译器自动产生
Attributes
在class文件中,属性表,方法表都可以保护眼自己的属性表集合,用于描述某些场景的专有信息.
与class文件中其他数据项对长度,顺序,格式的严格要求不同,属性表集合不要求其中包含的属性表具有严格的顺序,并且只要属性的名称不与已有的属性名称重复,任何人实现的编译器都可以向属性表写入自己定义的属性信息.虚拟机在运行时会忽略不能识别的属性.
常见属性表
以下是JVM规定的一些属性,但是根据vmSymbols来看目前OpenJDK支持的更多
Code
位置:方法表
含义:java编译成的字节码指令,有方法体的方法才会有Code
Code属性的结构
类型 | 名称 | 数量 | 含义 |
---|---|---|---|
u2 | attribute_name_index | 1 | |
u4 | attribute_length | 1 | |
u2 | max_stack | 1 | 虚拟机运行时根据这个值分配栈的操作数栈深度 |
u2 | max_locals | 1 | 局部变量所需的slot数量,不同变量可以服用slot |
u4 | code_length | 1 | 编译后字节码长度 |
u1 | code | code_length | 字节码指令 |
u2 | exception_table_length | 1 | |
exception_info | exception_table | exception_table_length | |
u2 | attributes_count | 1 | |
attribute_info | attributes | attributes_count |
虽然上面的code_length定义为u4,但是按照JVM规范一个方法不能超过65535字节
另外Code里涉及到指令集,参见后面的揭秘java虚拟机(三)JVM指令集
ConstantValue
位置:字段表
含义:static定义的常量值(这是JVM要求的,但是javac一般会加上final这一限制)
ConstantValue属性结构
类型 | 名称 | 数量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | constantvalue_index | 1 |
其中attribute_length的值恒为0x0000002,另外ConstantValue只支持int和String
Deprecated
位置:类文件,字段表,方法表
含义:被声明为deprecated的类,字段,方法
Exceptions
位置:方法表
含义:方法声明的受检异常
类型 | 名称 | 数量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | number_of_exceptions | 1 |
u2 | exception_index_table | number_of_exceptions |
InnerClasses
位置:类文件
含义:内部类
InnerClasses属性结构表
类型 | 名称 | 数量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | number_of_classes | 1 |
inner_classes_info | inner_classes | number_of_classes |
inner_classes_info表结构
类型 | 名称 | 数量 |
---|---|---|
u2 | inner_class_info_index | 1 |
u2 | outer_class_index | 1 |
u2 | inner_name_index | 1 |
u2 | inner_class_access_flags | 1 |
书中这个地方写出了,最后一行按照源代码以及语义可以知道应该是inner_class_access_flags,而书中写的是inner_name_access_flags,并且下面还照抄了.
LineNumberTable
位置:Code
含义:java源码行号和字节码指令对应关系
在这里,书中再一次错误,不过这是个拼写错误,但是被大量CV或者是批量替换
LineNumberTable属性结构表
类型 | 名称 | 数量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | line_number_table_length | 1 |
line_number_info | line_number_table | line_number_table_length |
line_number_info属性结构表
类型 | 名称 | 数量 | 说明 |
---|---|---|---|
u2 | sart_pc | 1 | 字节码行号 |
u2 | line_number | 1 | java源代码行号 |
这个是用于端点调试和打印异常时使用
LocalVariableTable
位置:Code
含义:方法的局部变量
根据Option.java可以知道在class文件中是否生成LocalVariableTable可以在javac中通过-g:none和-g:vars来控制关闭或生成,默认是不生成.
LocalVariableTable属性结构表
类型 | 名称 | 数量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | local_variable_table_length | 1 |
local_variable_info | local_variable_table | local_variable_table_length |
SourceFile
位置:类文件
含义:源文件名称
还是参见Option.java知道这个通过none或source来控制是否生成
SourceFile属性结构表
类型 | 名称 | 数量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | sourcefile_index | 1 |
Synthetic
位置:类文件,方法表,字段表
含义:该类/方法/字段由编译器自动生成,但是clinit和init例外
classFileParser.cpp
最后读取attributes并确保stream被读取完毕
// Additional attributes/annotations
_parsed_annotations = new ClassAnnotationCollector();
parse_classfile_attributes(stream, cp, _parsed_annotations, CHECK);
assert(_inner_classes != NULL, "invariant");
// Finalize the Annotations metadata object,
// now that all annotation arrays have been created.
create_combined_annotations(CHECK);
最后是确保stream被读取完毕
// Make sure this is the end of class file stream
guarantee_property(stream->at_eos(),
"Extra bytes at the end of class file %s",
CHECK);
// all bytes in stream read and parsed