JVM源碼分析(二)jvm中的線程

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市较屿,隨后出現(xiàn)的幾起案子隧魄,更是在濱河造成了極大的恐慌卓练,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件购啄,死亡現(xiàn)場(chǎng)離奇詭異襟企,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)狮含,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門顽悼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人几迄,你說(shuō)我怎么就攤上這事蔚龙。” “怎么了映胁?”我有些...
    開(kāi)封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵木羹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我解孙,道長(zhǎng)坑填,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任妆距,我火速辦了婚禮穷遂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘娱据。我一直安慰自己蚪黑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布中剩。 她就那樣靜靜地躺著忌穿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪结啼。 梳的紋絲不亂的頭發(fā)上掠剑,一...
    開(kāi)封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音郊愧,去河邊找鬼朴译。 笑死,一個(gè)胖子當(dāng)著我的面吹牛属铁,可吹牛的內(nèi)容都是我干的眠寿。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼焦蘑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼盯拱!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤狡逢,失蹤者是張志新(化名)和其女友劉穎宁舰,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奢浑,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蛮艰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了殷费。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片印荔。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖详羡,靈堂內(nèi)的尸體忽然破棺而出仍律,到底是詐尸還是另有隱情,我是刑警寧澤实柠,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布水泉,位于F島的核電站,受9級(jí)特大地震影響窒盐,放射性物質(zhì)發(fā)生泄漏草则。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一蟹漓、第九天 我趴在偏房一處隱蔽的房頂上張望炕横。 院中可真熱鬧,春花似錦葡粒、人聲如沸份殿。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)卿嘲。三九已至,卻和暖如春夫壁,著一層夾襖步出監(jiān)牢的瞬間拾枣,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工盒让, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留梅肤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓邑茄,卻偏偏與公主長(zhǎng)得像凭语,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子撩扒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容