经过前文《深入剖析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. Python 中 is 和 == 的区别

    is 和 == 的区别 相信学过 Python 小伙伴们都知道 is 和 == 都是用来比较 Python 对象的,但是区别就是 is 比较需要对象的值和内存地址都相等 == 比较只需要对象的值相等就 ...

  2. [Linux/CENTOS]YUM提示: Another app is currently holding the yum lock; waiting for it to exit...

    1 问题描述 使用yum安装Nginx的安装依赖组件: yum -y install gcc gcc-c++ automake autoconf libtool make 但是,在执行过程中出现如下信 ...

  3. CSS3-页面布局基础二——Box Model、边距折叠、内联与块标签

    一.盒子模型(Box Model) 盒子模型也有人称为框模型,HTML中的多数元素都会在浏览器中生成一个矩形的区域,每个区域包含四个组成部分,从外向内依次是:外边距(Margin).边框(Border ...

  4. Linux网络管理入门

    根据自己的需要来设置Linux的一些属性 网络状态查看 在终端输入ifconfig可以查看网络状态 # ifconfig eth0: flags=4163<UP,BROADCAST,RUNNIN ...

  5. Go语言网络编程:SSH连接

    旨在提升Go语言网络编程能力 SSH是什么? SSH 为 Secure Shell 的缩写,为建立在应用层基础上的安全协议.SSH 是较可靠,专为远程登录会话和其他网络服务提供安全性的协议.利用 SS ...

  6. ArcGIS Pro发布地图服务(影像、矢量)

    做GIS一般都是用ArcMap发布影像或者矢量服务,由于ArcGIS后续不在更新ArcMap,改用ArcGIS Pro,本文对ArcGIS Pro发布服务进行说明. 本文示例使用(因为portal的授 ...

  7. ROS机器人摄像头寻线

    ROS机器人摄像头寻线 连接小车 注意:必须在同一区域网 ssh clbrobort@clbrobort 激活树莓派主板 roslaunch clbrobot bringup.launch 开启摄像头 ...

  8. 第4章. 安装reco主题

    大家可以按照我的教程来安装,也可以访问 reco_luan 大佬的 官方教程 根据自己的电脑类型和开发环境配置,来选择合适的安装方式. 一.快速开始 npx # 初始化,并选择 2.x npx @vu ...

  9. 【介绍】C++五种迭代器

    目录 1. 输入迭代器(Input Iterator): 2. 输出迭代器(Output Iterator): 3. 前向迭代器(Forward Iterator): 4. 双向迭代器(Bidirec ...

  10. 前端js几种加密/解密方法

    https://www.jianshu.com/p/4c236d83ea04 https://blog.csdn.net/guxingsheng/article/details/84451573 vu ...