首页 > 其他分享 >4. VM是如何初始化的-create_vm()

4. VM是如何初始化的-create_vm()

时间:2024-09-24 18:19:31浏览次数:3  
标签:初始化 java thread create vm VM init 线程 os

前言

3. 初窥全貌 - main方法执行全流程-CSDN博客,我们了解了一个Java类的main函数在执行java命令 到 最终被执行的全路径, 但是那里面最重要的三步

1.创建vm;

2.加载main类;

3.执行main方法;

我们并没有展开学习,这一章,我们从创建VM开始学习,这是整个系列里最重的函数。

流程很多,但是别担心,这一章是介绍流程的,属于走马观花,有个全局认识即可,真正的细节都会在后续不同的章节里列出来。

1. 整体流程  create_vm()

网上摘录的流程(来源见参考链接)。

有的不是很重要的部分,我也没细看,在后续也不会列出,但是无关大局,不要在意。

  1. is_supported_jni_version检查是否支持JNI的版本是否是JNI_VERSION_1_1,JNI_VERSION_1_2,JNI_VERSION_1_4,JNI_VERSION_1_6,JNI_VERSION_1_8。
  2. ostream_init初始化输出流模块。
  3. process_sun_java_launcher_properties处理java启动的配置。处理-Dsun.java.launcher和-Dsun.java.launcher.pid=参数
  4. os::init初始化os的模块,依赖对应的操作系统(例如:Liunx系统调用的是(os_liunx.cpp中init函数)。
  5. init_system_properties首先初始化系统配置参数,例如:java.class.path、java.home等参数。
  6. JDK_Version_init初始化jdk的版本,主要有主版本号,次版本号等.
  7. Arguments::parse 解析启动参数。
  8. os::init_before_ergo() 主要large page的初始化,Liunx系统初始华large_page_size大小是2M。
  9. Arguments::apply_ergo() 应用参数,主要设置堆的物理大小以及初始化MetaSpace flags 和 alignments.
  10. PauseAtStartup参数为true,则调用os::pause(),os系统一直通过poll轮询等待JVM启动完成.这个参数默认是false,
  11. HOTSPOT_VM_INIT_BEGIN JVM启动
  12. 启动追踪JVM记录时间统计对象TraceVmCreationTime的start方法.
  13. os::init_2() 解析完JVM参数后调用操作系统的第二阶段初始化,。
  14. Arguments::adjust_after_os()进行os参数初始化后,进行参数的调整。
  15. ThreadLocalStorage::init() 对ThreadLocalStorage初始化。
  16. MemTracker::bootstrap_single_thread()单线程启动。
  17. ostream_init_log方法主要初始化 -Xloggc:参数中日志文件的创建。
  18. init_agents_at_startup方法是判断有没有-Xrun参数,将其转成-agentLibraryList中,将其转成-agentlib参数一样处理,主要是兼容之前jdk版本。
  19. create_vm_init_agents 创建虚拟机的包含参数-agentlib: -agentpath参数执行的agent的库以及-Xrun转换来的,并调用agent动态链接库中Agent_OnLoad方法进行初始化。
  20. 初始化线程的状态,_thread_list置空,_number_of_threads线程数量为0, _number_of_non_daemon_threads非守护线程数量为0.
  21. vm_init_globals 初始化全局的数据结构,并且在堆中创建系统的class。
  22. 创建JavaThread主线程对象,并设置状态为_thread_in_vm。并且record_stack_base_and_size记录栈基址和大小,initialize_thread_local_storage初始化线程本地存储,set_active_handles设置active的JNIHandler.set_as_starting_thread将自己作为启动main线程,create_stack_guard_pages受保护的栈页。
  23. ObjectMonitor::Initialize() 初始化JVM的对象监控,主要之后同步系统使用.
  24. MemTracker::bootstrap_multi_thread() JVM此时进入多线程启动阶段,
  25. init_globals方法是初始化全局模块,主要包括字节码、classloader、stubroutines、os、codeCache、interpreter、jnihandles、javaClasses等模块的初始化.
  26. cache_global_variables缓存全局变量,jdk1.8中是空的实现.
  27. 加一个线程的MutexLocker锁,然后执行thrads::add方法将主线程加入线程list列表中,主要是统计线程数量。
  28. JvmtiExport::transition_pending_onload_raw_monitors()是JVMTI的接口回调.
  29. MemTracker::start() 启动native的内容记录。
  30. VMThread::create() 创建虚拟机线程
  31. os::create_thread(vmthread, os::vm_thread)创建OSThread对象.
  32. 加MutexLocker锁,启动VM的线程,直到启动完成
  33. VerifyDuringStartup判断是否启动中验证。默认是false.
  34. DumpSharedSpaces是否导出共享空间状态,默认是fallse,
  35. 执行JVMTI的两个回调函数,enter_start_phase和post_vm_start可以监听到JVM启动事件。
  36. 加载并初始化java.lang.String和java.lang.System。java.lang.OutOfMemoryError、java.lang.NullPointerException等系统类.
  37. 创建线程组,并创建初始化线程并设置主线程的Thread的InstanceClass对象。
  38. 设置标志位_init_completed=true, 记录hotspot启动结束.
  39. SystemDictionary::compute_java_system_loader(THREAD);调用Classloader类中getSystemClassLoader获取系统类加载器。
  40. 执行JVMTI回调enter_live_phase函数
  41. AttachListener::init()是attacheListener的初始化.
  42. os::signal_init()将线程加入线程组中,并初始化java处理操作系统的信号的数据结构.
  43. BiasedLocking::init() 偏向锁的初始化
  44. JvmtiExport::post_vm_initialized执行JVM初始化事件的回调.
  45. 判断EnableInvokeDynamic是否开启invokeDynamic指令,默认是开启的,同时初始化java.lang.invoke包的MethodHandle、MemberName、MethodHandleNatives类。
  46. WatcherThread::start() 启动观察线程.
  47. os::init_3() JVM启动结束后操作系统的初始化.启动 MemNotifyThread线程监控低内存的
  48. create_vm_timer.end() JVM启动记录结束。
  49. 设置_vm_complete等于true,并返回JNI_OK

2. 重要步骤

2. ostream_init() 初始化输出流模块

具体细节见后续单独章节。

4. os::init os() os模块初始化 

包括如下一些步骤:

  • 设置内存页大小
  • 初始化系统信息(处理器、物理内存等)
  • 初始化操作系统信息
  • 获取原生主线程的句柄
  • 初始化系统时钟

具体细节见后续单独章节。

5. init_system_properties() 

初始化系统配置参数,例如:java.class.path、java.home等参数,封装成SystemProperty, 放到一个全局链表_system_properties中。

同时在os::init_system_properties_values() 中 (linux下实现)

设置动态链接库目录 Arguments::set_dll_dir(buf);

设置 JAVA_HOM  Arguments::set_java_home(buf)

设置引导路径分隔符 set_boot_path('/', ':') - Arguments::set_sysclasspath(sysclasspath);

设置本地库的搜索路径 Arguments::set_library_path(ld_library_path);

设置扩展目录 Arguments::set_ext_dirs(buf); 

设置认可目录 Arguments::set_endorsed_dirs(buf);

设置系统jar包搜索路径

hotspot/src/share/vm/runtime/arguments.cpp
SystemProperty类继承自CHeapObj
// Initialize system properties key and value.
void Arguments::init_system_properties() {
    // 初始化系统配置参数,例如:java.class.path、java.home等参数, 封装成SystemProperty
    // _system_properties 可以算作一个全局变量
    // 而 SystemProperty里面自带一个对一个SystemProperty的引用,所以构成了一个链表
    PropertyList_add(&_system_properties, new SystemProperty("java.vm.specification.name",
                                                                 "Java Virtual Machine Specification",  false));
    PropertyList_add(&_system_properties, new SystemProperty("java.vm.version", VM_Version::vm_release(),  false));
    PropertyList_add(&_system_properties, new SystemProperty("java.vm.name", VM_Version::vm_name(),  false));
    PropertyList_add(&_system_properties, new SystemProperty("java.vm.info", VM_Version::vm_info_string(),  true));
    
    // following are JVMTI agent writeable properties.
    // Properties values are set to NULL and they are
    // os specific they are initialized in os::init_system_properties_values().
    _java_ext_dirs = new SystemProperty("java.ext.dirs", NULL,  true);
    _java_endorsed_dirs = new SystemProperty("java.endorsed.dirs", NULL,  true);
    _sun_boot_library_path = new SystemProperty("sun.boot.library.path", NULL,  true);
    _java_library_path = new SystemProperty("java.library.path", NULL,  true);
    _java_home =  new SystemProperty("java.home", NULL,  true);
    _sun_boot_class_path = new SystemProperty("sun.boot.class.path", NULL,  true);
    
    _java_class_path = new SystemProperty("java.class.path", "",  true);
    
    // Add to System Property list.
    PropertyList_add(&_system_properties, _java_ext_dirs);
    PropertyList_add(&_system_properties, _java_endorsed_dirs);
    PropertyList_add(&_system_properties, _sun_boot_library_path);
    PropertyList_add(&_system_properties, _java_library_path);
    PropertyList_add(&_system_properties, _java_home);
    PropertyList_add(&_system_properties, _java_class_path);
    PropertyList_add(&_system_properties, _sun_boot_class_path);
    
    // Set OS specific system properties values
    os::init_system_properties_values();
}

6. JDK_Version_init()

加载libverify.so、 libjava.so,初始化jdk的主、次版本号

void JDK_Version::initialize() {
    jdk_version_info info;
    assert(!_current.is_valid(), "Don't initialize twice");
    
    // 加载 libverify.so、libjava.so、( 定义__OpenBSD__时,加载libnet.so)
    void *lib_handle = os::native_java_library();
    jdk_version_info_fn_t func = CAST_TO_FN_PTR(jdk_version_info_fn_t,
    os::dll_lookup(lib_handle, "JDK_GetVersionInfo0"));
    
    if (func == NULL) {
        // JDK older than 1.6
        _current._partially_initialized = true;
    } else {
        (*func)(&info, sizeof(info));
        
        int major = JDK_VERSION_MAJOR(info.jdk_version);
        int minor = JDK_VERSION_MINOR(info.jdk_version);
        int micro = JDK_VERSION_MICRO(info.jdk_version);
        int build = JDK_VERSION_BUILD(info.jdk_version);
        if (major == 1 && minor > 4) {
        // We represent "1.5.0" as "5.0", but 1.4.2 as itself.
        major = minor;
        minor = micro;
        micro = 0;
    }
    // 使用解析后的版本信息初始化 _current 成员,包括主要版本、次要版本、微版本、更新版本、特殊更新版本、构建号以及其他相关的标志。
    _current = JDK_Version(major, minor, micro, info.update_version,
       info.special_update_version, build,
       info.thread_park_blocker == 1,
       info.post_vm_init_hook_enabled == 1,
       info.pending_list_uses_discovered_field == 1);
}
os::native_java_library()

主要作用是加载几个函数库:libverify.so、libjava.so、libnet.so, 很多重要的实现都以动态链接库的形式封装起来。

void* os::native_java_library() {
    if (_native_java_library == NULL) {
        char buffer[JVM_MAXPATHLEN];
        char ebuf[1024];
        
        // 加载 libverify.so,并装入内存
        if (dll_build_name(buffer, sizeof(buffer), Arguments::get_dll_dir(), "verify")) {
            dll_load(buffer, ebuf, sizeof(ebuf));
        }
        
        // 加载 libjava.so ,并装入内存,
        if (dll_build_name(buffer, sizeof(buffer), Arguments::get_dll_dir(), "java")) {
            // _native_java_library 指向加载后的函数库指针,方便后续符号解析时使用
            _native_java_library = dll_load(buffer, ebuf, sizeof(ebuf));
        }
        
        if (_native_java_library == NULL) {
            vm_exit_during_initialization("Unable to load native library", ebuf);
        }
        
        #if defined(__OpenBSD__)
        // 加载 libnet.so,并装入内存
        if (dll_build_name(buffer, sizeof(buffer), Arguments::get_dll_dir(), "net")) {
            dll_load(buffer, ebuf, sizeof(ebuf));
        }
        #endif
    }
    
    static jboolean onl oaded = JNI_FALSE;
    if (onLoaded) {
        // We may have to wait to fire onl oad until TLS is initialized.
        if (ThreadLocalStorage::is_initialized()) {
            const char *onLoadSymbols[] = JNI_ONLOAD_SYMBOLS;
            // 下面一段代码就是把 解析  libjava.so  中的JNI_OnLoad函数,并执行
            JNI_OnLoad_t JNI_OnLoad = CAST_TO_FN_PTR( JNI_OnLoad_t, dll_lookup(_native_java_library, onl oadSymbols[0]));
            if (JNI_OnLoad != NULL) {
                JavaThread* thread = JavaThread::current();
                ThreadToNativeFromVM ttn(thread);
                HandleMark hm(thread);
                jint ver = (*JNI_OnLoad)(&main_vm, NULL);
                onl oaded = JNI_TRUE;
                if (!Threads::is_supported_jni_version_including_1_1(ver)) {
                    vm_exit_during_initialization("Unsupported JNI version");
                }
            }
        }
    }
    
    // 最终返回的是libjava.so的函数库指针
    return _native_java_library;  
}

7. Arguments::parse() 参数解析

处理之前解析出来的JavaVMOption,给各种全局参数设值,设置了一些对象对齐值, 供后续不同的模块使用。

具体细节见后续单独章节。

8. os::init_before_ergo() 确定处理器核数和指令集

hotspot/src/share/vm/runtime/os.cpp
void os::init_before_ergo() {
    // 通过系统调用api,拿到有效的cpu核数
    initialize_initial_active_processor_count();
    
    // 大页初始化,默认是没有实现
    large_page_init();
    
    // 确定平台特性,即所使用的平台架构指令集, linux这里啥都没做
    VM_Version::init_before_ergo();
}

9. Arguments::apply_ergo

主要设置堆的物理大小以及初始化MetaSpace flags 和 alignments.

13. os::init_2()

主要针对内存、栈、线程等与os模块密切相关的部分进行初始化。

包括如下一些步骤:

  • 分配polling_page,
  • 没有开放使用内存屏障的情况下,分配mem_serialize_page
  • 分配共享内存,设置大页内存
  • 初始化内核信号,安装信号处理函数JVM_handle_linux_signal
  • 对线程栈进行一系列配置,比如设置线程栈基址、栈大小
  • 检测glibc版本,选择使用NPTL 还是 LinuxThreads模型
  • NUMA相关配置
  • 文件描述符数量相关配置,改为系统允许的最大大小
  • 初始化用于线程创建的锁
  • 初始化线程优先级策略

具体细节见后续单独章节。

15. intialize TLS()

ThreadLocalStorage::init()

void ThreadLocalStorage::init() {
    assert(!is_initialized(),"More than one attempt to initialize threadLocalStorage");
    pd_init(); // x86下空实现
    // ThreadLocalStorage::_thread_index 静态变量
    set_thread_index(os::allocate_thread_local_storage());
    generate_code_for_get_thread(); // x86下空实现
}


static void restore_thread_pointer(void* p) {
    Thread* thread = (Thread*) p;
    os::thread_local_storage_at_put(ThreadLocalStorage::thread_index(), thread);
}

int os::allocate_thread_local_storage() {
    pthread_key_t key;
    int rslt = pthread_key_create(&key, restore_thread_pointer);
    assert(rslt == 0, "cannot allocate thread local storage");
    return (int)key;
}

在多线程的环境下,进程内的所有线程共享进程的数据空间。因此全局变量为所有线程共享。

出于程序设计的需要,在程序设计中有时需要保存线程自己的全局变量,这种特殊的变量仅在线程内部有效。

试想如果你的一个线程里面嵌套调用了很多函数,而你又想在这些函数之间使用一个公共的变量,如常见的errno,它返回标准的错误码。

errno不应该是一个局部变量。几乎每个函数都应该可以访问他,但他又不能作为是一个全局变量。

所以我们需要声明的这个全局变量只属于我们当前这个实例线程(同一个void *(*start_routine)(void *)可以实例化很多线程),其他的线程访问不到,故引入线程特定数据TSD(TSD  thread specific data)来解决, 也叫 TLS thread local storage。

它的实现涉及高级编程语言、编译器和链接器的支持。

TSD采用了一键多值的技术,即一个键对应多个值。访问数据时都是通过键值来访问,好像是对一个变量进行访问,其实是在访问不同的数据。

目前使用TLS的方法有多种,POSIX的pthread.h提供了一组API来实现此功能

#include <pthread.h>

int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
// Returns 0 on success, or a positive error number on error
// 为线程局部数据创建一个新键,并通过key指向新创建的键缓冲区

int pthread_key_delete(pthread_key_t key);
// Returns 0 on success, or a positive error number on error

int pthread_setspecific(pthread_key_t key, const void *value);
// Returns 0 on success, or a positive error number on error

void* pthread_getspecific(pthread_key_t key);
// Returns pointer, or NULL if no thread-specific data is assciated with key

int pthread_key_create(pthread_key_t *key,  void (*destructor)(void*));

第一个参数为指向一个键值的指针,

第二个参数指明了一个destructor函数,如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。

key一旦被创建,所有线程都可以访问它,但各线程可根据自己的需要往key中填入不同的值,这就相当于提供了一个同名而不同值的全局变量,一键多值。

一键多值靠的是一个关键数据结构数组即TSD池,创建一个TSD就相当于将结构数组中的某一项设置为“in_use”,并将其索引返回给*key,然后设置清理函数。

参考:

Linux 线程本地存储TLS

TLS介绍和实现

21. vm_init_globals()

全局数据结构初始化

hotspot/src/share/vm/runtime/init.cpp
void vm_init_globals() {
    // 验证ThreadShadow的实现,主要是验证线程对象的_pending_exception属性位置,
    check_ThreadShadow();
    
    // 基础类型初始化
    basic_types_init();
    
    // 各种事件日志初始化,不影响主流程,先忽略
    eventlog_init(); 
    
    // 各种互斥锁的初始化,不影响主流程,先忽略
    mutex_init(); 
    
    // 内存(块)池初始化,可以复用之前申请的chunk,避免频繁去系统malloc(即delete后不通过系统释放,而是放到chunkpool里复用)
    chunkpool_init(); 
    
    /*
    * 热点性能数据存储内存初始化。热点性能数据的收集通过-XX:+UsePerfData控制开通与否,默认是开通的。
    * 启用 PerfData 后,HotSpot JVM 会在目录下为每个 JVM 进程创建一个文件。通过工具扫描此目录
    * 以查找正在运行的 JVM。如果禁用 PerfData,这些工具将不会自动发现JVM
    * jstat 是依赖于热点性能数据的标准 JDK 工具之一。 不适用于禁用了性能数据的 JVM。
    * 细节上,这块也可以忽略
    */
    perfMemory_init();
}
basic_types_init() 
hotspot/src/share/vm/utilities/globalDefinitions.cpp
void basic_types_init() {
    // 下面的操作就是验证各个类型的在32位和64位机器上的内存占用位数
    
#ifdef ASSERT
#ifdef _LP64
    assert(min_intx ==  (intx)CONST64(0x8000000000000000), "correct constant");
    assert(max_intx ==  CONST64(0x7FFFFFFFFFFFFFFF), "correct constant");
    assert(max_uintx == CONST64(0xFFFFFFFFFFFFFFFF), "correct constant");
    assert( 8 == sizeof( intx),      "wrong size for basic type");
    assert( 8 == sizeof( jobject),   "wrong size for basic type");
#else
    assert(min_intx ==  (intx)0x80000000,  "correct constant");
    assert(max_intx ==  0x7FFFFFFF,  "correct constant");
    assert(max_uintx == 0xFFFFFFFF,  "correct constant");
    assert( 4 == sizeof( intx),      "wrong size for basic type");
    assert( 4 == sizeof( jobject),   "wrong size for basic type");
#endif
    assert( (~max_juint) == 0,      "max_juint has all its bits");
    assert( (~max_uintx) == 0,      "max_uintx has all its bits");
    assert( (~max_julong) == 0,     "max_julong has all its bits");
    assert( 1 == sizeof( jbyte),     "wrong size for basic type");
    assert( 2 == sizeof( jchar),     "wrong size for basic type");
    assert( 2 == sizeof( jshort),    "wrong size for basic type");
    assert( 4 == sizeof( juint),     "wrong size for basic type");
    assert( 4 == sizeof( jint),      "wrong size for basic type");
    assert( 1 == sizeof( jboolean),  "wrong size for basic type");
    assert( 8 == sizeof( jlong),     "wrong size for basic type");
    assert( 4 == sizeof( jfloat),    "wrong size for basic type");
    assert( 8 == sizeof( jdouble),   "wrong size for basic type");
    assert( 1 == sizeof( u1),        "wrong size for basic type");
    assert( 2 == sizeof( u2),        "wrong size for basic type");
    assert( 4 == sizeof( u4),        "wrong size for basic type");
    
    // 验证各个基础类型代表的字符,比如B代表byte,C代表char,依此类推
    int num_type_chars = 0;
    for (int i = 0; i < 99; i++) {
        if (type2char((BasicType)i) != 0) {
            assert(char2type(type2char((BasicType)i)) == i, "proper inverses");
            num_type_chars++;
        }
    }
    // 类型个数只能是11个,8个基础类型 + 1个void类型 + 1个object类型 + 1个array类型
    assert(num_type_chars == 11, "must have tested the right number of mappings");
    assert(char2type(0) == T_ILLEGAL, "correct illegality");
    
    {
        for (int i = T_BOOLEAN; i <= T_CONFLICT; i++) {
            BasicType vt = (BasicType)i;
            BasicType ft = type2field[vt];
            switch (vt) {
                // the following types might plausibly show up in memory layouts:
                case T_BOOLEAN:
                case T_BYTE:
                case T_CHAR:
                case T_SHORT:
                case T_INT:
                case T_FLOAT:
                case T_DOUBLE:
                case T_LONG:
                case T_OBJECT:
                case T_ADDRESS:     // random raw pointer
                case T_METADATA:    // metadata pointer
                case T_NARROWOOP:   // compressed pointer
                case T_NARROWKLASS: // compressed klass pointer
                case T_CONFLICT:    // might as well support a bottom type
                case T_VOID:        // padding or other unaddressed word
                // layout type must map to itself
                assert(vt == ft, "");
                break;
                default:
                    // non-layout type must map to a (different) layout type
                    assert(vt != ft, "");
                    assert(ft == type2field[ft], "");
            }
            // every type must map to same-sized layout type:
            assert(type2size[vt] == type2size[ft], "");
        }
    }
    
    // These are assumed, e.g., when filling HeapWords with juints.
    assert(is_power_of_2(sizeof(juint)),            "juint must be power of 2");
    assert(is_power_of_2(HeapWordSize),             "HeapWordSize must be power of 2");
    assert((size_t)HeapWordSize >= sizeof(juint),   "HeapWord should be at least as large as juint");
    assert(sizeof(NULL) == sizeof(char*),           "NULL must be same size as pointer");
#endif
    
    // 设置Java与OS的线程优先级对应关系,
    // 默认都是-1(JavaPriority1_To_OSPriority到JavaPriority10_To_OSPriority初始值都是-1),
    // 也就是没有优先级
    if( JavaPriority1_To_OSPriority != -1 )
        os::java_to_os_priority[1] = JavaPriority1_To_OSPriority;
    if( JavaPriority2_To_OSPriority != -1 )
        os::java_to_os_priority[2] = JavaPriority2_To_OSPriority;
    if( JavaPriority3_To_OSPriority != -1 )
        os::java_to_os_priority[3] = JavaPriority3_To_OSPriority;
    if( JavaPriority4_To_OSPriority != -1 )
        os::java_to_os_priority[4] = JavaPriority4_To_OSPriority;
    if( JavaPriority5_To_OSPriority != -1 )
        os::java_to_os_priority[5] = JavaPriority5_To_OSPriority;
    if( JavaPriority6_To_OSPriority != -1 )
        os::java_to_os_priority[6] = JavaPriority6_To_OSPriority;
    if( JavaPriority7_To_OSPriority != -1 )
        os::java_to_os_priority[7] = JavaPriority7_To_OSPriority;
    if( JavaPriority8_To_OSPriority != -1 )
        os::java_to_os_priority[8] = JavaPriority8_To_OSPriority;
    if( JavaPriority9_To_OSPriority != -1 )
        os::java_to_os_priority[9] = JavaPriority9_To_OSPriority;
    if(JavaPriority10_To_OSPriority != -1 )
        os::java_to_os_priority[10] = JavaPriority10_To_OSPriority;
    
    // Set the size of basic types here (after argument parsing but before stub generation).
    if (UseCompressedOops) {  // 压缩指针
        // Size info for oops within java objects is fixed
        heapOopSize        = jintSize;       // 堆中oop对象指针, 4
        LogBytesPerHeapOop = LogBytesPerInt; // oop对象指针字节的幂次方,2
        LogBitsPerHeapOop  = LogBitsPerInt;  // oop对象指针位数的幂次方,5
        BytesPerHeapOop    = BytesPerInt;    // oop对象指针字节数, 4
        BitsPerHeapOop     = BitsPerInt;     // oop对象指针位数,32
    } else {
        heapOopSize        = oopSize; // 32位机器占4字节,64位机器占8字节
        LogBytesPerHeapOop = LogBytesPerWord;// 32位机器为2,64位机器为3
        LogBitsPerHeapOop  = LogBitsPerWord; // 32位机器为5,64位机器为6
        BytesPerHeapOop    = BytesPerWord; // 32位机器为4字节,64位机器为8字节,
        BitsPerHeapOop     = BitsPerWord; // 32位机器为32位,64位机器为64位
    }
    // object和array在jvm中的表现都是指针,所以就是 heapOopSize 的值
    _type2aelembytes[T_OBJECT] = heapOopSize;
    _type2aelembytes[T_ARRAY]  = heapOopSize;
}

enum BasicType {
    T_BOOLEAN     =  4,
    T_CHAR        =  5,
    T_FLOAT       =  6,
    T_DOUBLE      =  7,
    T_BYTE        =  8,
    T_SHORT       =  9,
    T_INT         = 10,
    T_LONG        = 11,
    T_OBJECT      = 12,
    T_ARRAY       = 13,
    T_VOID        = 14,
    T_ADDRESS     = 15,
    T_NARROWOOP   = 16,
    T_METADATA    = 17,
    T_NARROWKLASS = 18,
    T_CONFLICT    = 19, // for stack value type with conflicting contents
    T_ILLEGAL     = 99
};
mutex_init()

在这里,你可以看到vm中所需要的各种锁。

void mutex_init() {
    def(tty_lock                     , Mutex  , event,       true ); // allow to lock in VM
    
    def(CGC_lock                   , Monitor, special,     true ); // coordinate between fore- and background GC
    def(STS_lock                   , Monitor, leaf,        true );
    if (UseConcMarkSweepGC) {
        def(iCMS_lock                  , Monitor, special,     true ); // CMS incremental mode start/stop notification
    }
    if (UseConcMarkSweepGC || UseG1GC) {
        def(FullGCCount_lock           , Monitor, leaf,        true ); // in support of ExplicitGCInvokesConcurrent
    }
    if (UseG1GC) {
        def(CMark_lock                 , Monitor, nonleaf,     true ); // coordinate concurrent mark thread
        def(CMRegionStack_lock         , Mutex,   leaf,        true );
         ...............
    }
    
    def(ParGCRareEvent_lock          , Mutex  , leaf     ,   true );
    def(DerivedPointerTableGC_lock   , Mutex,   leaf,        true );
    ...............
#ifndef PRODUCT
    def(FullGCALot_lock              , Mutex  , leaf,        false); // a lock to make FullGCALot MT safe
#endif
    def(BeforeExit_lock              , Monitor, leaf,        true );
    def(PerfDataMemAlloc_lock        , Mutex  , leaf,        true ); // used for allocating PerfData memory for performance data
    def(PerfDataManager_lock         , Mutex  , leaf,        true ); // used for synchronized access to PerfDataManager resources
     ...............
    // CMS_modUnionTable_lock                   leaf
    // CMS_bitMap_lock                          leaf + 1
    // CMS_freeList_lock                        leaf + 2
    
    def(Safepoint_lock               , Monitor, safepoint,   true ); // locks SnippetCache_lock/Threads_lock
    
    def(Threads_lock                 , Monitor, barrier,     true );
    
    
    
#if INCLUDE_JFR
    def(JfrMsg_lock                  , Monitor, leaf,        true);
    。。。。。。
    #ifndef SUPPORTS_NATIVE_CX8
    def(JfrCounters_lock             , Mutex,   special,     false);
    #endif
#endif
    
#ifndef SUPPORTS_NATIVE_CX8
    def(UnsafeJlong_lock             , Mutex,   special,     false);
#endif
}
chunkpool_init()

内存(块)池初始化,可以复用之前申请的内存块(chunk),避免频繁去系统malloc(即delete后不通过系统释放,而是放到chunkpool里复用)

所谓的ChunkPool 就是一个Chunk的链表(Chunk通过_next链接),这里的初始化,生成了四种size的chunkpool,并为每个pool生成了第一个chunk.

hotspot/src/share/vm/memory/allocation.cpp
void chunkpool_init() {
    ChunkPool::initialize();
}

22. main_thread() 为“main线程”补个证

线程的属性

main_thread的创建

hotspot/src/share/vm/runtime/thread.cpp create_vm() 中创建main_thread 部分

hotspot/src/share/vm/runtime/thread.cpp
    /*
    绑定主线程到os级线程(在此处,也就是当前线程),JavaThread是继承自Thread,
    注意,这里的JavaThread只是虚拟机层面的Java级线程,并不是我们Java编码层面的线程,
    这个概念要区分来,但是Java编码层面的线程Thread,最终在虚拟机底层就是用JavaThread来表示的,
    从这点看,两者又是一样,抛开编码层面,就可以总结为:JavaThread就是一个Java线程的表示。
    */
    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());
    
    // 将当前线程 与 一个OSThread绑定到一块
    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 again
        return JNI_ENOMEM;
    }
    
    // 创建线程栈保护区(将保护区那几页设为)
    main_thread->create_stack_guard_pages();

25. init_globals 全局模块初始化

hotspot/src/share/vm/runtime/init.cpp
jint init_globals() {
    HandleMark hm;
    // 管理模块初始化,包括时间统计、各种指标计数、性能数据统计、运行时数据统计和监控、类加载服务情况(加载类数量、加载类失败数量、加载字节数等)
    management_init();
    
    // 字节码初始化。Hotspot执行字节码时,大部分是解释执行,这就需要将字节码转换成机器码来执行,执行前需要知道字节码是什么格式、类型、占用长度、结果类型等信息,这一步就是做这些初始化工作的
    bytecodes_init();
    
    // 类加载器初始化
    classLoader_init();
    
    // 代码缓存空间初始化。虚拟机为了提升执行绩效,会把一些热点指令代码提前编译为机器码并缓存起来,这一步就是做这个操作的
    codeCache_init();
    
    // 虚拟机版本号初始化
    VM_Version_init();
    
    // os的扩展初始化,jdk8版本的hotspot里,这个函数没做任何具体实现
    os_init_globals();
    
    // JVM虚拟机调用Java程序时需要借助stub存根来处理,大家做rpc/webservice就了解stub存根的意义,就是对远程调用函数的一个声明/映射,
    // 这里的stubRoutines的意思与这个差不多,这个比较关键,后面章节会重点细讲
    stubRoutines_init1();
    
    // universe 翻译就是万物的意思,这一步初始化,主要是对元空间内存、各种符号表、字符表、Java堆内存空间相关的初始化
    jint status = universe_init();  
    
    if (status != JNI_OK)
        return status;
    
    // 解释器初始化
    interpreter_init();  
    
    // 方法调用计数初始化
    invocationCounter_init();  
    
    // 标记清除初始化,GC相关的,后面讲GC时再讲
    marksweep_init();
    
    // 这个仅仅是校验AccessFlags的大小,里面是一个assert断言
    accessFlags_init();
    
    // 模板表 TemplateTable 的初始化,TemplateTable中保存了各个字节码的执行模板(目标代码生成函数和参数)
    templateTable_init();
    
    // 这个函数没做什么,就是通过系统调用srand设置了随机数的随机种子,方便rand()调用时根据种子生成一个伪随机数
    InterfaceSupport_init();
    
    // 生成运行时的一些stub函数,例如错误方法处理的stub、静态解析处理的stub等
    SharedRuntime::generate_stubs();
    
    // universe_init的进一步初始化,里面内容很多,主要概括为对基础数据类型、数组类型、对象类型创建对应的Klass对象、bootstrap类加载器的初始化等
    universe2_init();  
    
    // 引用处理   referenceProcessor 初始化 
    referenceProcessor_init();
    
    // JNI句柄分配空间块并初始化
    jni_handles_init();
    
    #if INCLUDE_VM_STRUCTS
    vmStructs_init(); // vm内部数据结构的初始化
    #endif // INCLUDE_VM_STRUCTS
    
    // 初始化vtable数组的大小,并预分配数组空间,vtable(虚函数表)是c++中对虚函数表的存储和表示
    vtableStubs_init();
    
    // 代码缓冲区初始化
    InlineCacheBuffer_init();
    
    // oracle编译器初始化
    compilerOracle_init();
    
    // 代理编译初始化,主要做两件事:1.选择编译器;2.如何进行编译
    compilationPolicy_init();
    
    // 编译日志记录
    compileBroker_init();
    
    // 初始化寄存器数组,给每个寄存器初始化名字,汇编代码需要
    VMRegImpl::set_regName();
    
    // 初始化universe后的逻辑操作,包括加载基础类、构建报错信息、安全检查、加载器、引用管理等,用过spring的就知道里面有很多post后置逻辑操作
    if (!universe_post_init()) {
        return JNI_ERR;
    }
    // 系统及java class的初始化
    javaClasses_init();  
    
    // stubRoutines的第二阶段初始化
    stubRoutines_init2(); 
    
    #if INCLUDE_NMT
    NMT_stack_walkable = true;
    #endif // INCLUDE_NMT
    
    // 打印完成标志
    if (PrintFlagsFinal) {
        CommandLineFlags::printFlags(tty, false);
    }
    // 返回初始化成功标志
    return JNI_OK;
}

30. 创建VMThread

在执行命令 java [options] xxx.class param1 param2 ... paramn 时,实际上Linux是fork()出了一个新的进程(Java进程)来执行后续操作,并且在执行的过程中又会创建其他的线程(Java线程、VM线程、GC线程等)。

在Java执行的过程中,是需要Java线程与VM双向交互的,例如:在线程的启停时需要执行一些必要的任务(比如保存安全点),以及垃圾收集期间线程的协调和同步,所以Hotspot JVM需要管理这些线程。

但是线程又是由操作系统创建的,不是JVM创建的,因此在Hotspot中,每个Java线程都会映射到一个操作系统线程,同时需要跟踪和管理这些线程,确保Hotspot内部的状态与线程本身状态同步。

这也是之前提到的 Thread(java层) - osThread(vm层) - 内核线程(内核级别)三层体系。

vm中会有很多的线程, 而VMThread线程则只会有一个,它的作用是啥,后续我们再来补上。

{ MutexLocker mu(Threads_lock);
     // 这里继续之前主线程的处理,最终要把主线程 main_thread 加入到线程链(表)中
    Threads::add(main_thread);
  }

  // Any JVMTI raw monitors entered in onl oad will transition into
  // real raw monitor. VM is setup enough here for raw monitor enter.
  JvmtiExport::transition_pending_onload_raw_monitors();

    // Create the VMThread
    { 
    TraceTime timer("Start VMThread", TraceStartupTime);
    VMThread::create(); // 创建VMThread 对象
    
    Thread* vmthread = VMThread::vm_thread(); // 拿到上面创建的 VMThread 对象
    
    if (!os::create_thread(vmthread, os::vm_thread))
        vm_exit_during_initialization("Cannot create VM thread. Out of system resources.");
    
        // vmthread 创建完后,等待运行
        {
            MutexLocker ml(Notify_lock);
            // 父子线程handshaking
            os::start_thread(vmthread);  
            while (vmthread->active_handles() == NULL) {
                Notify_lock->wait();
            }
        }
    }

    assert (Universe::is_fully_initialized(), "not initialized");
    if (VerifyDuringStartup) { // 默认是 false
        // Make sure we're starting with a clean slate.
        VM_Verify verify_op;
        VMThread::execute(&verify_op);  // 执行验证操作
    }

36. 初始化核心类

java.lang.String 、 java.lang.System 、 java.lang.ThreadGroup 等

{
    TraceTime timer("Initialize java.lang classes", TraceStartupTime);
    
    if (EagerXrunInit && Arguments::init_libraries_at_startup()) {
        create_vm_init_libraries();
    }
    // 初始化String类
    initialize_class(vmSymbols::java_lang_String(), CHECK_0);
    
    // Initialize java_lang.System (needed before creating the thread)
    // 初始化System类、ThreadGroup类
    initialize_class(vmSymbols::java_lang_System(), CHECK_0);
    initialize_class(vmSymbols::java_lang_ThreadGroup(), CHECK_0);
    
    // 创建一个名为“main"的ThreadGroup oop,其parent字段为 一个名为"system"的ThreadGroup     
    Handle thread_group = create_initial_thread_group(CHECK_0);
    // 有一个全局变量 oop _main_thread_group, 为其赋值
    Universe::set_main_thread_group(thread_group());
    
    // 初始化Thread类           
    initialize_class(vmSymbols::java_lang_Thread(), CHECK_0);
    // 为main_thread创建一个Java层面的Thread对象
    oop thread_object = create_initial_thread(thread_group, main_thread, CHECK_0);
    main_thread->set_threadObj(thread_object);    
    // Set thread status to running since main thread has been started and running.
    java_lang_Thread::set_thread_status(thread_object, ava_lang_Thread::RUNNABLE);
    
    // 初始化Class类
    // The VM creates & returns objects of this class. Make sure it's initialized.
    initialize_class(vmSymbols::java_lang_Class(), CHECK_0);
    
    // The VM preresolves methods to these classes. Make sure that they get initialized
    initialize_class(vmSymbols::java_lang_reflect_Method(), CHECK_0);
    initialize_class(vmSymbols::java_lang_ref_Finalizer(),  CHECK_0);
    
    // 调用System类的 initializeSystemClass()
    call_initializeSystemClass(CHECK_0);
    
    // get the Java runtime name after java.lang.System is initialized
    JDK_Version::set_runtime_name(get_java_runtime_name(THREAD));
    JDK_Version::set_runtime_version(get_java_runtime_version(THREAD));
    
    // an instance of OutOfMemory exception has been allocated earlier
    initialize_class(vmSymbols::java_lang_OutOfMemoryError(), CHECK_0);
    initialize_class(vmSymbols::java_lang_NullPointerException(), CHECK_0);
    initialize_class(vmSymbols::java_lang_ClassCastException(), CHECK_0);
    initialize_class(vmSymbols::java_lang_ArrayStoreException(), CHECK_0);
    initialize_class(vmSymbols::java_lang_ArithmeticException(), CHECK_0);
    initialize_class(vmSymbols::java_lang_StackOverflowError(), CHECK_0);
    initialize_class(vmSymbols::java_lang_IllegalMonitorStateException(), CHECK_0);
    initialize_class(vmSymbols::java_lang_IllegalArgumentException(), CHECK_0);
  }

42. os::signal_init()

将线程加入线程组中,并初始化java处理操作系统的信号的数据结构.

43. BiasedLocking::init() 偏向锁的初始化

void BiasedLocking::init() {
    if (UseBiasedLocking) {
        // 如果设置了启动延迟时间(BiasedLockingStartupDelay),
        // 则创建一个定时任务(EnableBiasedLockingTask),在程序运行一段时间后启动,以减轻启动时偏向锁撤销所带来的性能影响。
        // 这是因为在 JVM 启动过程中,由于偏向锁撤销而频繁触发安全点,可能会导致性能下降。
        if (BiasedLockingStartupDelay > 0) { // BiasedLockingStartupDelay 默认时间是 4000 毫秒,
            EnableBiasedLockingTask* task = new EnableBiasedLockingTask(BiasedLockingStartupDelay);
            task->enroll();
        } else { // 如果没有设置启动延迟时间,直接执行偏向锁启用操作(VM_EnableBiasedLocking)
            VM_EnableBiasedLocking op(false);
            VMThread::execute(&op); // 交由VMThread执行
        }
    }
}

总结

本章中大部分内容都没有展开详细说明,因为想在一篇或者几篇文章中把细节展开是不可能的。

同时,这里仍处在走马观花的阶段,有个印象就可以了。

在后续的章节里,我们会慢慢深挖细节,不用着急。

附录

宏 _JNI_IMPORT_OR_EXPORT_

__attribute__((visibility("default")))

gcc编译参数

-fvisibility=default / internal / hidden / protected

上述表示:

 gcc在编译动态库的时候visibility有四个选项,只有使用default和protected选项编译时,编译出来的动态库的符号是可以被外界调用的;

 而编译时使用internal和hidden选项时,如果函数内没有:__attribute ((visibility("default")))声明,动态库使隐藏的不可被外界调用。

查看物理CPU个数

# 查看物理CPU个数

cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l

# 查看每个物理CPU中core的个数(即核数)

cat /proc/cpuinfo| grep "cpu cores"| uniq

Debug模式

xdebug简单来说就是远程的debug模式,通过开启JVM的debug模式来达到远程断点(debug)的效果,线上代码本地可以进行debug运行查看


要让远程服务器运行的代码支持远程调试,则服务端启动的时候必须加上特定的 JVM 参数,这些参数是:
    不同的JDK版本需要设置不同的配置:
        JDK 9 or later
            -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:9999 
        JDK 5-8
            -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9999
        JDK 1.4.x
            -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9999
        JDK 1.3.x or earlier
            -Xnoagent -Djava.compiler=NONE -Xdebug
            -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9999

这里的9999则是服务端开放的端口,后期客户端IDEA需要连接当前端口进行远程交互和调试。
但是我们需要注意的是,这个9999端口在服务端一定要放开防火墙或者安全组;

具体端口看项目需求;运行服务端jar包程序则如下(JDK 5-8版本):
    java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9999 ./SwaggerDemo-0.0.1-SNAPSHOT.jar > app.log &

 JVM 的 -X参数是非标准选项,在不同的版本的 JVM 中,参数可能不同,可通过 java -X查看参数。
   
-Xdebug:通知JVM工作在debug模式下;
-Xnoagent 禁用默认sun.tools.debug调试器。
-Djava.compiler=NONE 禁止 JIT 编译器的加载。
-Xrunjdwp:通知JVM使用(java debug wire protocol)来运行调试环境;加载JDWP的JPDA参考执行实例
transport:监听Socket端口连接方式(也可以dt_shmem共享内存方式,但限于windows机器,并且服务提供端和调试端只能位于同一台机);
server:server=y表示当前是调试服务端,=n表示当前是调试客户端
suspend:suspend=n表示启动时不中断,一般用于设置主动连接;suspend=y表示启动时就进入调试模式,一般用于被动连接
address=prot 表示远程debug所开放的端口号

对象头

栈保护页

了解保护页,先从几个问题开始吧

1. 为什么线程栈有栈帧了,还要有保护页?

答:在操作系统中内存可以看成是一个大数组,这就有一个问题,线程之间可能会互相踩了别人的内存空间,所以栈空间也存在这个问题。

为了防止栈溢出时破坏栈之外的数据结构,语言运行时会保留最大栈上限limit所在的一片区域,这就是保护页(Guard Page),也可叫哨兵值(Sentry)。当函数返回时检查保护页的值,如果被修改,说明已到达最大栈上限,此时就要输出错误并终止程序。

2. Java栈溢出后,保护页的作用?

答:Java也有栈溢出,发生时会抛出StackOverflowError,输出调用栈和代码行数。这些过程都需要额外执行很多方法,但是发生栈溢出就意味着不能继续执行方法了(因为方法执行需要栈空间)。

为了解决这个问题,HotSpot虚拟机在C++语言运行时提供的保护页之外会使用create_stack_guard_pages()创建额外的保护页来支持栈溢出错误处理,如下所示。

// Java thread:
//
//   Low memory addresses
//    +------------------------+
//    |                        |\  JavaThread created by VM does not have glibc
//    |    glibc guard page    | - guard, attached Java thread usually has
//    |                        |/  1 page glibc guard.
// P1 +------------------------+ Thread::stack_base() - Thread::stack_size()
//    |                        |\
//    |  HotSpot Guard Pages   | - red and yellow pages
//    |                        |/
//    +------------------------+ JavaThread::stack_yellow_zone_base()
//    |                        |\
//    |      Normal Stack      | -
//    |                        |/
// P2 +------------------------+ Thread::stack_base()
//
// Non-Java thread:
//
//   Low memory addresses
//    +------------------------+
//    |                        |\
//    |  glibc guard page      | - usually 1 page
//    |                        |/
// P1 +------------------------+ Thread::stack_base() - Thread::stack_size()
//    |                        |\
//    |      Normal Stack      | -
//    |                        |/
// P2 +------------------------+ Thread::stack_base()
//
// ** P1 (aka bottom) and size ( P2 = P1 - size) are the address and stack size returned from
//    pthread_attr_getstack()

3.保护页有几种类型及各类型的作用?

答:线程栈的最大上限处会保留三块保护页(Guard Page)支持栈溢出,分别是Reserved Page、Yellow Page、Red Page。

主要内容分析如下:

Reserved Page:Reserved Page旨在为一些关键段(Critical Section)方法保存外栈空间,让有@ jdk.internal.vm.annotation.ReservedStackAccess注解的方法能完成执行(如lock与unlock之间的代码),防止关键段方法中的对象出现不一致的状态。

当执行关键段方法时分配的栈顶触及Reserved Page,则虚拟机会将Reserved Page标记为正常栈空间,供关键段方法完成执行,然后再抛出StackOVerflowError。Reserved Page的大小由-XX:StackReservedPages指定。

Yellow Page:如果执行Java代码时分配的栈顶触及YellowPage,则虚拟机会抛出StackOverflowError,然后将Yellow Page标为正常栈空间,让抛异常的代码有栈可用。

Yellow Page的数量由参数-XX:StackYellowPages=指定,最后Yellow Page占用的空间是page数量*page大小(page的大小一般是4KB,如果开启-XX:+UseLargePages且操作系统支持large page特性,page的大小可达到4MB,但是一般都不用)。

Red Page:如果执行Java代码时分配的栈顶触及Red Page,则虚拟机会创建错误日志hs_err_pid.log,然后关闭虚拟机。同样,为了让创建日志的代码执行,虚拟机会将Red Page标为正常栈空间。RedPage的大小由-XX:StackRedPages指定。

Shadow Page:前面区域都是执行Java代码出现栈溢出的错误处理。虚拟机还可能执行native方法或者虚拟机本身需要执行的方法,这些方法的栈大小不像Java代码一样能确定(编译器能确定但是虚拟机不能),

如果开启虚拟机参数-XX:+UseStackBanging,JVM会分配一块足够大的Shadow Page执行,如果RSP(栈顶指针)超出Shadow Page区则抛出StackOverflowError。

这里所说的栈异常处理的逻辑在JVM的信号处理函数中(后续有专门章节描述)

C++各种变量可见性、生命周期

变量类型

何处定义

修饰属性

多执行单元共享or独享

生命周期

可见性

全局变量

在函数外

auto

共享

整个程序

全局可见

全局线程变量

在函数外

auto

独享

整个程序

全局可见

全局静态变量

在函数外

static

共享

整个程序

同一.c文件可见

全局静态线程变量

在函数外

static

独享

整个程序

同一.c文件可见

局部变量

在函数内

auto

独享

只在函数开始执行到结束这段时间

函数内

静态局部变量

在函数内

static

共享

整个程序

函数内可见

静态局部线程变量

在函数内

static

独享

整个程序

函数内可见

参考

/proc/pid/stat字段说明_/proc/id/stat-CSDN博客

链接、装载与库 --- 运行库 | Technology Blog (markrepo.github.io)

使用IDEA远程Debug调试(详细) - 蚂蚁小哥 - 博客园 (cnblogs.com)

JVM.dll装载过程与源代码分析_jvm.dll源码-CSDN博客

Hotspot 内存管理之CodeCache 源码解析_could not reserve enough space for code cache-CSDN博客

深入理解堆外内存 Metaspace_Javadoop

C++多态虚函数表详解(多重继承、多继承情况)_一个类有几个虚函数表-CSDN博客

Hotspot Java对象创建和TLAB源码解析_threadlocalallocbuffer::clear_before_allocation-CSDN博客

标签:初始化,java,thread,create,vm,VM,init,线程,os
From: https://blog.csdn.net/capaabbcc/article/details/142427951

相关文章

  • 中秋献礼!2024年中科院一区极光优化算法+分解对比!VMD-PLO-Transformer-LSTM多变量时间
    中秋献礼!2024年中科院一区极光优化算法+分解对比!VMD-PLO-Transformer-LSTM多变量时间序列光伏功率预测目录中秋献礼!2024年中科院一区极光优化算法+分解对比!VMD-PLO-Transformer-LSTM多变量时间序列光伏功率预测效果一览基本介绍程序设计参考资料效果一览基本介绍1.中秋献礼!2024年......
  • 分类预测 | Matlab实现FA-FS-SVM萤火虫算法同步优化特征选择结合支持向量机分类预测
    分类预测|Matlab实现FA-FS-SVM萤火虫算法同步优化特征选择结合支持向量机分类预测目录分类预测|Matlab实现FA-FS-SVM萤火虫算法同步优化特征选择结合支持向量机分类预测效果一览基本介绍程序设计参考资料效果一览基本介绍Matlab实现FA-FS-SVM萤火虫算法同步优化特征选择结合......
  • JVM虚拟机总结
        读了周志明老师的《深入理解Java虚拟机:JVM高级特性与最佳实践》第三版,总结一下里面的知识点。一方面是知识储备更多一些,另外是也为接下来的面试准备一下。    全书分为13个章节,共5部分内容。我着重是看了jvm的内管管理、垃圾收集与内存分配策略、虚拟机故障......
  • 【VMware ESXi】如何查看启用内存分层功能的 ESXi 主机使用了多少 NVMe 内存。
    VMwarevSphere8U3中作为技术预览所引入的功能“内存分层(MemoryTiering)”,相信大家已经在自己的测试或实验环境中应用并验证了,如果你还不知道,请跳转到这篇(把硬盘当内存用?VMware内存分层(MemoryTiering),你值得拥有!)文章了解相关介绍以及如何启用它。需要注意的是,目前在启用内存......
  • JVM内存区域详解及DirectByteBuffer内存
    Java虚拟机(JVM)是Java程序运行的基础,它为Java程序提供了一个与平台无关的执行环境。JVM内存区域的划分对于理解Java程序的运行机制至关重要。本文将详细介绍JVM的内存区域,并探讨对外内存中的DirectByteBuffer。方法区(MethodArea)方法区是JVM中所有线程共享的内存区域。它主......
  • VMWare安装Ubuntu之后与Windows系统共享文件夹的设置步骤
    1.首先在Windows系统中新建一个需要共享的文件夹,并设置文件夹的共享属性,如下图: 2.VMWare软件开启【共享文件夹】功能,如图所示3.进入Ubuntu系统,查看是否存在/mnt/hgfs目录,若是没有,先要以root权限建立该目录sudomkdir/mnt/hgfs4.挂载目录sudovmhgfs-fuse.host:......
  • 基于RHEL 9 搭建 KVM 虚拟化环境
    一、准备工作1.检查硬件虚拟化支持KVM要求处理器支持硬件虚拟化技术:IntelVT-x(虚拟化技术扩展)或AMD-V(虚拟化技术扩展)。检查方法:使用以下命令检查CPU是否支持虚拟化:egrep'(vmx|svm)'/proc/cpuinfo输出含有vmx的为Intel处理器支持虚拟化输出含有svm的为A......
  • 2024年JCR一区极光优化算法+分解对比!VMD-PLO-Transformer-BiLSTM多变量时间序列光伏功
    中秋献礼!2024年中科院一区极光优化算法+分解对比!VMD-PLO-Transformer-LSTM多变量时间序列光伏功率预测目录中秋献礼!2024年中科院一区极光优化算法+分解对比!VMD-PLO-Transformer-LSTM多变量时间序列光伏功率预测效果一览基本介绍程序设计参考资料效果一览基本介绍1.中秋献礼!2024年......
  • 【Java】JVM基本组成
    一、JDK、JRE、JVM        JDK:全称“JavaDevelopmentKit”Java开发工具包,提供javac编译器、jheap、jconsole等监控工具;        JRE:全称“JavaRuntimeEnvironment” Java运行环境,提供classLibrary核心类库+JVM;             ......
  • 图解VMware通过NAT模式实现互联互通案例
    一、概述在VMware中安装虚拟主机,其中一项重要的任务就是实现互联连互通,达成以下目标:虚拟主机实现能够随时上互联网。虚拟主机与宿主机之间互联访问。虚拟主机之间互联访问。    其实VMware不仅具有虚拟主机功能,还具有虚拟网络功能,正是有了虚拟网络的支持,处在同网段的......