Hotspot源码解析-第十二章-JavaThread的创建

2024-01-04 06:28

本文主要是介绍Hotspot源码解析-第十二章-JavaThread的创建,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在本章中会大量用到Handle,也就是句柄的概念,那么首先先把指针、引用、句柄的概念搞清楚

1、指针:在C/C++中大量使用指针,表示某个对象/内存的地址,不受 指向的类型限制,只是表示地址,这个概念好理解

2、引用:引用在面向对象语言(C++、Java)中大量存在,可以把引用等价于指针,区别在于,引用限定了类型,声明时是什么类型,就是什么类型,这样的好处就是不至于在运行时产生类型转换的错误

3、句柄:句柄其实就是对指针的一层包装,想像成家里电视机的遥控器,可以遥控很多电视机,句柄就是通过间接访问内存的方式,应对指针在运行过程中发生的变化,比如电视机换了一台,但是遥控还可以控制,就是这个道理

12.1 JavaThread的创建

12.1.1 thread.cpp

12.1.1.1 create_vm

create_vm函数内容太多,这里只截取了与本章相关的内容

// 绑定主线程到os级线程,在`第十一章`中讲过,JavaThread是继承自Thread,注意,这里的JavaThread只是虚拟机层面的Java级线程,并不是我们Java编码层面的线程,这个概念要区分来,但是Java编码层面的线程Thread,最终在虚拟机底层就是用JavaThread来表示的,从这点看,两者又是一样,抛开编码层面,就可以总结为:JavaThread就是一个Java线程的表示,后续章节也会细讲。new JavaThread()的实现细节看`章节12.1.2.1`JavaThread* main_thread = new JavaThread();// 设置线程状态,thread_state状态跟Java层面线程的状态(new、running等)不是一个概念,这个值主要标注线程当前运行状况,或者说在哪个层面运行,比如Java层面、虚拟机层面、native层面main_thread->set_thread_state(_thread_in_vm); // 设置线程栈基址和栈占用空间大小(可以得出栈顶最大值)main_thread->record_stack_base_and_size();main_thread->initialize_thread_local_storage(); // 初始化线程本地存储// 分配JNI句柄存储块,并设置关联到当前线程main_thread->set_active_handles(JNIHandleBlock::allocate_block());// 将当前线程绑定到OS线程,细节看`章节12.2`if (!main_thread->set_as_starting_thread()) {// 主线程绑定失败,虚拟机退出vm_shutdown_during_initialization("Failed necessary internal allocation. Out of swap space");delete main_thread;*canTryAgain = false; // don't let caller call JNI_CreateJavaVM againreturn JNI_ENOMEM;}// 创建线程栈保护区,细节看`章节12.3`main_thread->create_stack_guard_pages();
12.1.1.2 Thread::allocate

这个函数就是给线程分配内存的,分为两部分,一个是支持偏向锁;一个是不支持偏向锁

void* Thread::allocate(size_t size, bool throw_excpt, MEMFLAGS flags) {if (UseBiasedLocking) { // 支持偏向锁const int alignment = markOopDesc::biased_lock_alignment;  // 偏向锁对齐大小size_t aligned_size = size + (alignment - sizeof(intptr_t)); // 要分配的内存大小// 实际分配内存操作,并返回分配的内存首地址void* real_malloc_addr = throw_excpt? AllocateHeap(aligned_size, flags, CURRENT_PC): AllocateHeap(aligned_size, flags, CURRENT_PC,AllocFailStrategy::RETURN_NULL);// 处理对齐操作void* aligned_addr     = (void*) align_size_up((intptr_t) real_malloc_addr, alignment);assert(((uintptr_t) aligned_addr + (uintptr_t) size) <=((uintptr_t) real_malloc_addr + (uintptr_t) aligned_size),"JavaThread alignment code overflowed allocated storage");if (TraceBiasedLocking) {   // 打印日志if (aligned_addr != real_malloc_addr)tty->print_cr("Aligned thread " INTPTR_FORMAT " to " INTPTR_FORMAT,real_malloc_addr, aligned_addr);}// 给Thread对象中属性 _real_malloc_address 赋值为分配的内存首地址 ((Thread*) aligned_addr)->_real_malloc_address = real_malloc_addr;return aligned_addr;} else { // 不支持偏向锁,直接分配// 用AllocateHeap来在C堆(注意,这里不是Java堆,这个C堆就是所谓的进程运行时堆,参见`章节11.1.1`)中分配内存,AllocateHeap的细节看后面描述return throw_excpt? AllocateHeap(size, flags, CURRENT_PC): AllocateHeap(size, flags, CURRENT_PC, AllocFailStrategy::RETURN_NULL);}
}

AllocateHeap函数

inline char* AllocateHeap(size_t size, MEMFLAGS flags,AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM) {return AllocateHeap(size, flags, CURRENT_PC, alloc_failmode);
}inline char* AllocateHeap(size_t size, MEMFLAGS flags,const NativeCallStack& stack,AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM) {// 最终分配是通过系统调用malloc来完成的char* p = (char*) os::malloc(size, flags, stack);#ifdef ASSERTif (PrintMallocFree) trace_heap_malloc(size, "AllocateHeap", p);#endifif (p == NULL && alloc_failmode == AllocFailStrategy::EXIT_OOM) {vm_exit_out_of_memory(size, OOM_MALLOC_ERROR, "AllocateHeap");}return p;
}
12.1.1.3 JavaThread::JavaThread()构造函数
JavaThread::JavaThread(bool is_attaching_via_jni) :Thread()  // 先调用父类Thread的构造函数,看`章节12.1.1.4`
#if INCLUDE_ALL_GCS, _satb_mark_queue(&_satb_mark_queue_set),_dirty_card_queue(&_dirty_card_queue_set)
#endif // INCLUDE_ALL_GCS
{// 进一步从Java线程角度初始化,看`章节12.1.1.5`initialize();if (is_attaching_via_jni) {_jni_attach_state = _attaching_via_jni;} else {_jni_attach_state = _not_attaching_via_jni;}assert(deferred_card_mark().is_empty(), "Default MemRegion ctor");
}
12.1.1.4 Thread()构造函数
Thread::Thread() {// 栈设置set_stack_base(NULL); // 设置栈基址为Nullset_stack_size(0); // 初始化栈大小为0set_self_raw_id(0); // 初始化线程原生idset_lgrp_id(-1);// 数据结构分配set_osthread(NULL); // 对应的os线程,初始化时,还没有绑定,所以先设置null// 设置资源分配区域,ResourceArea就是管理当前线程分配的内存区域,比如在哪个Chunk块set_resource_area(new (mtThread)ResourceArea());DEBUG_ONLY(_current_resource_mark = NULL;) // 赋值// 设置句柄分配区域,HandleArea就是管理当前线程分配的句柄内存区域,比如在哪个Chunk块、句柄区域链表set_handle_area(new (mtThread) HandleArea(NULL));// 在C堆中分配最大元素Metadata个数为30的可增长的数组内存,用于存放元数据,并用元数组句柄指向set_metadata_handles(new (ResourceObj::C_HEAP, mtClass) GrowableArray<Metadata*>(30, true));// 下面这些属性的含义可以看`章节111.1.4`set_active_handles(NULL); // 设置指向当前线程持有的JNI句柄链表头指针,默认为Nullset_free_handle_block(NULL); // 设置空闲JNI句柄块的链表头指针,默认为Nullset_last_handle_mark(NULL); // 设置HandleMark的指针,默认为Null// This initial value ==> never claimed._oops_do_parity = 0;_metadata_on_stack_buffer = NULL;// 将当前对象用HandleMark句柄来管理,在句柄链中,初始化时this即是链表头也是链表尾(last_handle_mark)new HandleMark(this);// 下面就是常规的初始化debug_only(_owned_locks = NULL;)debug_only(_allow_allocation_count = 0;)NOT_PRODUCT(_allow_safepoint_count = 0;)NOT_PRODUCT(_skip_gcalot = false;)_jvmti_env_iteration_count = 0;set_allocated_bytes(0);_vm_operation_started_count = 0;_vm_operation_completed_count = 0;_current_pending_monitor = NULL;_current_pending_monitor_is_from_java = true;_current_waiting_monitor = NULL;_num_nested_signal = 0;omFreeList = NULL ;omFreeCount = 0 ;omFreeProvision = 32 ;omInUseList = NULL ;omInUseCount = 0 ;#ifdef ASSERT_visited_for_critical_count = false;
#endif_SR_lock = new Monitor(Mutex::suspend_resume, "SR_lock", true);_suspend_flags = 0;// thread-specific hashCode stream generator state - Marsaglia shift-xor form_hashStateX = os::random() ;_hashStateY = 842502087 ;_hashStateZ = 0x8767 ;    // (int)(3579807591LL & 0xffff) ;_hashStateW = 273326509 ;_OnTrap   = 0 ;_schedctl = NULL ;_Stalled  = 0 ;_TypeTag  = 0x2BAD ;// 为各事件分配内存_ParkEvent   = ParkEvent::Allocate (this) ;_SleepEvent  = ParkEvent::Allocate (this) ;_MutexEvent  = ParkEvent::Allocate (this) ;_MuxEvent    = ParkEvent::Allocate (this) ;#ifdef CHECK_UNHANDLED_OOPSif (CheckUnhandledOops) {_unhandled_oops = new UnhandledOops(this);}
#endif // CHECK_UNHANDLED_OOPS
#ifdef ASSERTif (UseBiasedLocking) {assert((((uintptr_t) this) & (markOopDesc::biased_lock_alignment - 1)) == 0, "forced alignment of thread object failed");assert(this == _real_malloc_address ||this == (void*) align_size_up((intptr_t) _real_malloc_address, markOopDesc::biased_lock_alignment),"bug in forced alignment of thread objects");}
#endif /* ASSERT */
}
12.1.1.5 JavaThread::initialize
// A JavaThread is a normal Java threadvoid JavaThread::initialize() {// Initialize fields// Set the claimed par_id to UINT_MAX (ie not claiming any par_ids)// 将par_id设置为uint(无符号Int)表示的最大值set_claimed_par_id(UINT_MAX);set_saved_exception_pc(NULL);set_threadObj(NULL);_anchor.clear();set_entry_point(NULL);// 设置jni要用的函数数组jni_NativeInterface,这个数组在jni.cpp文件中定义set_jni_functions(jni_functions());set_callee_target(NULL);set_vm_result(NULL);set_vm_result_2(NULL);set_vframe_array_head(NULL);set_vframe_array_last(NULL);set_deferred_locals(NULL);set_deopt_mark(NULL);set_deopt_nmethod(NULL);clear_must_deopt_id();set_monitor_chunks(NULL);set_next(NULL);// 设置线程的状态为new,也就是刚创建set_thread_state(_thread_new);_terminated = _not_terminated;_privileged_stack_top = NULL;_array_for_gc = NULL;_suspend_equivalent = false;_in_deopt_handler = 0;_doing_unsafe_access = false;_stack_guard_state = stack_guard_unused;(void)const_cast<oop&>(_exception_oop = oop(NULL));_exception_pc  = 0;_exception_handler_pc = 0;_is_method_handle_return = 0;_jvmti_thread_state= NULL;_should_post_on_exceptions_flag = JNI_FALSE;_jvmti_get_loaded_classes_closure = NULL;_interp_only_mode    = 0;_special_runtime_exit_condition = _no_async_condition;_pending_async_exception = NULL;_thread_stat = NULL;_thread_stat = new ThreadStatistics();_blocked_on_compilation = false;_jni_active_critical = 0;_pending_jni_exception_check_fn = NULL;_do_not_unlock_if_synchronized = false;_cached_monitor_info = NULL;_parker = Parker::Allocate(this) ;#ifndef PRODUCT_jmp_ring_index = 0;for (int ji = 0 ; ji < jump_ring_buffer_size ; ji++ ) {record_jump(NULL, NULL, NULL, 0);}
#endif /* PRODUCT */set_thread_profiler(NULL);if (FlatProfiler::is_active()) {// This is where we would decide to either give each thread it's own profiler// or use one global one from FlatProfiler,// or up to some count of the number of profiled threads, etc.ThreadProfiler* pp = new ThreadProfiler();pp->engage();set_thread_profiler(pp);}// 设置线程安全点状态信息,底层线程的很多操作都要检查安全点,后面讲GC的时候会讲到ThreadSafepointState::create(this);debug_only(_java_call_counter = 0);// JVMTI PopFrame support_popframe_condition = popframe_inactive;_popframe_preserved_args = NULL;_popframe_preserved_args_size = 0;_frames_to_pop_failed_realloc = 0;pd_initialize();
}

12.1.2 thread.hpp

12.1.2.1 new JavaThread()

之前在线程描述的第十一章讲过,JavaThread是继承自Thread的,在JavaThread类中没有找到new运算符的实现函数,这种情况一般就要往上找,找它的父类,直到找到为此,很幸运在Thread类中有对new运算符的实现函数

void* operator new(size_t size) throw() { return allocate(size, true); }

在new函数中,实际调用的是allocate函数,该函数在thread.cpp源文件中实现,作用就是在进程的运行时堆中分配一块内存来存放JavaThread对象,分配细节看章节12.1.1.2,内存分配完后,要对JavaThread对象做一些赋值和初始化的工作,初始化细节看章节12.1.1.3

这篇关于Hotspot源码解析-第十二章-JavaThread的创建的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/568426

相关文章

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏

javacv依赖太大导致jar包也大的解决办法

《javacv依赖太大导致jar包也大的解决办法》随着项目的复杂度和依赖关系的增加,打包后的JAR包可能会变得很大,:本文主要介绍javacv依赖太大导致jar包也大的解决办法,文中通过代码介绍的... 目录前言1.检查依赖2.更改依赖3.检查副依赖总结 前言最近在写项目时,用到了Javacv里的获取视频

Java实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

SpringBoot全局域名替换的实现

《SpringBoot全局域名替换的实现》本文主要介绍了SpringBoot全局域名替换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录 项目结构⚙️ 配置文件application.yml️ 配置类AppProperties.Ja

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H