1. JVM中線程的創(chuàng)建流程
jvm剝離了一套公共的無(wú)關(guān)平臺(tái)的線程類:Thread抽象類已脓。Thread類聲明在jdk8u-dev/hotspot/src/share/vm/runtime/thread.hpp
中柬采。Thread有個(gè)抽象方法run()
户侥。當(dāng)創(chuàng)建好一個(gè)Thread實(shí)例崔挖,需要調(diào)用操作系統(tǒng)底層的os::create_thread
方法术幔,傳入thread對(duì)象和新線程棧大小(64位系統(tǒng)默認(rèn)是1m侄柔,32位的是512k既们;CompilerThread比較特殊仲吏,分配了4m)不铆。
在調(diào)用系統(tǒng)的底層函數(shù)創(chuàng)建好線程之后,將以java_start
函數(shù)為線程入口裹唆,Thread
對(duì)象為參數(shù)啟動(dòng)線程誓斥。在線程中,會(huì)調(diào)用該對(duì)象的run
方法品腹,實(shí)現(xiàn)線程功能的定制岖食。直接借用thread.hpp
中的代碼注釋顯示繼承關(guān)系。
// Class hierarchy
// - Thread
// - NamedThread
// - VMThread
// - ConcurrentGCThread
// - WorkerThread
// - GangWorker
// - GCTaskThread
// - JavaThread
// - WatcherThread
使用偽代碼大概是這樣舞吭。
父線程:
step1: Thread *thread = new VMThread();//for example
step2 : os::create_thread(Thread:thread ,stacksize:0);
子線程:
thread->run();
我們知道泡垃,線程是cpu調(diào)度的最小單位∠叟福可以從線程的角度蔑穴,大致分析下jvm的工作內(nèi)容。所以我們先初步分析下各種線程的作用惧浴。
2. JVM中的Thread子類
2.1 VMThread
VMThread可以說(shuō)是最核心的線程了存和,一個(gè)進(jìn)程中只會(huì)創(chuàng)建一個(gè),繼承關(guān)系:VMThread->NamedThread->Thread->ThreadShadow
,其中ThreadShadow
是一個(gè)異常相關(guān)的類衷旅。VMThread內(nèi)部維護(hù)了一個(gè)任務(wù)隊(duì)列捐腿,jvm通過(guò)調(diào)用VMThread的execute方法來(lái)塞入VM_Operation類型的任務(wù)。在vm線程獲取到任務(wù)后柿顶,調(diào)用VM_Operation
對(duì)象的doit()
方法茄袖。下面我們看看有哪些VM_Operation
。先上我們最關(guān)心的GC任務(wù)嘁锯。
VM_CMS_Operation
VM_CGC_Operation
VM_GC_Operation
重要的子類:VM_ParallelGCSystemGC,調(diào)用java代碼System.gc() 或者JVMTI中會(huì)用到;VM_G1CollectFull,VM_GenCollectFull,VM_GC_HeapInspection宪祥。
VM_CGC_Operation
VM_CGC_Operation
VM_CGC_Operation
VM_CGC_Operation
VM_CGC_Operation
VM_CGC_Operation
2.2 JavaThread
用戶可以通過(guò)參數(shù)-XX:ThreadStackSize=2m
或者 -Xss2m
或者 -XX:ThreadStackSize=2048
(注意千字節(jié)時(shí)不加k,我調(diào)試的時(shí)候加k直接變成2g了)將JavaThread
類型的線程的棧大小調(diào)整為2m(注意僅僅是JavaThread類型家乘,包括用戶自定的和jvm內(nèi)部生成的)蝗羊。但是有最小值,低于這個(gè)最小值則取系統(tǒng)最小值仁锯,我的機(jī)器上是160k耀找。
2.2.1 java程序中創(chuàng)建的線程
我們知道,java.lang.Thread 的start方法最終是調(diào)用了start0本地方法扑馁。查看jvm的實(shí)際映射函數(shù)為jvm.cpp
中的JVM_StartThread
涯呻。實(shí)現(xiàn)也比較清晰凉驻,將java線程對(duì)象包裝成一個(gè)entry,生成一個(gè)JavaThread
按照第一節(jié)的流程創(chuàng)建對(duì)象复罐,最后在新線程中通過(guò)JavaCalls::call_virtual
方法調(diào)用Java代碼中對(duì)應(yīng)的線程對(duì)象run
方法涝登,在退出時(shí)將線程狀態(tài)改變成terminal。之前我還很疑惑在jdk代碼中沒(méi)有改變threadStatus
的地方效诅,但是實(shí)際卻可以通過(guò)這個(gè)值來(lái)判斷線程的狀態(tài)胀滚。原來(lái)是jvm代碼中操縱的,具體的函數(shù)是java_lang_Thread::set_thread_status
乱投。
2.2.2 jvm自建的JavaThread
2.2.2.1 CompilerThread
CompilerThread 按照功能細(xì)微不同分為C1 CompilerThread(client模式下的編譯器) 和C2 CompilerThread(server模式下的編譯器)咽笼。默認(rèn)數(shù)量根據(jù)cpu計(jì)算,我們可以用vm參數(shù)-XX:CICompilerCount=n
來(lái)設(shè)置總的編譯線程數(shù)量戚炫,其中 C1類型的占1/3剑刑。
CompilerThread中的隊(duì)列實(shí)際上是CompileBroker管理的,我們可以通過(guò)調(diào)用CompileBroker::compile_method方法塞入一個(gè)方法級(jí)的編譯任務(wù)双肤。
2.3 GCTaskThread
顧名思義施掏,GCTaskThread是用來(lái)執(zhí)行g(shù)c任務(wù)的。粘貼其run方法
void GCTaskThread::run() {
// Set up the thread for stack overflow support
this->record_stack_base_and_size();
this->initialize_thread_local_storage();
// Bind yourself to your processor.
if (processor_id() != GCTaskManager::sentinel_worker()) {
if (TraceGCTaskThread) {
tty->print_cr("GCTaskThread::run: "
" binding to processor %u", processor_id());
}
if (!os::bind_to_processor(processor_id())) {
DEBUG_ONLY(
warning("Couldn't bind GCTaskThread %u to processor %u",
which(), processor_id());
)
}
}
// Part of thread setup.
// ??? Are these set up once here to make subsequent ones fast?
HandleMark hm_outer;
ResourceMark rm_outer;
TimeStamp timer;
for (;/* ever */;) {
// These are so we can flush the resources allocated in the inner loop.
HandleMark hm_inner;
ResourceMark rm_inner;
for (; /* break */; ) {
// This will block until there is a task to be gotten.
GCTask* task = manager()->get_task(which());
// Record if this is an idle task for later use.
bool is_idle_task = task->is_idle_task();
// In case the update is costly
if (PrintGCTaskTimeStamps) {
timer.update();
}
jlong entry_time = timer.ticks();
char* name = task->name();
// If this is the barrier task, it can be destroyed
// by the GC task manager once the do_it() executes.
task->do_it(manager(), which());
// Use the saved value of is_idle_task because references
// using "task" are not reliable for the barrier task.
if (!is_idle_task) {
manager()->note_completion(which());
if (PrintGCTaskTimeStamps) {
assert(_time_stamps != NULL,
"Sanity (PrintGCTaskTimeStamps set late?)");
timer.update();
GCTaskTimeStamp* time_stamp = time_stamp_at(_time_stamp_index++);
time_stamp->set_name(name);
time_stamp->set_entry_time(entry_time);
time_stamp->set_exit_time(timer.ticks());
}
} else {
// idle tasks complete outside the normal accounting
// so that a task can complete without waiting for idle tasks.
// They have to be terminated separately.
IdleGCTask::destroy((IdleGCTask*)task);
set_is_working(true);
}
// Check if we should release our inner resources.
if (manager()->should_release_resources(which())) {
manager()->note_release(which());
break;
}
}
}
}
這里引入一個(gè)GCTaskManager類茅糜,用于管理gc線程的七芭。gc線程從GCTaskManager的任務(wù)隊(duì)列SynchronizedGCTaskQueue中取一個(gè)GCTask任務(wù),然后調(diào)用GCTask對(duì)象的do_it方法執(zhí)行具體邏輯蔑赘。
- GCTaskManager維護(hù)了一個(gè)GCTask任務(wù)隊(duì)列狸驳,如何塞入隊(duì)列?
GCTaskManager提供了兩個(gè)方法入列缩赛,add_task:添加單個(gè)耙箍,實(shí)際未使用;add_list:添加多個(gè),類外是通過(guò)execute_and_wait方法間接調(diào)用來(lái)插入酥馍。
GCTask任務(wù)有哪些究西?
NoopGCTask
啥也沒(méi)做
- BarrierGCTask
等待其他gc工作線程空閑下來(lái)才返回do_it方法, 一個(gè)常見(jiàn)子類是WaitForBarrierGCTask物喷,內(nèi)部維護(hù)了monitor,實(shí)現(xiàn)了事后通知的功能遮斥。
- IdleGCTask
只能用于動(dòng)態(tài)的gc線程峦失,作用應(yīng)該是輔助,如果是獲取到的是"傻子"任務(wù)术吗,那么可以略過(guò)尉辑。
- MarkFromRootsTask
RefProcTaskProxy
RefEnqueueTaskProxy
StealMarkingTask
StealRegionCompactionTask
UpdateDensePrefixTask
DrainStacksCompactionTask
PSRefProcTaskProxy
PSRefEnqueueTaskProxy
ScavengeRootsTask
ThreadRootsTask
StealTask
OldToYoungRootsTask