Java線程映射到操作系統線程原理淺析

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前言","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之前看JVM內存結構時,看到了《深入理解JVM》這本書說“每個線程都有一個程序計數器,記錄了當前執行字節碼的位置”。但是想起來JVM的線程是委託OS實現的,或者說,Java線程映射到了OS線程,那這個PC記錄的字節碼指令位置到底是什麼?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"OS的線程那可是正兒八經的C線程,C線程的PC保存二進制指令的位置(忽略亂序執行導致的不完整指令),他們是如何一一對應的?難道是每個字節碼對應一個本地機器碼嗎?但是Java還有解釋執行器這個東西,他是一條一條翻譯的。👴屬實想了好久,後來在谷歌上翻了不少時間,勉強找到一些解釋。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在開始討論這個問題之前,我們不妨先來實現一個屬於自己的線程來驗證。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"要求","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1⃣️一個可以debug的JDK,一個谷歌。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2⃣️可以閱讀C++代碼。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"過程","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,我們先試着定義自己的Java線程。因爲我暱稱是CodeWithBuff嘛,所以前綴就是CWB:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * @author CodeWithBuff(給代碼來點Buff)\n * @device iMacPro\n * @time 2021/6/29 1:27 下午\n */\npublic class CWBThread {\n\n private String msg;\n\n public CWBThread(String msg) {\n this.msg = msg;\n }\n\n public void run() {\n System.out.println(msg);\n }\n\n public void start() {\n start0();\n }\n\n private native void start0();\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們模仿JVM,整個了start0()方法,它是native的,所以我們需要在C++裏實現。使用它很簡單:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"new CWBThread(\"aaa\").start();\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"即可。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"既然我們要模仿JVM,那就把線程的創建,銷燬等操作,也學JVM一樣,丟給OS去做!在這裏我不打算學JVM在JDK裏面添加動態鏈接庫,而是通過JNI的方式來實現。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在終端輸入:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"javac /.../CWBThread.java -h /.../[目錄]\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"來生成我們需要實現的本地方法的頭文件,不出意外你會得到這樣的.h文件:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"cpp"},"content":[{"type":"text","text":"/* DO NOT EDIT THIS FILE - it is machine generated */\n#include \n/* Header for class com_codewithbuff_javathread_CWBThread */\n\n#ifndef _Included_com_codewithbuff_javathread_CWBThread\n#define _Included_com_codewithbuff_javathread_CWBThread\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n/*\n * Class: com_codewithbuff_javathread_CWBThread\n * Method: start0\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_codewithbuff_javathread_CWBThread_start0\n (JNIEnv *, jobject);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後就是實現它的方法了,我是用CLion編寫,在此之前需要鏈接jni.h和jni_md.h兩個文件,否則會失敗。所以我的CMakeLists文件如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"txt"},"content":[{"type":"text","text":"cmake_minimum_required(VERSION 3.19)\nproject(JavaThreadLearn)\n\nset(CMAKE_CXX_STANDARD 20)\n\n# 這裏改成你自己的文件位置和文件名\nadd_executable(JavaThreadLearn src/main/cpp/com_codewithbuff_javathread_CWBThread.h src/main/cpp/cwb_thread.cpp)\n# 這裏記得修改成你自己的JDK目錄\ninclude_directories(/Library/Java/JavaVirtualMachines/jdk-15.0.2.jdk/Contents/Home/include)\ninclude_directories(/Library/Java/JavaVirtualMachines/jdk-15.0.2.jdk/Contents/Home/include/darwin)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之後呢,我們就可以編寫對應的cpp文件了,編寫之後如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"cpp"},"content":[{"type":"text","text":"//\n// Created by joker on 2021/6/29.\n//\n#include \n#include \n#include \"com_codewithbuff_javathread_CWBThread.h\"\nusing namespace std;\n\nclass CWBThreadWrapper {\nprivate:\n JavaVM* javaVm;\n jobject cwbThreadObject;\n JNIEnv* attachToJVM();\npublic:\n CWBThreadWrapper(JNIEnv *env, jobject obj);\n void callRunMethod();\n ~CWBThreadWrapper();\n};\n\nJNIEnv *CWBThreadWrapper::attachToJVM() {\n JNIEnv *jniEnv;\n if (javaVm->AttachCurrentThread((void **)&jniEnv, nullptr) != 0) {\n cout << \"Attach failed.\\n\";\n }\n return jniEnv;\n}\n\nCWBThreadWrapper::CWBThreadWrapper(JNIEnv *env, jobject obj) {\n env->GetJavaVM(&(this->javaVm));\n this->cwbThreadObject = env->NewGlobalRef(obj);\n}\n\nCWBThreadWrapper::~CWBThreadWrapper() {\n javaVm->DetachCurrentThread();\n}\n\nvoid CWBThreadWrapper::callRunMethod() {\n JNIEnv *env = attachToJVM();\n jclass clazz = env->GetObjectClass(this->cwbThreadObject);\n jmethodID methodId = env->GetMethodID(clazz, \"run\", \"()V\");\n if (methodId != nullptr) {\n env->CallVoidMethod(this->cwbThreadObject, methodId);\n } else {\n cout << \"Can't find run() method.\\n\";\n }\n}\n\nvoid *thread_entry_pointer(void *args) {\n cout << \"Start set thread entry pointer.\\n\";\n CWBThreadWrapper *cwbThreadWrapper = (CWBThreadWrapper *) args;\n cwbThreadWrapper->callRunMethod();\n delete cwbThreadWrapper;\n return nullptr;\n}\n\nJNIEXPORT void JNICALL Java_com_codewithbuff_javathread_CWBThread_start0(JNIEnv *jniEnv, jobject cswThreadObject) {\n CWBThreadWrapper *cwbThreadWrapper = new CWBThreadWrapper(jniEnv, cswThreadObject);\n pthread_attr_t pthreadAttr;\n pthread_attr_init(&pthreadAttr);\n pthread_attr_setdetachstate(&pthreadAttr, PTHREAD_CREATE_DETACHED);\n pthread_t pthread;\n if (pthread_create(&pthread, &pthreadAttr, thread_entry_pointer, cwbThreadWrapper)) {\n cout << \"Create error.\\n\";\n } else {\n cout << \"Start a linux thread.\\n\";\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這代碼閱讀起來問題不大,就是一個簡單的JNI本地方法調用。因爲我們把線程的創建交給了pthread來完成,我們也不需要考慮爲線程插入安全點,安全區域,解釋器入口等JVM纔有的操作,所以總代碼不多,功能單一。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在一切就緒,我們把這個cpp文件編譯成平臺相關的動態鏈接文件,因爲我是macOS,所以後綴是jnilib,輸入指令:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"g++ -I[你的JDK的位置]/jdk-15.0.2.jdk/Contents/Home/include -I[你的JDK的位置]/jdk-15.0.2.jdk/Contents/Home/include/darwin -dynamiclib [你的cpp文件的位置]/cwb_thread.cpp -o libCWBThread.jnilib\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後會生成一個jnilib的文件在你的C++工程文件夾下面。然後我們通過System.load()方法加載這個文件到JVM中去,JVM就可以爲我們的native的start0()方法調用C++方法了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如下所示:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class Main {\n\n public static void main(String[] args) {\n System.load(\"[你的C++工程位置]/libCWBThread.jnilib\");\n new CWBThread(\"aaa\").start();\n new CWBThread(\"bbb\").start();\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以看到這樣的輸出:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/83/83ebc4cde0c6465b6da798a7532785bc.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此時我們通過在C++中創建一個線程,在線程中傳入CWBThread的對象,然後通過這個對象調用它的run()方法來實現類似JVM的Thread創建運行。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到此結束了嗎?那我們一開始提的問題,又怎麼回答呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在回答問題之前,我們先來看看JVM架構:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e8/e8b8f384590593338deb6dfae3305497.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"類加載器我們就不說了,運行時數據區主要包括以下幾個:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b5/b58827231d34592a7367fdd2180e74a6.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"重點看後面的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"執行引擎","attrs":{}},{"type":"text","text":"部分。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/34/34ab8acf82ad8ff55feced392b4cb14f.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"執行引擎負責執行class文件,除此之外,還負責處理Java代碼中的JNI本地調用,並把結果返回給Java程序。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏的執行引擎包括三個:解釋器,即時編譯器和垃圾回收器。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1️⃣解釋器負責把class文件一行一行的解釋執行,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"並不是翻譯成機器碼","attrs":{}},{"type":"text","text":",而是讀取每一行字節碼,然後在自己的內部執行,所以Java線程的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"PC記錄的是當前解釋器解釋到了哪一行","attrs":{}},{"type":"text","text":"(第一個疑惑——PC記錄的是啥)。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2️⃣JIT負責對熱點代碼生成本地碼,具體過程包括:","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"熱點探測技術發現熱點代碼","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"字節碼=>中間碼","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"中間碼優化","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"中間碼=>本地機器碼","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏需要說明的是,JIT只負責生成,不負責執行,生成之後的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"機器碼的調用還是解釋器來完成","attrs":{}},{"type":"text","text":",所以實際的執行流程裏有一個解釋器判斷當前代碼有沒有被編譯的過程,如果被編譯了,就直接執行編譯過的本地碼,否則自己一行一行解釋執行。","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3️⃣垃圾回收器不是我們這節的重點,我們就不說了。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在我們通過我們創建的這個小的自定義線程來理一理:首先,pthread屬於C的方法,JVM也無非是調用OS的thread create來創建線程,OS的底層實現也是pthread;其次,我們通過在pthread創建時傳入我們的JavaThread對象,然後調用它的run()方法來實現線程中執行run()的功能;最後執行完畢,線程因爲是C線程,由OS負責銷燬,我們啥也沒幹就結束了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這裏我們忽略了JVM在爲JavaThread創建OS線程時插入的安全點等操作,單純考慮最簡單的功能。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"後來我在StackOverflow和知乎上找到了答案。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每個線程的執行分爲兩種:解釋器直接執行+本地方法執行。如果是本地方法執行,解釋器不做任何行爲;如果是解釋器執行,則解釋執行當前字節碼,如有必要,由JIT翻譯成本地機器碼,但是依舊是解釋器執行本地代碼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java字節碼必須通過執行引擎執行,所以即使是在pthread中的run()方法,它所包含的字節碼也必須由執行引擎執行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(我的理解)執行引擎+run()是一起運行在pthread中的,它們倆組合運行,而不是run()單獨跑在操作系統線程中,也不是多個run()共享一個執行引擎。執行引擎只是一個程序,相當於在run()之前插入一些代碼,然後讀取字節碼,(執行引擎)在pthread中運行(即運行自己也運行字節碼)。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那些答案在強調Java只能以字節碼+執行引擎執行,JIT只是把字節碼中的熱點代碼編譯成本地碼加快速度,但不等於Java字節碼可以直接跑在機器上。既然Java字節碼只能通過執行引擎運行,而run()裏面保存的是字節碼,那麼由pthread運行的run()必然需要執行引擎介入。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"順帶一提,對於方法調用,在同一線程中,只是執行引擎入口處的棧幀+字節碼發生了替換而已。不同線程擁有自己的執行引擎,彼此獨立;這裏需要強調的是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"執行引擎是一個程序,不是一個實例對象","attrs":{}},{"type":"text","text":",它位於線程最開始的地方,接受一個方法的棧幀+字節碼作爲參數,然後執行字節碼(純解釋器執行或JIT翻譯之後解釋器執行本地碼)。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏還要說一下,關於PC(程序計數器),如果執行的是Java代碼(字節碼),PC確實是字節碼位置,哪怕在pthread中執行,因爲我們剛剛已經弄明白了一件事,那就是即使是在pthread中,還是跑的是字節碼,所以PC是存在的,也確確實實記錄了字節碼行號;但若跑的是本地方法,那PC就是未定義的,因爲此時本地方法的PC表示的是二進制指令的位置。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"參考","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.zhihu.com/question/64677339/answer/223307974","title":"","type":null},"content":[{"type":"text","text":"對於OpenJDK而言,是不是每個Java線程都對應一個執行引擎線程? - ETIN的回答 - 知乎","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://medium.com/@unmeshvjoshi/how-java-thread-maps-to-os-thread-e280a9fb2e06","title":"","type":null},"content":[{"type":"text","text":"How Java thread maps to OS thread?","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://stackoverflow.com/questions/9712455/wouldnt-each-thread-require-its-own-copy-of-the-jvm","title":"","type":null},"content":[{"type":"text","text":"Wouldn't each thread require its own copy of the JVM?","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.freecodecamp.org/news/jvm-tutorial-java-virtual-machine-architecture-explained-for-beginners/","title":"","type":null},"content":[{"type":"text","text":"JVM Tutorial - Java Virtual Machine Architecture Explained for Beginners","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://stackoverflow.com/a/41498131","title":"","type":null},"content":[{"type":"text","text":"What exactly is the JIT compiler inside a JVM?","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.webopedia.com/definitions/interpreter/","title":"","type":null},"content":[{"type":"text","text":"Interpreter","attrs":{}}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章