• 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的线程模型。

    參考资料

  • 相关阅读:
    教你如何把切割好的html页面转换成可供DIY的页面
    [二次开发][Discuz!X] Discuz!二次开发基本知识
    discuz之C::t 方法
    header_userstatus.htm文件注释
    【discuzx2】header.htm模板文件深入分析
    discuz的页头header和页尾footer文件必要代码
    discuz全局数组变量 后台各项设置 完整版
    交换机到底有没有MAC地址?
    妙味课堂—JavaScript基础课程笔记
    jquery 绘图工具 flot 学习笔记
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/7219235.html
Copyright © 2020-2023  润新知