经过前文《深入剖析java.c文件中JavaMain方法中InitializeJVM的实现》的分析,找到了创建Java虚拟机具体实现的方法Threads::create_vm((JavaVMInitArgs*) args, &can_try_again)。该方法的实现在src\hotspot\share\runtime\threads.cpp文件,我去掉了部分英文注释和宏条件代码,代码更简洁清晰,下面我们看看该方法的具体细节:

jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
// JDK版本信息初始化前的准备工作
extern void JDK_Version_init();
VM_Version::early_initialize();
// 检查版本是否支持
if (!is_supported_jni_version(args->version)) return JNI_EVERSION;
// 初始化基于库的TLS(线程本地存储)
ThreadLocalStorage::init();
// 初始化输出流模块
ostream_init();
// 处理Java启动器属性
Arguments::process_sun_java_launcher_properties(args); // 初始化操作系统模块,其中包括:获取进程ID、初始化系统信息、
// 设置流模式、设置虚拟内存页大小、初始化主进程句柄和主线程句柄等
os::init(); //该代码的作用是在MacOS AARCH64架构下启用当前线程的写执行保护
MACOS_AARCH64_ONLY(os::current_thread_enable_wx(WXWrite));
// 创建TraceVmCreationTime对象create_vm_timer,开始统计虚拟机创建时间
TraceVmCreationTime create_vm_timer;
create_vm_timer.start(); // 初始化系统属性
Arguments::init_system_properties();
// 初始化JDK版本,以便在解析参数时使用
JDK_Version_init();
// 在JDK版本号已知后,更新或初始化系统属性
Arguments::init_version_specific_system_properties();
// 在解析参数之前,确保初始化日志配置
LogConfiguration::initialize(create_vm_timer.begin_time()); // 解析参数
jint parse_result = Arguments::parse(args);
if (parse_result != JNI_OK) return parse_result; // 初始化内存跟踪
MemTracker::initialize();
// 初始化初始的活动处理器数量、大页面支持、栈溢出大小和VM版本
os::init_before_ergo(); // 设置堆大小,初始化元空间标志和对齐,设置编译器标志和
// 字节码重写标志,以及根据激进优化标志设置标志
jint ergo_result = Arguments::apply_ergo();
if (ergo_result != JNI_OK) return ergo_result;
// 检查上述init_before_ergo()和apply_ergo()对JVM的设置
if (!JVMFlagLimit::check_all_ranges()) {
return JNI_EINVAL;
}
// 检查上述init_before_ergo()和apply_ergo()对JVM的约束设置
bool constraint_result = JVMFlagLimit::check_all_constraints(
JVMFlagConstraintPhase::AfterErgo);
if (!constraint_result) {
return JNI_EINVAL;
}
// 如果上述检查有问题,则暂停虚拟机初始化
if (PauseAtStartup) {
os::pause();
} // 正式开始虚拟机的初始化
HOTSPOT_VM_INIT_BEGIN(); // 使用TraceTime计时器记录“Create VM”这个操作的时间,并将时间记录
// 在TRACETIME_LOG中的Info级别的startuptime日志中
TraceTime timer("Create VM", TRACETIME_LOG(Info, startuptime)); // 在全局参数解析后,初始化一些操作系统相关的设置,
// 包括设置互斥锁初始化完成标志、配置Windows异常处理、
// 检查和设置最小堆栈大小、注册退出函数等
jint os_init_2_result = os::init_2();
if (os_init_2_result != JNI_OK) return os_init_2_result; // 初始化安全点机制
SafepointMechanism::initialize(); // 根据操作系统的结果进行调整
jint adjust_after_os_result = Arguments::adjust_after_os();
if (adjust_after_os_result != JNI_OK) return adjust_after_os_result; // 初始化输出流日志
ostream_init_log(); // 加载 -agentlib/-agentpath 和转化后的 -Xrun agents
JvmtiAgentList::load_agents(); // 初始化线程状态
_number_of_threads = 0;
_number_of_non_daemon_threads = 0; // 初始化虚拟机的全局变量,包括:基本类型、事件日志、
// 互斥锁、面向对象存储集、性能内存、可挂起线程集合等
vm_init_globals(); // 初始化Java线程的旧计数器,如果JVMCI计数器大小大于0,
// 则会分配一个新的C堆数组来存储旧计数器,并将其初始化为0。
// 否则,将JavaThread::_jvmci_old_thread_counters设置为nullptr。
// 这段代码主要是为了支持JVMCI,该技术可以在运行时优化Java应用程序
#if INCLUDE_JVMCI
if (JVMCICounterSize > 0) {
JavaThread::_jvmci_old_thread_counters = NEW_C_HEAP_ARRAY(jlong, JVMCICounterSize, mtJVMCI);
memset(JavaThread::_jvmci_old_thread_counters, 0, sizeof(jlong) * JVMCICounterSize);
} else {
JavaThread::_jvmci_old_thread_counters = nullptr;
}
#endif // INCLUDE_JVMCI // 给当前线程对象初始化面向对象存储集
JavaThread::_thread_oop_storage = OopStorageSet::create_strong(
"Thread OopStorage", mtThread); // 创建一个JVM主线程,并将它挂载到当前操作系统线程上
JavaThread* main_thread = new JavaThread();
// 设置了线程的状态
main_thread->set_thread_state(_thread_in_vm);
// 初始化JVM主线程
main_thread->initialize_thread_current();
// 记录了线程的栈基址和大小 before set_active_handles
main_thread->record_stack_base_and_size();
// 将线程栈注册到NMT中
main_thread->register_thread_stack_with_NMT();
// 分配了一个JNIHandleBlock对象,为线程设置活动句柄
main_thread->set_active_handles(JNIHandleBlock::allocate_block());
// 在特定平台上对当前线程启用写保护
MACOS_AARCH64_ONLY(main_thread->init_wx());
// 以下if代码作用是:检查主线程是否被设置为起始线程,
// 如果没有成功设置,则会发生内部分配失败,导致虚拟机初始化失败,
// 此时会关闭虚拟机并返回错误代码JNI_ENOMEM
if (!main_thread->set_as_starting_thread()) {
vm_shutdown_during_initialization("Failed necessary
internal allocation. Out of swap space");
main_thread->smr_delete();
*canTryAgain = false;
return JNI_ENOMEM;
}
// 在主线程创建后启用保护页,以避免Linux VM 崩溃
main_thread->stack_overflow_state()->create_stack_guard_pages(); // 初始化Java级别的同步子系统。首先调用ObjectMonitor类的
// Initialize方法来初始化监视器对象,然后调用ObjectSynchronizer
// 类的initialize方法来初始化对象同步器。这些步骤是确保Java程序
// 中的多线程同步操作能够正确地执行
ObjectMonitor::Initialize();
ObjectSynchronizer::initialize(); // 初始化全局变量,包括:管理模块、对象存储、字节码、引导类加载器、
// 编译策略、代码缓存、虚拟机版本、存根、Java内存模型相关的组件
// (比如、TLAB、堆、元空间、方法缓存、方法表、符号表、字符串表、
// JVM内部数据结构等)以及GC日志、异步日志、垃圾回收栅栏、协程、
// 协程存根、解释器存根、访问标识、接口支持、寄存器名称、共享
// 运行时生成存根
jint status = init_globals();
if (status != JNI_OK) {
// 删除主线程
main_thread->smr_delete();
// 将canTryAgain指针指向false,防止调用者再次调用
// JNI_CreateJavaVM函数
*canTryAgain = false;
return status;
} // 将主线程添加到线程列表中,以完成栅栏的设置,
// 并在init_globals2中构建Java对象之前进on_thread_attach。
// 该代码通过使用MutexLocker锁定线程锁Threads_lock,
// 然后将主线程添加到线程列表中
{
MutexLocker mu(Threads_lock);
Threads::add(main_thread);
} // 初始化一些全局变量
status = init_globals2();
if (status != JNI_OK) {
// 从线程列表中移除指定的线程
Threads::remove(main_thread, false);
main_thread->smr_delete();
*canTryAgain = false;
return status;
} // 这段代码的作用是仅在JFR(Java Flight Recorder)启用时
// 调用Jfr::on_create_vm_1()函数
JFR_ONLY(Jfr::on_create_vm_1();)
// 在堆完全创建后运行,主线程将缓存全局变量
main_thread->cache_global_variables(); // 在onload中进入的任何JVMTI原始监视器都将转换为真实的原始
// 监视器,在这里,VM已经设置好了,可以进入原始监视器
JvmtiExport::transition_pending_onload_raw_monitors(); // 以下代码的作用是创建一个虚拟机线程并等待它初始化完成
{
TraceTime timer("Start VMThread", TRACETIME_LOG(Info,
startuptime));
VMThread::create();
VMThread* vmthread = VMThread::vm_thread();
if (!os::create_thread(vmthread, os::vm_thread)) {
vm_exit_during_initialization("Cannot create VM thread. "
"Out of system resources.");
}
{
// 使用MonitorLocker和Notify_lock来等待VM线程初始化完成
MonitorLocker ml(Notify_lock);
os::start_thread(vmthread);
while (!vmthread->is_running()) {
ml.wait();
}
}
} // Java内存模型及JVM核心组件是否初始化完成,确保我们以干净的状态开始
assert(Universe::is_fully_initialized(), "not initialized"); if (VerifyDuringStartup) {
VM_Verify verify_op;
// 启动一个新线程,执行验证操作
VMThread::execute(&verify_op);
} // 这段代码的作用是更新java.vm.info属性,以防初始定义
// 它的任何标志已被更改。
// 这对于CDS非常重要,因为UseSharedSpaces可能在java.vm.info
// 最初计算之后被更改。在我们初始化java类之前,但在可能修改
// 标志的任何初始化逻辑之后,必须进行此更新。
// CDS是Class Data Sharing的缩写,是一种JVM的优化技术。
// 它可以将类数据预处理并保存在共享的归档文件中,以便在后续
// 的JVM启动中重用这些数据,从而加快JVM的启动时间 和内存占用。
// CDS在JDK 5中首次引入,JDK 8中进行了进一步的改进。
Arguments::update_vm_info_property(VM_Version::vm_info_string()); // 创建一个Java线程,并使用异常宏THREAD进行异常处理
JavaThread* THREAD = JavaThread::current(); // For exception macros.
// 创建一个HandleMark对象,该对象在当前线程上下文中创建一个
// 句柄标记。这将确保在当前线程上下文中创建的所有句柄都被正确
// 处理,并在当前线程上下文结束时自动清除
HandleMark hm(THREAD); // 调用JvmtiExport的enter_early_start_phase()函数,即使还没有
// JVMTI环境,因为环境可能会晚些时候附加,JVMTI必须跟踪VM执行
// 的阶段,JvmtiExport是一个JVMTI API的导出类,其中包含了
// 许多用于与JVMTI代理交互的函数。
JvmtiExport::enter_early_start_phase();
// 通知JVMTI代理VM已经启动(JNI已经启动),如果没有代理则
// 不执行任何操作
JvmtiExport::post_early_vm_start();
// 检查是否设置了EagerXrunInit标志,如果设置了,则会尽早
// 启动-Xrun代理。
if (EagerXrunInit) {
// 如果设置了该标志,则调用JvmtiAgentList::load_xrun_agents()
// 函数来加载代理
JvmtiAgentList::load_xrun_agents();
} // 初始化Java核心java.lang包中的类,通过传入的主线程和检查
// JNI错误的参数来实现。
// 第1阶段:java.lang.系统类初始化。
// java.lang.System是一个原始类,由VM在启动初期加载和初始化。
// java.lang.System.<clinit>只执行registerNatives,
// 并在线程初始化完成之前保留类初始化的其余工作。
// System.initPhase1初始化系统属性、静态字段in、out和err。
// 设置java信号处理程序、操作系统特定的系统设置和主线程的线程组。
initialize_java_lang_classes(main_thread, CHECK_JNI_ERR); // 加速JNI函数,通过使用快速版本替换获取基础类型的函数
quicken_jni_functions(); // 在这个点,存根代码不允许再生成了
// StubCodeDesc描述一段生成的代码(通常是存根)。此信息主要
// 用于调试和打印。目前代码描述符只是简单地链接在一个链表中,
// 如果搜索变得太慢,这种情况可能不得不改变。
StubCodeDesc::freeze(); // 设置了一个标志,表示基本初始化已经完成。这个标志被异常和
// 各种调试工具使用,在所有基本类都被初始化之前无法使用
set_init_completed(); // 下面三行代码分别调用了三个类的post_initialize()函数。
// 其中LogConfiguration类用于配置日志输出,
// Metaspace类用于管理元空间,
// MutexLocker类用于管理锁。
// 这些函数的作用是在程序启动后初始化相关的资源和状态,以便
// 后续的程序运行。每个类的post_initialize()函数都会执行一些
// 初始化操作,例如设置默认值、分配内存等。这样可以确保程序在
// 正式运行前已经准备好了必要的资源和状态,从而提高程序的
// 稳定性和性能
LogConfiguration::post_initialize();
Metaspace::post_initialize();
MutexLocker::post_initialize(); // Java虚拟机初始化完成
HOTSPOT_VM_INIT_END(); // 如果包含管理功能,则调用Management类的
// record_vm_init_completed()方法记录虚拟机初始化完成的时间
#if INCLUDE_MANAGEMENT
Management::record_vm_init_completed();
#endif // INCLUDE_MANAGEMENT // 打印初始化JVM的进程ID
log_info(os)("Initialized VM with process ID %d",
os::current_process_id()); // 在VMInit事件被发布之前启动Signal Dispatcher
os::initialize_jdk_signal_support(CHECK_JNI_ERR); // AttachListener线程通过客户端工具为排队的操作队列提供服务。
// 每个操作都由一个名称标识,最多有3个参数。
// 操作名称映射到执行操作的函数,被outputStream调用,
// 该输出流可用于写入任何结果数据(例如,序列化写入properties
// 名称和值)当该函数执行完成,会将所有结果数据返回给客户端工具
if (!DisableAttachMechanism) {
// 如果没有禁用Attach 机制,则启动AttachListener。
AttachListener::vm_start();
if (StartAttachListener || AttachListener::init_at_startup()) {
AttachListener::init();
}
} // 如果没有设置EagerXrunInit,则启动-Xrun代理
if (!EagerXrunInit) {
JvmtiAgentList::load_xrun_agents();
} // 启动一个清理内存池的任务
Chunk::start_chunk_pool_cleaner_task(); // JVMTI是Java虚拟机工具接口(Java Virtual Machine Tool Interface)
// 的缩写。它是一组用于Java虚拟机的原生编程接口,允许开发人员
// 创建各种工具来监视、管理和分析Java应用程序的行为。JVMTI提供了
// 对Java虚拟机内部状态的访问,包括类、对象、线程和堆信息等。
// 它还允许开发人员创建自己的代理程序,以便在Java应用程序中动态
// 植入代码。JVMTI常用于Java应用程序的性能分析、调试和测试等方面。
// 启动了一个服务线程,该服务线程将JVMTI延迟事件排队,并进行
// 各种哈希表和其他清理工作。需要在编译器开始发布事件之前启动。
ServiceThread::initialize(); // 启动了监视器缩减线程。首先,代码调用了MonitorDeflationThread
// 类的initialize()方法。这个方法可能会执行一些初始化操作,然后
// 启动一个后台线程,该线程会定期检查系统中的监视器,以确保它们
// 不会占用太多的内存。如果监视器过大,线程会自动将其缩小。
// 这个线程将在后台运行,直到系统关闭或线程被显式停止。
MonitorDeflationThread::initialize(); // 以下宏条件中的代码,意思是初始化编译器
// JVMCI是Java虚拟机编译接口(JVM Compiler Interface)的缩写,
// 它是Java 9引入的一个新的JIT编译器接口。JVMCI允许第三方开发者
// 编写JIT编译器,并将其集成到Java虚拟机中,以替代默认的JIT编译
// 器。JVMCI的引入使得Java虚拟机更加灵活,可以根据具体的应用
// 场景选择更加适合的JIT编译器,从而提高Java应用程序的性能。
#if defined(COMPILER1) || COMPILER2_OR_JVMCI
#if INCLUDE_JVMCI
bool force_JVMCI_intialization = false;
if (EnableJVMCI) {
// 在初始化JVMCI时,如果启用了EagerJVMCI或者
// JVMCIPrintProperties或者JVMCILibDumpJNIConfig,
// 则强制初始化JVMCI。否则,如果使用了JVMCI编译器
// 并且不使用解释器或后台编译,则强制初始化JVMCI
force_JVMCI_intialization = EagerJVMCI || JVMCIPrintProperties
|| JVMCILibDumpJNIConfig;
if (!force_JVMCI_intialization) {
force_JVMCI_intialization = UseJVMCICompiler &&
(!UseInterpreter || !BackgroundCompilation);
}
}
#endif
CompileBroker::compilation_init_phase1(CHECK_JNI_ERR);
if (JVMCI_ONLY(!force_JVMCI_intialization) NOT_JVMCI(true)) {
CompileBroker::compilation_init_phase2();
}
#endif // 如果启用了字符串去重功能,则启动字符串去重线程
if (StringDedup::is_enabled()) {
StringDedup::start();
} // 预初始化一些JSR292核心类,以避免在类加载期间发生死锁。
// 它在编译器初始化后执行,因为否则可能会错过签名多态
// MH内部函数的编译。方法内部代码步骤:
// 1. 初始化java.lang包中类的方法处理的调用。
// 2. 初始化java.lang包中类的解析方法名称的调用。
// 3. 初始化java.lang包中类的成员名称的调用。
// 4. 初始化java.lang包中类的本地方法处理的调用。
initialize_jsr292_core_classes(CHECK_JNI_ERR); // 第2阶段初始化:初始化模块系统,并且只有在第二阶段完成之前
// 可以加载java.base类。在编译器初始化和jsr292类初始化
// System.initPhase2,因为模块初始化运行了大量java代码,出于
// 性能原因,应该编译这些代码。此外,这将使启动代码能够在此
// 阶段及以后使用lambda和其他语言功能。在第2阶段之后,
// 虚拟机将从-Xbootclasspath/a开始搜索类。
call_initPhase2(CHECK_JNI_ERR); // 只有在启用了JFR(Java Flight Recorder)时才会执行该函数。
// JFR是一种用于收集和分析Java应用程序性能数据的工具
JFR_ONLY(Jfr::on_create_vm_2();) // 即使没有JVMTI环境也要调用,因为环境可能会在后期附加,
// JVMTI必须跟踪VM执行阶段。
JvmtiExport::enter_start_phase(); // 通知JVMTI代理VM已经启动(JNI已经启动),
// 如果没有代理则不执行任何操作。
JvmtiExport::post_vm_start(); // 第3阶段初始化 最后设置安全管理器、系统类加载器和TCCL。这将
// 实例化和设置安全管理器,设置系统类加载器以及线程上下文类
// 加载器。安全管理器和系统类加载器可以是从-Xbootclasspath/a、
// 其他模块或应用程序的类路径加载的自定义类。
call_initPhase3(CHECK_JNI_ERR); // 这段代码的作用是缓存系统和平台类加载器。在该方法中,首先,
// 通过调用ClassLoader类的getSystemClassLoader方法和
// getPlatformClassLoader方法获取系统类加载器和平台类加载器,
// 并将其缓存起来
SystemDictionary::compute_java_loaders(CHECK_JNI_ERR); // 如果定义了INCLUDE_CDS宏,则初始化类加载器的模块路径信息。
// 如果在初始化过程中出现异常,则会打印异常信息并退出虚拟机。
#if INCLUDE_CDS
ClassLoader::initialize_module_path(THREAD);
if (HAS_PENDING_EXCEPTION) {
java_lang_Throwable::print(PENDING_EXCEPTION, tty);
vm_exit_during_initialization("ClassLoader::initialize_module_path()
failed unexpectedly");
}
#endif
// 在JVMCI被包含的情况下,如果需要强制初始化JVMCI,
// 则进行编译器的初始化,并执行编译初始化的第二阶段
#if INCLUDE_JVMCI
if (force_JVMCI_intialization) {
JVMCI::initialize_compiler(CHECK_JNI_ERR);
CompileBroker::compilation_init_phase2();
}
#endif
// 方法是JVMTI API的一部分,用于通知JVMTI环境进入VM的“live”阶段,
// 即VM正在运行并且可以被监视和分析。这个方法应该在VM启动后尽早
// 调用,因为JVMTI需要跟踪VM执行的不同阶段。如果JVMTI环境在后期
// 附加,这个方法也应该被调用,以确保JVMTI能够正确跟踪VM的状态
JvmtiExport::enter_live_phase(); // 使性能内存可访问
// 性能内存通常用于存储程序运行时的性能数据,如CPU使用率、
// 内存使用率等。通过使性能内存可访问,开发人员可以获取这些数据
// 并进行分析,以便优化程序的性能。
PerfMemory::set_accessible(true); // 通知JVMTI代理VM初始化完成,如果没有代理则不执行任何操作
JvmtiExport::post_vm_initialized();
// 在启用JFR时,当虚拟机被创建时记录其配置信息。
JFR_ONLY(Jfr::on_create_vm_3();) // 如果定义了INCLUDE_MANAGEMENT宏,则初始化类管理器,
// 如果有异常,则退出VM。
#if INCLUDE_MANAGEMENT
Management::initialize(THREAD);
if (HAS_PENDING_EXCEPTION) {
vm_exit(1);
}
#endif
// 调用统计采样器(StatSampler)
StatSampler::engage();
if (CheckJNICalls) JniPeriodicChecker::engage(); // 如果定义了INCLUDE_RTM_OPT宏,则初始化用于计数的锁,它允许
// 多个线程同时访问计数器,并确保计数器的值正确地增加或减少。
#if INCLUDE_RTM_OPT
RTMLockingCounters::init();
#endif // 调用VM初始化完成后的钩子方法,Java库方法本身可以独立于VM进行更改
call_postVMInitHook(THREAD); // 在PostVMInitHook.run方法的Java部分处理所有异常并提供诊断手段。
// 如果存在挂起异常,则清除挂起异常。.
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
} // 启动一个WatcherThread线程来执行周期性任务
{
// 通过MutexLocker来锁定PeriodicTask_lock,确保WatcherThread
// 可以通过WatcherThread::start()或动态注册来启动
MutexLocker ml(PeriodicTask_lock);
// 将WatcherThread设置为可启动状态
WatcherThread::make_startable(); // 如果有周期性任务,则启动WatcherThread线程来执行任务。
// 需要注意的是,所有的周期性任务应该在此之前都已经注册了,
// 否则晚注册的任务可能会启动缓慢。
if (PeriodicTask::num_tasks() > 0) {
WatcherThread::start();
}
}
// 结束创建虚拟机的计时器计时
create_vm_timer.end(); #ifdef ASSERT
_vm_complete = true;
#endif // 判断是否启用了DumpSharedSpaces选项,如果启用,
// 则调用MetaspaceShared::preload_and_dump()函数,
// 该函数的作用是预加载和转储共享空间。然后,代码调用
// ShouldNotReachHere()函数,该函数的作用是抛出一个错误,
// 表示代码执行到了不应该到达的地方。
if (DumpSharedSpaces) {
MetaspaceShared::preload_and_dump();
ShouldNotReachHere();
}
return JNI_OK;
}

  

下面用一张流程图概括:

  

深入剖析创建Java虚拟机的实现方法的更多相关文章

  1. 在Android Native层中创建Java虚拟机实例

    前言 Android应用中JNI代码,是作为本地方法运行的.而大部分情况下,这些JNI方法均需要传递Dalvik虚拟机实例作为第一个参数.例如,你需要用虚拟机实例来创建jstring和其他的Java对 ...

  2. Java虚拟机栈 和 方法区 的联系

    1.Java虚拟机栈 java方法执行时的内存模型 1.1 栈帧 每个方法都会在虚拟机栈中创建一个对应的栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息. 一个方法的调用到结束就对应这一个 ...

  3. Java虚拟机栈---本地方法栈

    1.Java虚拟机栈(Java Virtual Machine Stacks) 线程私有,它的生命周期与线程相同.描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack ...

  4. eclipse不能创建java虚拟机-解决方法

    找到eclipse目录下的eclipse.ini,可以看到如下内容: -startup plugins/org.eclipse.equinox.launcher_1.1.0.v20100507.jar ...

  5. eclipse启动时出现无法创建java虚拟机

    最 近一直在用eclipse开发android程序,今天不知怎么的启动eclipse时就会出现Failed to create java virtual machine,无法打开eclipse程序,折 ...

  6. HotSpot Java虚拟机中的“方法区”“持久代”“元数据区”的关系?

    Sun/Oracle JDK的HotSpot VM中,直到JDK7都有“持久代”(Permanent Generation,简称PermGen).也称为方法区.Oracle JDK8的HotSpot ...

  7. Failed to create java virtue machine(不能创建java虚拟机)

    今天开发模块时,遇到这个问题,本来是版本的问题,jdk1.6的版本有点低,与cxf框架不兼容,需要用到jdk1.7,结果安装了jdk1.7之后,客户方要求必须用jdk1.6,要统一,所以卸载jdk1. ...

  8. Java虚拟机一 运行时数据区(栈、堆、方法区等)

    Java虚拟机的内存管理主要分两点:内存分配以及内存回收.· 一.内存分配图: 注: 所占区域的大小与实际的内存大小比例并无直接关系. 解读: 1.如图,分成两种颜色的内存区域,其中蓝色的是线程隔离的 ...

  9. JVM总结-Java 虚拟机是怎么识别目标方法(下)

    1. 虚方法调用 在上一篇中我曾经提到,Java 里所有非私有实例方法调用都会被编译成 invokevirtual 指令,而接口方法调用都会被编译成 invokeinterface 指令.这两种指令, ...

  10. JVM总结-Java 虚拟机是怎么识别目标方法(上)

    重载与重写 在 Java 程序里,如果同一个类中出现多个名字相同,并且参数类型相同的方法,那么它无法通过编译.也就是说,在正常情况下,如果我们想要在同一个类中定义名字相同的方法,那么它们的参数类型必须 ...

随机推荐

  1. 开源不到 48 小时获 35k star 的推荐算法「GitHub 热点速览」

    本周的热点除了 GPT 各类衍生品之外,还多了一个被马斯克预告过.在愚人节开源出来的推特推荐算法,开源不到 2 天就有了 35k+ 的 star,有意思的是,除了推荐算法本身之外,阅读源码的工程师们甚 ...

  2. 修复Joe主题静态资源为国内地址

    背景 Typecho 是由 type 和 echo 两个词合成的,来自于开发团队的头脑风暴. Type,有打字的意思,博客这个东西,正是一个让我们通过打字,在网络上表达自己的平台.Echo,意思是回声 ...

  3. 浏览器层面优化前端性能(1):Chrom组件与进程/线程模型分析

    现阶段的浏览器运行在一个单用户,多合作,多任务的操作系统中.一个糟糕的网页同样可以让一个现代的浏览器崩溃.其原因可能是一个插件出现bug,最终的结果是整个浏览器以及其他正在运行的标签被销毁. 现代操作 ...

  4. day32:进程&进程join&守护进程deamon

    目录 1.进程的基本概念 2.进程初体验 3.join:先子后主 4.守护进程:deamon 5.使用自定义类的方式创建进程 6.两张和进程相关的图 进程的基本概念 什么是进程? 进程就是正在运行的程 ...

  5. linux 安装 node 和 npm 服务

    1.安装文件下载 下载地址:https://nodejs.org/zh-cn/download/ 2.安装步骤 1.将安装包上传到指定位置(我习惯放到:/usr/local/application/目 ...

  6. 沁恒 CH32V208(二): CH32V208的储存结构, 启动模式和时钟

    目录 沁恒 CH32V208(一): CH32V208WBU6 评估板上手报告和Win10环境配置 沁恒 CH32V208(二): CH32V208的储存结构, 启动模式和时钟 CH32V 存储容量命 ...

  7. NC23054 华华开始学信息学

    题目链接 题目 题目描述 因为上次在月月面前丢人了,所以华华决定开始学信息学.十分钟后,他就开始学树状数组了.这是一道树状数组的入门题: 给定一个长度为 \(N\) 的序列 \(A\) ,所有元素初值 ...

  8. 数据治理之关键环节元数据管理开源项目datahub探索

    @ 目录 概述 定义 核心功能 概念 元数据应用 其他开源 架构 概览 组件 元数据摄取架构 服务体系结构 本地部署 环境要求 安装 摄取样例 摄取入门 介绍 核心概念 命令行MySQL摄取示例 配置 ...

  9. Vue拖拽排序

    转载至https://www.crazyming.com/note/757/ 使用拖拽功能来实现排序. 需要先学习w3cschool 关于拖拽的教程:http://www.w3school.com.c ...

  10. 【Python】爬虫下载视频

    Python爬虫下载视频 前言 这两天我一时兴起想学习 PS ,于是去我的软件宝库中翻出陈年已久的 PhotoshopCS6 安装,结果发现很真流畅诶! 然后去搜索学习视频,网上的视频大多浮躁,收费, ...