JVM源码分析VMThread线程

思想至上

大多数Java程序员对线程的感知度很低、少部分Java程序员明白在适合的业务项目中使用线程池完成异步操作,也明白在使用Java的Spring系列框架开发后端程序请求是从Tomcat的工作线程池最终到用户Servlet业务逻辑、极少部分的Java程序员明白除开Java层面,底层JVM也有众多线程在工作。

而此篇文章就带各位读者分析在JVM中默默工作的"VMThread"线程。

笔者会从几个问题分析:

  1. VMThread何时创建?
  2. VMThread与Java程序创建的线程有什么区别?
  3. VMThread作用?

问题1 :

肯定是在JVM启动的时候去创建的VMThread线程

问题2:

在JVM中定义了线程的类型,在hotspot/src/share/vm/runtime/os.hpp文件中,以枚举的形式定义

  // 线程类型。JVM层面的抽象。
  enum ThreadType {
    vm_thread,          // JVM内部工作线程 
    cgc_thread,         // 并发GC线程
    pgc_thread,         // 并行GC线程
    java_thread,        // java层面定义的线程
    compiler_thread,    // Jit编译线程
    watcher_thread,     // JVM内部的定时处理
    os_thread           // 操作系用的线程
  };

这里定义了JVM内部所能出现的线程类型,而VMThread和Java程序创建的线程区别就一目了然了,一个是JVM内部工作的线程,一个是Java程序使用的线程。相同点就是他两都是线程....

问题3:

谈到作用,不得不再引入一个知识点,那就是安全点(SafePoint),安全点是JVM提出让工作线程阻塞的想法的落地实现,而为什么工作线程需要阻塞,也很简单,比如GC的时候需要SWT,那么工作线程就需要阻塞等待。安全点的实现也很简单,当需要工作线程阻塞的时候就启动安全点,而工作线程在不同的时期需要去检测是否开启了安全点,如果安全点开启了,当前工作线程就需要去阻塞等待被唤醒。

而安全点的触发工作就是"VMThread"线程来做,并且此线程还会执行需要安全点才能执行的工作,比如:GC垃圾回收、dump 线程堆栈数据 等等需要安全点的操作。

源码论证

下面代码选用JDK1.8版本。Hotspot虚拟机源码。

问题1:

jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {

	…………

	// 创建VMThread
	{ 
		TraceTime timer("Start VMThread", TraceStartupTime);
		// 创建VMThread对象,因为当前C++层面,所以也有对象的抽象,好比在Java层面创建Thread对象。
		VMThread::create();
		Thread* vmthread = VMThread::vm_thread();

    	// 创建底层真正的线程(PThread线程库)。
		if (!os::create_thread(vmthread, os::vm_thread))
			vm_exit_during_initialization("Cannot create VM thread. Out of system resources.");


		// 启动VMThread,并且等待VMThread中必要部分初始化完毕。
		{
			MutexLocker ml(Notify_lock);
			os::start_thread(vmthread);
			while (vmthread->active_handles() == NULL) {
				Notify_lock->wait();
			}
		}
	}

	…………
}

hotspot/src/share/vm/runtime/thread.cpp 文件中create_vm方法是启动并初始化JVM个个模块,其中就初始化了VMThread线程。代码非常的简单,就是创建、启动、初始化VMThread线程。

问题3:

上述已经介绍完VMThread线程的创建并且运行起来了,所以我们需要找到运行代码。

hotspot/src/share/vm/runtime/vmThread.cpp文件中。

void VMThread::run() {
	…………
	// 执行队列传输事件。
	this->loop();
	…………
}

// 死循环处理队列来的任务。
void VMThread::loop() {
  assert(_cur_vm_operation == NULL, "no current one should be executing");

  while(true) {
    VM_Operation* safepoint_ops = NULL;


    //
    // Wait for VM operation
    //

    …………

    //
    // Execute VM operation
    //
    {

      if (_cur_vm_operation->evaluate_at_safepoint()) {

        // 进入线程安全点,准备做事。
        SafepointSynchronize::begin();
        // 执行任务
        evaluate_operation(_cur_vm_operation);

        do {
          _cur_vm_operation = safepoint_ops;
          if (_cur_vm_operation != NULL) {
            do {

              VM_Operation* next = _cur_vm_operation->next();
              _vm_queue->set_drain_list(next);
              // 执行任务
              evaluate_operation(_cur_vm_operation);
              _cur_vm_operation = next;
              if (PrintSafepointStatistics) {
                SafepointSynchronize::inc_vmop_coalesced_count();
              }
            } while (_cur_vm_operation != NULL);
          }
 
        } while(safepoint_ops != NULL);

        _vm_queue->set_drain_list(NULL);

        // Complete safepoint synchronization
        SafepointSynchronize::end();

      } 
    }

    …………
  }
}

此代码量特别大,所以笔者删减了很多跟主流程无关代码,这样方便读者阅读,减轻难度。

大致流程如下:

  1. 死循环一直处理任务,直到JVM关闭
  2. 阻塞等待VMOperationQueue队列来任务
  3. 开启线程安全点
  4. 处理VMOperationQueue队列的VM_Operation任务
  5. 关闭线程安全点
  6. 进入到下一次的阻塞等待,周而复始

所以,我们还需要哪里给VMOperationQueue队列投递的VM_Operation任务

我们以dump thread线程堆栈数据来作为展示。

hotspot/src/share/vm/services/attachListener.cpp文件中。

static jint thread_dump(AttachOperation* op, outputStream* out) {

  // thread stacks
  VM_PrintThreads op1(out, print_concurrent_locks);
  VMThread::execute(&op1);

  // JNI global handles
  VM_PrintJNI op2(out);
  VMThread::execute(&op2);

  // Deadlock detection
  VM_FindDeadlocks op3(out);
  VMThread::execute(&op3);

  return JNI_OK;
}

void VMThread::execute(VM_Operation* op) {
  Thread* t = Thread::current();

  …………

  if (!t->is_VM_thread()) {			// 当前不是VMThread线程,所以需要把VM_Operation通过Queue的方式传给VMThread线程去处理。
    {
      VMOperationQueue_lock->lock_without_safepoint_check();
      // 将任务添加到VMOperationQueue队列,然后尝试唤醒VMThread线程。
      bool ok = _vm_queue->add(op);
      op->set_timestamp(os::javaTimeMillis());
      VMOperationQueue_lock->notify();
      VMOperationQueue_lock->unlock();
    }
  } else {							// 当前已经是VMThread线程了,所以可以直接执行。

  	…………

    // 如果当前已经是VMThread在执行此代码,那就直接执行
    if (op->evaluate_at_safepoint() && !SafepointSynchronize::is_at_safepoint()) {
      // 安全点的设置。
      SafepointSynchronize::begin();
      // 执行任务
      op->evaluate();
      SafepointSynchronize::end();
    } else {
      op->evaluate();
    }
    _cur_vm_operation = prev_vm_operation;
  }
}

这里把VM_Operation任务添加到VMOperationQueue队列中,并且尝试把VMThread给唤醒。此时VMThread线程醒来后,又开始走上面的周而复始的流程了。

总结

思想至上,源码作为论证过程

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Prometheus Exporter是一种用于将JVM的指标信息暴露给Prometheus监控系统的工具。您提到的源码分析可以参考以下步骤: 1. 首先,您需要下载jmx_exporter,并将其jar包(jmx_prometheus_javaagent-0.16.1.jar)放置在Kafka的家目录下。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [prometheus(jvm_exporter监控kafka jvm)](https://blog.csdn.net/weixin_45837370/article/details/121232020)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [jmx_exporter源码分析](https://blog.csdn.net/qqqq0199181/article/details/83792364)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [prometheus jmx_exporter 源码分析](https://blog.csdn.net/weixin_40455124/article/details/105693264)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员李哈

创作不易,希望能给与支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值