前言
classloader 相关的类
在分析 classloader 流程之前,我们先看一些相关类(数据结构)
ClassFileStream
Input stream for reading .class file
The entire input stream is present in a buffer allocated by the caller.
The caller is responsible for deallocating the buffer and for using ResourceMarks appropriately when constructing streams
ClassFileStream 用于读取 .class 文件,内部使用一个字节缓冲区,_buffer_start 指向缓冲区开始位置,_buffer_end 指向缓冲区结束位置 + 1,这样 _buffer_end - buffer_start 可以得到缓冲区当前大小,_current 指向缓冲区当前位置
// classFileStream.hpp
private:
const u1* const _buffer_start; // Buffer bottom
const u1* const _buffer_end; // Buffer top (one past last element)
mutable const u1* _current; // Current buffer position
const char* const _source; // Source of stream (directory name, ZIP/JAR archive name)
ClassFileStream 提供了一些方法 get_uX(X = 1,2,4,8)分别从缓冲区读取 X 字节数据
// Read u1 from stream
u1 get_u1(TRAPS) const;
u1 get_u1_fast() const {
return *_current++;
}
// Read u2 from stream
u2 get_u2(TRAPS) const;
u2 get_u2_fast() const {
u2 res = Bytes::get_Java_u2((address)_current);
_current += 2;
return res;
}
// Read u4 from stream
u4 get_u4(TRAPS) const;
u4 get_u4_fast() const {
u4 res = Bytes::get_Java_u4((address)_current);
_current += 4;
return res;
}
// Read u8 from stream
u8 get_u8(TRAPS) const;
u8 get_u8_fast() const {
u8 res = Bytes::get_Java_u8((address)_current);
_current += 8;
return res;
}
ClassFileParser
ClassFileParser 类用于解析 class 文件,关于 class 文件的格式可以参考 网上的文章. 创建 ClassFileParser 对象时,ClassFileParser 构造函数被调用,里面调用了 parse_stream 函数解析 class file stream
// classFileParser.cpp
ClassFileParser::ClassFileParser(ClassFileStream* stream,
Symbol* name,
ClassLoaderData* loader_data,
Handle protection_domain,
const InstanceKlass* host_klass,
GrowableArray<Handle>* cp_patches,
Publicity pub_level,
TRAPS) :
... {
...
parse_stream(stream, CHECK);
post_process_parsed_stream(stream, _cp, CHECK);
}
ConstantPool(常量池)
常量池存放了文字字符串,数值常量,类名,字段名,方法名等信息. 可以使用 jdk 提供的 javap 工具 dump class 文件中的常量池
$ javap -v ~/tmp/java/Main.class
Classfile /home/xingpingz/tmp/java/Main.class
Last modified 2017-2-15; size 506 bytes
MD5 checksum 9a37f28b3e5951ab9ca14d99ea98d7c4
Compiled from "Main.java"
public class Main
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #8.#24 // java/lang/Object."<init>":()V
#2 = Class #25 // Main
#3 = Methodref #2.#24 // Main."<init>":()V
#4 = Methodref #26.#27 // java/lang/Thread.currentThread:()Ljava/lang/Thread;
#5 = Long 3000l
#7 = Methodref #26.#28 // java/lang/Thread.sleep:(J)V
#8 = Class #29 // java/lang/Object
#9 = Utf8 field1
#10 = Utf8 I
#11 = Utf8 field2
#12 = Utf8 <init>
#13 = Utf8 ()V
#14 = Utf8 Code
#15 = Utf8 LineNumberTable
#16 = Utf8 main
#17 = Utf8 ([Ljava/lang/String;)V
#18 = Utf8 StackMapTable
#19 = Class #25 // Main
#20 = Utf8 Exceptions
#21 = Class #30 // java/lang/Exception
#22 = Utf8 SourceFile
#23 = Utf8 Main.java
#24 = NameAndType #12:#13 // "<init>":()V
#25 = Utf8 Main
#26 = Class #31 // java/lang/Thread
#27 = NameAndType #32:#33 // currentThread:()Ljava/lang/Thread;
#28 = NameAndType #34:#35 // sleep:(J)V
#29 = Utf8 java/lang/Object
#30 = Utf8 java/lang/Exception
#31 = Utf8 java/lang/Thread
#32 = Utf8 currentThread
#33 = Utf8 ()Ljava/lang/Thread;
#34 = Utf8 sleep
#35 = Utf8 (J)V
创建常量池
ConstantPool 是通过静态工厂方法 allocate 创建的,常量池大小由 length 参数给出,tags "数组"(Array)用于存储常量的 tag(标记 or 类型),ConstantPool::size 函数根据 length 计算待分配的内存空间大小
// constantPool.cpp
ConstantPool* ConstantPool::allocate(ClassLoaderData* loader_data, int length, TRAPS) {
// Tags are RW but comment below applies to tags also.
Array<u1>* tags = MetadataFactory::new_writeable_array<u1>(loader_data, length, 0, CHECK_NULL);
int size = ConstantPool::size(length);
// CDS considerations:
// Allocate read-write but may be able to move to read-only at dumping time
// if all the klasses are resolved. The only other field that is writable is
// the resolved_references array, which is recreated at startup time.
// But that could be moved to InstanceKlass (although a pain to access from
// assembly code). Maybe it could be moved to the cpCache which is RW.
return new (loader_data, size, false, MetaspaceObj::ConstantPoolType, THREAD) ConstantPool(tags);
}
我们来看看 ConstantPool::size 函数,它返回 ConstantPool 类自身占用的大小以及 length * wordSize(CPU 字长),额外分配的内存空间用于存储常量池中的常量(紧挨在 ConstantPool 对象自身占用的内存之后)
// constantPool.hpp
// Sizing (in words)
static int header_size() { return sizeof(ConstantPool)/wordSize; }
static int size(int length) { return align_metadata_size(header_size() + length); }
CPSlot
Most of the constant pool entries are written during class parsing, which is safe
解析 class 文件的时候,大部分的常量池 entry 都会被写入 ConstantPool
For klass types, the constant pool entry is modified when the entry is resolved. If a klass constant pool entry is read without a lock, only the resolved state guarantees that the entry in the constant pool is a klass object and not a Symbol*.
对于 kclass 类型的 constant pool entry,
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。