前面已经分析了栈帧的内存结构,Java 函数的调用,以及JVM的初始化流程,会发现里面都穿插了线程,一个函数的调用就必定有线程的创建以及线程栈帧的创建。
函数调用的最终入口时 call_helper(),但在进入 call_helper 之前,线程实际上就已经创建了,这里分析从创建线程到调用 call_helper() 中间的这一部分代码。
1.Java线程、OS线程、线程栈帧的关系
每个 Java 中的 Thread.java 对象的创建,会在 C++ 中创建 JavaMethod 对象,并且调用系统内核线程创建的函数创建系统线程。也就是说现代 JVM 的 Java 线程实际上就是调用了系统的os接口创建的内核线程。
这种设计是在 jdk1.2 之后才出现的,jdk1.2 之外是使用的用户线程模型,也就是在 JVM 这一层实现统一的线程调度。
而在 JDK 1.2 及以后,JVM 选择了更加稳定且方便使用的操作系统原生的内核级线程,通过系统调用,将线程的调度交给了操作系统内核。
而对于不同的操作系统来说,它们本身的设计思路基本上是完全不一样的,因此它们各自对于线程的设计也存在种种差异,所以 JVM 中明确声明了:虚拟机中的线程状态,不反应任何操作系统中的线程状态。
用户空间和内核空间实现线程的区别:
主要在于任务调度是交给用户实现还是说使用内核实现。
用户空间实现线程的方式是,向cpu申请一个进程,内核统一为这个进程分配计算资源。但是内核只看得见你这个进程,而由这个进程自己去实现下面线程的切换、调度。
缺点就是一个线程的耗时阻塞会造成整个进程的耗时阻塞另外用户空间没有时钟中断概念造成多线程的资源分配不均,有的线程长时间等待。
而内核空间实现线程的方式就是,直接调用内核的线程接口去创建线程,调度交给内核统一调度。
栈帧是在内存中分配,栈是 JVM 内存中的一部分,因为 JVM 是基于栈的虚拟机,也就是通过变量的载入、计算、结果弹出,以及局部变量保存,以及 stub 调用入口点这些信息都需要在栈中保存。实际上在内核中栈也是对一段内存的描述。
2.Java线程的创建
首先看一下线程相关的代码文件的结构:
Thread.c 通过 JNINativeMethod 定义 Java 调用的 start0() 本地方法会进入 JVM_StartThread()
jvm.cpp JVM_StartThread 会创建 JavaThread 对象并执行 Thread::start()
thread.cpp
Thread::start 会调用 os::start_thread()
thread.cpp JavaThread::JavaThread 创建 JavaThread 构造函数,执行
os::create_thread()
os_linux.cpp
os::create_thread 会调用 unix pthread_create() 创建内核线程的函数,并传入 java_start 作为启动函数。
os_linux.cpp *java_start 会修改线程为 INITIALIZED 状态,并while等待,状态变为 RUNNABLE 则执行 thread->run()
os.cpp os::start_thread 会修改线程为 RUNNABLE 状态
其中 Thread.java 的 start() 方法调用的本地的 start0() 函数,这个 JNI 在头文件中定义服下:
java_lang_Thread.h
/*
* Class: java_lang_Thread
* Method: start0
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_java_lang_Thread_start0
(JNIEnv *, jobject);
其中映射的名称为 Java_java_lang_Thread_start0 按照JNI规则,在 Thread.c 中本地方法表可以查到最终调用 JVM_StartThread 这个函数,这个就是 Java 线程的入口:
static JNINativeMethod methods[] = {
{"start0", "()V", (void *)&JVM_StartThread},
};
在 JVM_StartThread() 函数中,会创建 JavaThread() 并最终执行 Thread::start():
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
bool throw_illegal_thread_state = false;
{
MutexLocker mu(Threads_lock);
if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
throw_illegal_thread_state = true;
} else {
jlong size = java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
size_t sz = size > 0 ? (size_t) size : 0;
// 创建 JavaThread 对象,会调用 os::create_thread() 创建系统线程
// thread_entry 就是线程 stub 调用入口,会执行 JavaCalls::call_virtual
// 系统线程会while等待 os::start_thread()调用来触发 run()
native_thread = new JavaThread(&thread_entry, sz);
if (native_thread->osthread() != NULL) {
// Note: the current thread is not being used within "prepare".
native_thread->prepare(jthread);
}
}
}
if (throw_illegal_thread_state) {
THROW(vmSymbols::java_lang_IllegalThreadStateException());
}
assert(native_thread != NULL, "Starting null thread?");
if (native_thread->osthread() == NULL) {
// No one should hold a reference to the 'native_thread'.
delete native_thread;
if (JvmtiExport::should_post_resource_exhausted()) {
JvmtiExport::post_resource_exhausted(
JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
"unable to create new native thread");
}
THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),"unable to create new native thread");
}
// 里面会调用 os::start_thread(thread) 并修改内核线程状态为 RUNNABLE,触发内核线程执行传入的 run() 函数
Thread::start(native_thread);
JVM_END
而 JavaThread 对象的构造函数中会调用内核线程创建函数 os::create_thread(),这个函数就会调用 pthread_create() 也就是 unix 创建线程的函数:
bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
// 省略
ThreadState state;
{
// 创建线程 pthread_create,是类Unix操作系统(Unix、Linux、Mac OS X等)的创建线程的函数。
pthread_t tid;
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
// 省略
}
// 省略
return true;
}
另外在创建内核线程时,传入的启动函数是 java_start,这个函数会设置线程状态为初始化,并while等待状态的改变,如果状态改为运行了就会调用 thread->run(); 来运行最终的任务函数:
static void *java_start(Thread *thread) {
// 省略
// handshaking with parent thread
{
MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
// 内核线程状态置为初始状态
osthread->set_state(INITIALIZED);
sync->notify_all();
// wait until os::start_thread()
// while循环等待 os::start_thread()调用,直到状态不等于INITIALIZED就会调用JavaThread对象的run方法
while (osthread->get_state() == INITIALIZED) {
sync->wait(Mutex::_no_safepoint_check_flag);
}
}
// 前面循环完毕之后说明 os::start_thread()调用了,这里开始执行线程run函数
thread->run();
return 0;
}
到了这里就只差最后一步了,那就是 run() 函数的启动了。在前面 JVM_StartThread() 函数中最后调用了 Thread::start() ,这个就会触发 java_start 中执行 thread->run()。
void Thread::start(Thread* thread) {
trace("start", thread);
if (!DisableStartThread) {
os::start_thread(thread);
}
}
而 os::start_thread() 函数会修改线程状态为运行,触发 java_start 中的 thread-> run():
void os::start_thread(Thread* thread) {
MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
OSThread* osthread = thread->osthread();
// 设置线程状态,将前面while监听不等于INITIALIZED的线程开始执行
osthread->set_state(RUNNABLE);
// 通知新线程运行 thread_native_entry 函数
pd_start_thread(thread);
}
3.线程运行的入口 entry
前面线程创建成功了,也开始执行 thread->run() 了,会执行 JavaThread::run() 函数。
void JavaThread::run() {
// 线程本地分配缓存TLAB,这里将调用ThreadLocalAllocBuffer在年轻代堆内存为线程分配TLAB
// 在虚拟机创建对象时会优先到TLAB去分配,一方面TLAB使用指针碰撞分配效率高,此外是线程独有有助于并发和垃圾回收
this->initialize_tlab();
// 记录栈基指针
this->record_base_of_stack_pointer();
// 记录栈基和栈大小
this->record_stack_base_and_size();
// Initialize thread local storage; set before calling MutexLocker
this->initialize_thread_local_storage();
//创建保护页
this->create_stack_guard_pages();
// 缓存全局变量
this->cache_global_variables();
// 线程状态_thread_new变为_thread_in_vm,进入safeponit
ThreadStateTransition::transition_and_fence(this, _thread_new, _thread_in_vm);
assert(JavaThread::current() == this, "sanity check");
assert(!Thread::current()->owns_locks(), "sanity check");
this->set_active_handles(JNIHandleBlock::allocate_block());
if (JvmtiExport::should_post_thread_life()) {
JvmtiExport::post_thread_start(this);
}
EventThreadStart event;
if (event.should_commit()) {
event.set_javalangthread(java_lang_Thread::thread_id(this->threadObj()));
event.commit();
}
// 将调用entry_ponit函数
thread_main_inner();
}
会进一步调用 thread_main_inner() 函数,会调用前面设置的 thread_entry:
void JavaThread::thread_main_inner() {
assert(JavaThread::current() == this, "sanity check");
assert(this->threadObj() != NULL, "just checking");
if (!this->has_pending_exception() &&!java_lang_Thread::is_stillborn(this->threadObj())) {
{
ResourceMark rm(this);
this->set_native_thread_name(this->get_thread_name());
}
HandleMark hm(this);
// 调用entry_ponit即前文提到的jcm.cpp中的thread_entry函数
this->entry_point()(this, this);
}
DTRACE_THREAD_PROBE(stop, this);
this->exit(false);
delete this;
}
最终进入了 JavaCalls::call_virtual(),后续就会进入 call_helper() 和调用 Java 普通方法流程一样了:
static void thread_entry(JavaThread* thread, TRAPS) {
HandleMark hm(THREAD);
Handle obj(THREAD, thread->threadObj());
JavaValue result(T_VOID);
// 执行 call stub
JavaCalls::call_virtual(&result,
obj,
KlassHandle(THREAD, SystemDictionary::Thread_klass()),
vmSymbols::run_method_name(),
vmSymbols::void_method_signature(),
THREAD);
}
JavaCalls::call_virtual() =>
JavaCalls::call() =>
JavaCalls::call_helper() =>
generate_call_stub() =>