第十九章-ClassLoaderData初始化
講解本章先從一張圖開始
眾所周知,Java類的相關(guān)信息都是存儲在元空間中的,但是是怎么存儲的,相信很多讀者是不清楚的,這里就不得不涉及到ClassLoaderDataGraph、classLoader、classLoaderData(簡稱CLD)和Klass的概念及他們四者的關(guān)系,這里簡單描述下他們的概念,具體細(xì)節(jié)放到類加載器那一張來講解。
InstanceKlass(繼承自Klass):每個被加載的類在虛擬機中的表示為一個InstanceKlass
ClassLoaderData:類加載器加載類后,存儲數(shù)據(jù)的對象,也就是說被加載的類最終都存儲在ClassLoaderData指向的地方,一個CLD可以存入很多被加載的類InstanceKlass,多個InstanceKlass之間通過鏈表形式存儲,且鏈表頭永遠(yuǎn)是最新加載的類
ClassLoader:類加載器,每個ClassLoader都有一個CLD
ClassLoaderDataGraph:這是CLD的總?cè)肟?,把所有CLD通過鏈表管理起來
19.1 根加載器CLD的創(chuàng)建
19.1.1 universe.cpp
19.1.1.1 ClassLoaderData::init_null_class_loader_data
static void init_null_class_loader_data() {
// 驗證重復(fù)初始化
assert(_the_null_class_loader_data == NULL, "cannot initialize twice");
assert(ClassLoaderDataGraph::_head == NULL, "cannot initialize twice");
// 創(chuàng)建ClassLoaderData對象,第一個加載器的參數(shù)是NULL,因為在Java中,沒有對根加載器的實現(xiàn),這個是由虛擬機自身來實現(xiàn)加載的,所以相對Java,這是一個NULL,實現(xiàn)看`章節(jié)19.1.2`
_the_null_class_loader_data = new ClassLoaderData((oop)NULL, false, Dependencies());
// 創(chuàng)建完后,賦值給ClassLoaderDataGraph::_head,表示第一個CLD
ClassLoaderDataGraph::_head = _the_null_class_loader_data;
assert(_the_null_class_loader_data->is_the_null_class_loader_data(), "Must be");
if (DumpSharedSpaces) { // 不涉及多Java進程共享,這一步不會走
_the_null_class_loader_data->initialize_shared_metaspaces();
}
}
19.1.2 classLoaderData.cpp
19.1.2.1 ClassLoaderData構(gòu)造函數(shù)
發(fā)現(xiàn)這個構(gòu)造函數(shù),啥也沒做,就是對字段賦初始值
ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool is_anonymous, Dependencies dependencies) :
_class_loader(h_class_loader()),
_is_anonymous(is_anonymous),
// An anonymous class loader data doesn't have anything to keep
// it from being unloaded during parsing of the anonymous class.
// The null-class-loader should always be kept alive.
_keep_alive(is_anonymous || h_class_loader.is_null()),
_metaspace(NULL), _unloading(false), _klasses(NULL),
_claimed(0), _jmethod_ids(NULL), _handles(), _deallocate_list(NULL),
_next(NULL), _dependencies(dependencies),
_metaspace_lock(new Mutex(Monitor::leaf+1, "Metaspace allocation lock", true)) {
JFR_ONLY(INIT_ID(this);)
}
19.2 符號表和字符串表
先了解下什么是符號表,什么是字符串表?
SymbolTable(符號表):符號是指字節(jié)碼中產(chǎn)生的各種元數(shù)據(jù)的utf-8字符表示,所以符號表就是用來存放這些utf-8字符的
StringTable(字符串表):就是存放Java中String對象的,把StringTable表中的數(shù)據(jù)都可以想像成與Java中String對應(yīng)
先這么理解吧,在類加載器那一章節(jié)會更細(xì)致的講這兩個概念。
以下是函數(shù)的部分內(nèi)容,該函數(shù)是universe.cpp->universe_init()
// 創(chuàng)建符號表,把它想像成java里的HashMap
SymbolTable::create_table();
// 創(chuàng)建字符串表,把它想像成java里的HashMap
StringTable::create_table();
// 創(chuàng)建包信息表,也可以想像成一個HashMap,包信息用到的時候再講
ClassLoader::create_package_info_table();
SymbolTable類的定義:
// 模板類,key為Symbol,value為mtSymbol
class SymbolTable : public RehashableHashtable<Symbol*, mtSymbol>
StringTable類的定義:
// 模板類,key為oop,value為mtSymbol
class StringTable : public RehashableHashtable<oop, mtSymbol>
SymbolTable:key是Symbol,Symbol可以理解為utf8編碼的字符信息
SymbolTable:value是mtSymbol,這是一個枚舉值,僅僅表示內(nèi)存的類型解釋,不起實際作用
StringTable:key是oop,oop可以理解為指向Java對象的地址(實際上存放的就是Java的String對象的地址)
StringTable:value是mtSymbol,這是一個枚舉值,僅僅表示內(nèi)存的類型解釋,不起實際作用
兩個表的創(chuàng)建過程都非常簡單,但是符號表SymbolTable的創(chuàng)建要復(fù)雜些,增加了initialize_symbols初始化符號的操作,代碼如下:
static void create_table() {
assert(_the_table == NULL, "One symbol table allowed.");
_the_table = new SymbolTable();
// 預(yù)先創(chuàng)建并分配存放符號的內(nèi)存chunk,symbol_alloc_arena_size = 360K
initialize_symbols(symbol_alloc_arena_size);
}
void SymbolTable::initialize_symbols(int arena_alloc_size) {
// Initialize the arena for global symbols, size passed in depends on CDS.
if (arena_alloc_size == 0) {
_arena = new (mtSymbol) Arena(mtSymbol);
} else {
// 創(chuàng)建一個Arena對象,細(xì)節(jié)看`章節(jié)19.2.1`
_arena = new (mtSymbol) Arena(mtSymbol, arena_alloc_size);
}
}
19.2.1 allocation.cpp
19.2.1.1 Arena構(gòu)造函數(shù)
在JVM運行過程中,會產(chǎn)生大量的符號,為了效率在存儲時不可能來一個分配一個,所以需要提前劃出一片區(qū)域chunk來存儲,如果一塊chunk不夠了,再創(chuàng)建一塊,依此類推,Arena就是管理這些chunk的類對象,各chunk之間以鏈表的形式關(guān)聯(lián),整個JVM中對內(nèi)存的管理中,大量使用鏈表這一數(shù)據(jù)結(jié)構(gòu)來處理。順便描述下Arena的幾個字段的含義文章來源:http://www.zghlxwxcb.cn/news/detail-798362.html
Chunk *_first; // 第一塊 chunk
Chunk *_chunk; // 前在用的 chunk
char *_hwm, *_max; // 當(dāng)前在用的 chunk 起始點和限制點
size_t _size_in_bytes; // Arena的總大?。ㄋ衏hunk大小相加)文章來源地址http://www.zghlxwxcb.cn/news/detail-798362.html
Arena::Arena(MEMFLAGS flag, size_t init_size) : _flags(flag), _size_in_bytes(0) {
size_t round_size = (sizeof (char *)) - 1;
init_size = (init_size+round_size) & ~round_size;
// 主要看這里,創(chuàng)建了一個init_size大小的chunk塊
_first = _chunk = new (AllocFailStrategy::EXIT_OOM, init_size) Chunk(init_size);
_hwm = _chunk->bottom(); // 保存 hwm, max,分別指向chunk塊可操作的起始點和限制點
_max = _chunk->top();
MemTracker::record_new_arena(flag);
set_size_in_bytes(init_size);
}
到了這里,關(guān)于Hotspot源碼解析-第十九章-ClassLoaderData、符號表、字符串表的初始化的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!