HotSpotVM 线程实现浅析

今天来看下HotSpotVM在Linux下的线程模型。

Thread.start

HotSpot Runtime Overview 中说道,

There are two basic ways for a thread to be introduced into the VM: execution of Java code that calls start() on a java.lang.Thread object; or attaching an existing native thread to the VM using JNI.

Thread.start会调用native方法,start0之前介绍过怎么找到这个native方法的实现,在Thread.c中,

static JNINativeMethod methods[] = {
    {"start0",           "()V",        (void *)&JVM_StartThread},
    {"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},
    {"isAlive",          "()Z",        (void *)&JVM_IsThreadAlive},
    {"suspend0",         "()V",        (void *)&JVM_SuspendThread},
    {"resume0",          "()V",        (void *)&JVM_ResumeThread},
    {"setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority},
    {"yield",            "()V",        (void *)&JVM_Yield},
    {"sleep",            "(J)V",       (void *)&JVM_Sleep},
    {"currentThread",    "()" THD,     (void *)&JVM_CurrentThread},
    {"countStackFrames", "()I",        (void *)&JVM_CountStackFrames},
    {"interrupt0",       "()V",        (void *)&JVM_Interrupt},
    {"isInterrupted",    "(Z)Z",       (void *)&JVM_IsInterrupted},
    {"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},
    {"getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads},
    {"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
    {"setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};

JVM_StartThread的定义在jvm.h

JNIEXPORT void JNICALL
JVM_StartThread(JNIEnv *env, jobject thread);

具体实现在jvm.cpp。读源码之前可以参考这里先看下java.lang.ThreadJavaThreadOSThread它们之间的关系。

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_StartThread");
  JavaThread *native_thread = NULL;

  // We cannot hold the Threads_lock when we throw an exception,
  // due to rank ordering issues. Example:  we might need to grab the
  // Heap_lock while we construct the exception.
  bool throw_illegal_thread_state = false;

  // We must release the Threads_lock before we can post a jvmti event
  // in Thread::start.
  {
    // Ensure that the C++ Thread and OSThread structures aren‘t freed before
    // we operate.
    MutexLocker mu(Threads_lock);

    // Since JDK 5 the java.lang.Thread threadStatus is used to prevent
    // re-starting an already started thread, so we should usually find
    // that the JavaThread is null. However for a JNI attached thread
    // there is a small window between the Thread object being created
    // (with its JavaThread set) and the update to its threadStatus, so we
    // have to check for this
    if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
      throw_illegal_thread_state = true;
    } else {
      // We could also check the stillborn flag to see if this thread was already stopped, but
      // for historical reasons we let the thread detect that itself when it starts running

      jlong size =
             java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
      // Allocate the C++ Thread structure and create the native thread.  The
      // stack size retrieved from java is signed, but the constructor takes
      // size_t (an unsigned type), so avoid passing negative values which would
      // result in really large stacks.
      size_t sz = size > 0 ? (size_t) size : 0;

      //////////////////////////////////////////////////
      // 1. 创建与java.lang.Thread对应的JavaThread
      //////////////////////////////////////////////////
      native_thread = new JavaThread(&thread_entry, sz);

      // At this point it may be possible that no osthread was created for the
      // JavaThread due to lack of memory. Check for this situation and throw
      // an exception if necessary. Eventually we may want to change this so
      // that we only grab the lock if the thread was created successfully -
      // then we can also do this check and throw the exception in the
      // JavaThread constructor.
      if (native_thread->osthread() != NULL) {
        // Note: the current thread is not being used within "prepare".

      //////////////////////////////////////////////////
      // 2. 关联java.lang.Thread与JavaThread
      //////////////////////////////////////////////////
        native_thread->prepare(jthread);
      }
    }
  }

  if (throw_illegal_thread_state) {
    THROW(vmSymbols::java_lang_IllegalThreadStateException());
  }

  assert(native_thread != NULL, "Starting null thread?");

  if (native_thread->osthread() == NULL) {
    // No one should hold a reference to the ‘native_thread‘.
    delete native_thread;
    if (JvmtiExport::should_post_resource_exhausted()) {
      JvmtiExport::post_resource_exhausted(
        JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
        "unable to create new native thread");
    }
    THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
              "unable to create new native thread");
  }

  //////////////////////////////////////////////////
  // 3. 启动JavaThread
  //////////////////////////////////////////////////
  Thread::start(native_thread);

JVM_END

构造JavaThread

来看下JavaThread的构造函数

JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
  Thread() //// 各种初始化
#ifndef SERIALGC
  , _satb_mark_queue(&_satb_mark_queue_set),
  _dirty_card_queue(&_dirty_card_queue_set)
#endif // !SERIALGC
{
  if (TraceThreadEvents) {
    tty->print_cr("creating thread %p", this);
  }

  ////////////////////////////////////////
  // 1. 初始化
  ////////////////////////////////////////
  initialize();

  _jni_attach_state = _not_attaching_via_jni;
  set_entry_point(entry_point);
  // Create the native thread itself.
  // %note runtime_23
  os::ThreadType thr_type = os::java_thread;
  thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :                                               os::java_thread;

  ////////////////////////////////////////
  // 2. 创建OSThread
  ////////////////////////////////////////
  os::create_thread(this, thr_type, stack_sz);

  _safepoint_visible = false;
  // The _osthread may be NULL here because we ran out of memory (too many threads active).
  // We need to throw and OutOfMemoryError - however we cannot do this here because the caller
  // may hold a lock and all locks must be unlocked before throwing the exception (throwing
  // the exception consists of creating the exception object & initializing it, initialization
  // will leave the VM via a JavaCall and then all locks must be unlocked).
  //
  // The thread is still suspended when we reach here. Thread must be explicit started
  // by creator! Furthermore, the thread must also explicitly be added to the Threads list
  // by calling Threads:add. The reason why this is not done here, is because the thread
  // object must be fully initialized (take a look at JVM_Start)
}

传进来的entry_pointthread_entry,这个方法会调用Thread.run方法。待会就能看到是怎么用的了。

static void thread_entry(JavaThread* thread, TRAPS) {
  HandleMark hm(THREAD);
  Handle obj(THREAD, thread->threadObj());
  JavaValue result(T_VOID);
  JavaCalls::call_virtual(&result,
                          obj,
                          KlassHandle(THREAD, SystemDictionary::Thread_klass()),
                          vmSymbols::run_method_name(),
                          vmSymbols::void_method_signature(),
                          THREAD);
}

创建OSThread

JavaThread的构造函数中会创建OSThread,直接看下Linux下的os::create_thread

bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
  assert(thread->osthread() == NULL, "caller responsible");

  ////////////////////////////////////////
  // 1. 构造OSThread
  ////////////////////////////////////////
  // Allocate the OSThread object
  OSThread* osthread = new OSThread(NULL, NULL);
  if (osthread == NULL) {
    return false;
  }

  // set the correct thread state
  osthread->set_thread_type(thr_type);

  ////////////////////////////////////////
  // 设置OSThread的状态为ALLOCATED
  ////////////////////////////////////////
  // Initial state is ALLOCATED but not INITIALIZED
  osthread->set_state(ALLOCATED);

  ////////////////////////////////////////
  // 关联OSThread与JavaThread
  ////////////////////////////////////////
  thread->set_osthread(osthread);

  // init thread attributes
  pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

  // stack size
  if (os::Linux::supports_variable_stack_size()) {
    // calculate stack size if it‘s not specified by caller
    if (stack_size == 0) {
      stack_size = os::Linux::default_stack_size(thr_type);

      switch (thr_type) {
      case os::java_thread:
        // Java threads use ThreadStackSize which default value can be
        // changed with the flag -Xss
        assert (JavaThread::stack_size_at_create() > 0, "this should be set");
        stack_size = JavaThread::stack_size_at_create();
        break;
      case os::compiler_thread:
        if (CompilerThreadStackSize > 0) {
          stack_size = (size_t)(CompilerThreadStackSize * K);
          break;
        } // else fall through:
          // use VMThreadStackSize if CompilerThreadStackSize is not defined
      case os::vm_thread:
      case os::pgc_thread:
      case os::cgc_thread:
      case os::watcher_thread:
        if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K);
        break;
      }
    }

    stack_size = MAX2(stack_size, os::Linux::min_stack_allowed);
    pthread_attr_setstacksize(&attr, stack_size);
  } else {
    // let pthread_create() pick the default value.
  }

  // glibc guard page
  pthread_attr_setguardsize(&attr, os::Linux::default_guard_size(thr_type));

  ThreadState state;

  {
    // Serialize thread creation if we are running with fixed stack LinuxThreads
    bool lock = os::Linux::is_LinuxThreads() && !os::Linux::is_floating_stack();
    if (lock) {
      os::Linux::createThread_lock()->lock_without_safepoint_check();
    }

    ////////////////////////////////////////
    // 2. 使用pthread创建本地子线程
    //    本地子线程运行java_start方法,JavaThread作为参数
    ////////////////////////////////////////
    pthread_t tid;
    int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);

    pthread_attr_destroy(&attr);

    if (ret != 0) {
      if (PrintMiscellaneous && (Verbose || WizardMode)) {
        perror("pthread_create()");
      }
      // Need to clean up stuff we‘ve allocated so far
      thread->set_osthread(NULL);
      delete osthread;
      if (lock) os::Linux::createThread_lock()->unlock();
      return false;
    }

    // Store pthread info into the OSThread
    osthread->set_pthread_id(tid);

    ////////////////////////////////////////
    // 3. 等待子线程改变OSThread的状态
    ////////////////////////////////////////
    // Wait until child thread is either initialized or aborted
    {
      Monitor* sync_with_child = osthread->startThread_lock();
      MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag);
      while ((state = osthread->get_state()) == ALLOCATED) {
        sync_with_child->wait(Mutex::_no_safepoint_check_flag);
      }
    }

    if (lock) {
      os::Linux::createThread_lock()->unlock();
    }
  }

  // Aborted due to thread limit being reached
  if (state == ZOMBIE) {
      thread->set_osthread(NULL);
      delete osthread;
      return false;
  }

  // The thread is returned suspended (in state INITIALIZED),
  // and is started higher up in the call chain
  assert(state == INITIALIZED, "race condition");
  return true;
}

java_start

到这里就可以看到,底层是用的pthread库pthread_create创建的子线程会执行java_start方法,

// Thread start routine for all newly created threads
static void *java_start(Thread *thread) {
  // Try to randomize the cache line index of hot stack frames.
  // This helps when threads of the same stack traces evict each other‘s
  // cache lines. The threads can be either from the same JVM instance, or
  // from different JVM instances. The benefit is especially true for
  // processors with hyperthreading technology.
  static int counter = 0;
  int pid = os::current_process_id();
  alloca(((pid ^ counter++) & 7) * 128);

  ThreadLocalStorage::set_thread(thread);

  OSThread* osthread = thread->osthread();
  Monitor* sync = osthread->startThread_lock();

  // non floating stack LinuxThreads needs extra check, see above
  if (!_thread_safety_check(thread)) {
    // notify parent thread
    MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
    osthread->set_state(ZOMBIE);
    sync->notify_all();
    return NULL;
  }

  // thread_id is kernel thread id (similar to Solaris LWP id)
  osthread->set_thread_id(os::Linux::gettid());

  if (UseNUMA) {
    int lgrp_id = os::numa_get_group_id();
    if (lgrp_id != -1) {
      thread->set_lgrp_id(lgrp_id);
    }
  }
  // initialize signal mask for this thread
  os::Linux::hotspot_sigmask(thread);

  // initialize floating point control register
  os::Linux::init_thread_fpu_state();

  // handshaking with parent thread
  {
    MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);

    ////////////////////////////////////////
    // 改变OSThread状态
    ////////////////////////////////////////
    // notify parent thread
    osthread->set_state(INITIALIZED);
    sync->notify_all();

   ////////////////////////////////////////
   // 等待OSThread状态改变
   ////////////////////////////////////////
   // wait until os::start_thread()
    while (osthread->get_state() == INITIALIZED) {
      sync->wait(Mutex::_no_safepoint_check_flag);
    }
  }

  ////////////////////////////////////////
  // 调用JavaThread::run
  ////////////////////////////////////////
  // call one more level start routine
  thread->run();

  return 0;
}

上面注释已经说了,子线程会等待OSThread状态的改变才往下执行,那么现在我们需要先回到JVM_StartThread方法,JavaThread::prepare没什么,看下Thread::start

void Thread::start(Thread* thread) {
  trace("start", thread);
  // Start is different from resume in that its safety is guaranteed by context or
  // being called from a Java method synchronized on the Thread object.
  if (!DisableStartThread) {
    if (thread->is_Java_thread()) {
      // Initialize the thread state to RUNNABLE before starting this thread.
      // Can not set it after the thread started because we do not know the
      // exact thread state at that time. It could be in MONITOR_WAIT or
      // in SLEEPING or some other state.
      java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
                                          java_lang_Thread::RUNNABLE);
    }
    ////////////////////////////////////////
    // 启动JavaThread
    ////////////////////////////////////////
    os::start_thread(thread);
  }
}

os::start_thread

void os::start_thread(Thread* thread) {
  // guard suspend/resume
  MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
  OSThread* osthread = thread->osthread();
  ////////////////////////////////////////
  // 改变OSThread状态
  ////////////////////////////////////////
  osthread->set_state(RUNNABLE);
  ////////////////////////////////////////
  // 调用pd_start_thread
  // pd是platform dependent
  ////////////////////////////////////////
  pd_start_thread(thread);
}

此时子线程等待的OSThread状态改变了,可以继续往下执行JavaThread::run了。

JavaThread::run

JavaThread::run

void JavaThread::run() {

  ////////////////////////////////////////
  // TLAB,TLS 等初始化
  ////////////////////////////////////////

  // initialize thread-local alloc buffer related fields
  this->initialize_tlab();

  // used to test validitity of stack trace backs
  this->record_base_of_stack_pointer();

  // Record real stack base and size.
  this->record_stack_base_and_size();

  // Initialize thread local storage; set before calling MutexLocker
  this->initialize_thread_local_storage();

  this->create_stack_guard_pages();

  this->cache_global_variables();

  // Thread is now sufficient initialized to be handled by the safepoint code as being
  // in the VM. Change thread state from _thread_new to _thread_in_vm
  ThreadStateTransition::transition_and_fence(this, _thread_new, _thread_in_vm);

  assert(JavaThread::current() == this, "sanity check");
  assert(!Thread::current()->owns_locks(), "sanity check");

  DTRACE_THREAD_PROBE(start, this);

  // This operation might block. We call that after all safepoint checks for a new thread has
  // been completed.
  this->set_active_handles(JNIHandleBlock::allocate_block());

  if (JvmtiExport::should_post_thread_life()) {
    JvmtiExport::post_thread_start(this);
  }

  EventThreadStart event;
  if (event.should_commit()) {
     event.set_javalangthread(java_lang_Thread::thread_id(this->threadObj()));
     event.commit();
  }

  // We call another function to do the rest so we are sure that the stack addresses used
  // from there will be lower than the stack base just computed
  thread_main_inner();

  // Note, thread is no longer valid at this point!
}
void JavaThread::thread_main_inner() {
  assert(JavaThread::current() == this, "sanity check");
  assert(this->threadObj() != NULL, "just checking");

  // Execute thread entry point unless this thread has a pending exception
  // or has been stopped before starting.
  // Note: Due to JVM_StopThread we can have pending exceptions already!
  if (!this->has_pending_exception() &&
      !java_lang_Thread::is_stillborn(this->threadObj())) {
    {
      ResourceMark rm(this);
      this->set_native_thread_name(this->get_thread_name());
    }
    HandleMark hm(this);

    ////////////////////////////////////////
    // 执行一开始传入的thread_entry
    // 也就是java.lang.Thread#run
    ////////////////////////////////////////
    this->entry_point()(this, this);
  }

  DTRACE_THREAD_PROBE(stop, this);

  ////////////////////////////////////////
  // 执行清理工作
  ////////////////////////////////////////
  this->exit(false);
  ////////////////////////////////////////
  // 销毁JavaThread
  ////////////////////////////////////////
  delete this;
}

妥妥的,也就是说是由pthread_create出来的线程来执行了Thread.run方法,1:1的线程模型。

参考资料

时间: 2024-11-30 15:50:11

HotSpotVM 线程实现浅析的相关文章

Android线程机制浅析(ppt)

Android线程机制浅析(ppt)

操作系统 进程与线程 图解浅析

进程(process)和线程(thread)是操作系统的基本概念,可是它们比較抽象.不easy掌握.近期.我读到一篇材料,发现有一个非常好的类比,能够把它们解释地清晰易懂. 1.计算机的核心是CPU,它承担了全部的计算任务. 它就像一座工厂.时刻在执行. 2.假定工厂的电力有限.一次仅仅能供给一个车间使用.也就是说.一个车间开工的时候.其它车间都必须停工. 背后的含义就是.单个CPU一次仅仅能执行一个任务. 3.进程就好比工厂的车间,它代表CPU所能处理的单个任务.任一时刻,CPU总是执行一个进

HotSpotVM JNI实现浅析

最近想看下HotSpotVM是怎么找到一个native方法的实现的,例如Thread.start0和FileChannelImpl.transferTo0,最后发现是两种不同的方式. nativeLookup 通过JNI规范可以知道,JVM通过加载动态链接库,将native方法链接过去,具体native方法解析成哪个符号,是按下面的约定来的, Dynamic linkers resolve entries based on their names. A native method name is

单例模式与线程安全问题浅析

近期看到到Struts1与Struts2的比較.说Struts1的控制器是单例的,线程不安全的:Struts2的多例的,不存在线程不安全的问题.之后又想到了之前自己用过的HttpHandler... 这些类.好像单例的线程安全问题确实是随处可见的. 可是仅仅是知道这个是不安全的,也没有认真分析过.接下来就细致分析下. 一,改动单例模式代码 首先我先写一段单例类的代码: /** * @ClassName: Sigleton * @Description: 单例类 * @author 水田 * @d

线程死锁浅析

情形1: dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"不会执行"); }); 如果在主线程添加这段代码,即同步执行添加到主队列的block.这个函数会等block执行完毕返回主线程,再继续执行下面的代码,而block要等主线程返回才会执行,所以循环等待造成死锁. 如果改成异步就可以了,因为当前主线程的一次runloop会马上返回,下一次runloop则会执行主队列里的block. dispatch_async(dispatch

C#线程优先级浅析

C#线程优先级的必要性:如果在应用程序中有多个线程在运行,但一些线程比另一些线程重要,该怎么办在这种情况下,可以在一个进程中为不同的线程指定不同的优先级.一般情况下,如果有优先级较高的线程在工作,就不会给优先级较低的线程分配任何时间片,其优点是可以保证给接收用户输入的线程指定较高的优先级.在大多数的时间内,这个线程什么也不做,而其他线程则执行它们的任务.但是,如果用户输入了信息,这个线程就立即获得比应用程序中其他线程更高的优先级,在短时间内处理用户输入事件. C#线程优先级的规律:高优先级的线程

HotSpotVM 字符串实现浅析#1

今天来看下,HotSpotVM里面字符串实现相关的一些东东.来看这样一个问题,下面的栗子中,在HotSpotVM里面会保存几份Hello,World.这个字符串? public static void main(String[] args) throws Throwable { String s1 = "Hello,World."; String s2 = "Hello,"+"World."; StringBuilder sb = new Str

软件工程(C编码实践篇)课程总结

课程内容来自网易云课堂中科大孟宁老师的软件工程(C编码实践篇)课程. 课程页面 我觉得本门课程的设置非常科学,每一周课程都是基于上一周课程的进一步抽象,使得学习者能够循序渐进,逐渐加深对软件工程的理解. 1. 第一周:熟悉Linux实验环境:要求 实验一:写一个hello world小程序. 实验报告链接+git代码库链接 总结:hello world本身很容易,但是linux下工作方式和windows下完全不同,会给不熟悉linux的人带来不少问题.首先vi没有Windows下的IDE智能化,

我与狗子的日常5

线程池浅析 线程池顾名思义就是放线程的池子 Thread Pool. 那么为什么要有线程池呢?有些时候系统需要处理非常多的执行时间很短的请求,如果每一个请求都开启一个新的线程,则系统创建销毁线程的开销就太大了,甚至在创建和销毁线程的时间比任务的执行时间还长. 使用new Thread() 会有如下缺点: 1.创建和销毁线程耗费大量系统资源: 2.每次new Thread() 缺乏线程管理,被称为野线程,而且可以无限制创建,之间相互竞争,会导致过多占用系统资源导致系统瘫痪. 3.不利于扩展,如定期