Top


定義場所(file name)

hotspot/src/share/vm/runtime/thread.cpp

説明(description)

// For any new cleanup additions, please check to see if they need to be applied to
// cleanup_failed_attach_current_thread as well.

名前(function name)

void JavaThread::exit(bool destroy_vm, ExitType exit_type) {

本体部(body)

  {- -------------------------------------------
  (1) (assert)
      ---------------------------------------- -}

      assert(this == JavaThread::current(),  "thread consistency check");

  {- -------------------------------------------
  (1) (デバッグ用の処理) (関連する develop オプションが指定されている場合にのみ実行) (See: InitializeJavaLangSystem)
      ---------------------------------------- -}

      if (!InitializeJavaLangSystem) return;

  {- -------------------------------------------
  (1) (変数宣言など)
      ---------------------------------------- -}

      HandleMark hm(this);
      Handle uncaught_exception(this, this->pending_exception());

  {- -------------------------------------------
  (1) カレントスレッドの pending_exception を消しておく.
      (なお, 消す前の pending_exception は上の uncaught_exception 局所変数に保存されている)
      ---------------------------------------- -}

      this->clear_pending_exception();

  {- -------------------------------------------
  (1) (変数宣言など)
      ---------------------------------------- -}

      Handle threadObj(this, this->threadObj());

  {- -------------------------------------------
  (1) (assert)
      ---------------------------------------- -}

      assert(threadObj.not_null(), "Java thread object should be created");

  {- -------------------------------------------
  (1) (プロファイル情報の出力) (See: ThreadProfiler)
      (See: ThreadProfiler::disengage(), ThreadProfiler::print())
      ---------------------------------------- -}

      if (get_thread_profiler() != NULL) {
        get_thread_profiler()->disengage();
        ResourceMark rm;
        get_thread_profiler()->print(get_thread_name());
      }


  {- -------------------------------------------
  (1) #TODO
      ---------------------------------------- -}

      // FIXIT: This code should be moved into else part, when reliable 1.2/1.3 check is in place
      {
        EXCEPTION_MARK;

        CLEAR_PENDING_EXCEPTION;
      }

  {- -------------------------------------------
  (1) 以下のどちらかが成り立つ場合は, 次の if ブロック内の処理を実行する.
      * この JavaThread::exit() が, HotSpot の終了処理である Threads::destroy_vm() から
        呼ばれたものではない場合 (= 引数の destroy_vm が false の場合)
      * JDK バージョンが 1.2 の場合
      ---------------------------------------- -}

      // FIXIT: The is_null check is only so it works better on JDK1.2 VM's. This
      // has to be fixed by a runtime query method
      if (!destroy_vm || JDK_Version::is_jdk12x_version()) {

    {- -------------------------------------------
  (1.1) もしこの時点で pending_exception が空でなければ 
        (= uncaught_exception.not_null() が true であれば), 
        uncaughtException を呼び出す処理を行う.
        これは以下のどちらかの処理によって行う.
        * JDK 1.5 以降のライブラリを使っている場合は
          (= java.lang.Thread.dispatchUncaughtException() メソッドが存在する場合は), 
          java.lang.Thread.dispatchUncaughtException() メソッドを呼び出す.
        * JDK 1.4 以前の古いライブラリとリンクしている場合は, 
          java_lang_Thread::threadGroup() で ThreadGroup オブジェクトを取得し, 
          それに対して java.lang.ThreadGroup.uncaughtException() を呼び出す.

        (なお, UncaughtExceptionHandler 内で例外が起こった場合には, 
         この場で "\nException: ... thrown from the UncaughtExceptionHandler ..." というエラーメッセージを出すだけにして, 
         例外自体は消す.)
        ---------------------------------------- -}

        // JSR-166: change call from from ThreadGroup.uncaughtException to
        // java.lang.Thread.dispatchUncaughtException
        if (uncaught_exception.not_null()) {
          Handle group(this, java_lang_Thread::threadGroup(threadObj()));
          Events::log("uncaught exception INTPTR_FORMAT " " INTPTR_FORMAT " " INTPTR_FORMAT",
            (address)uncaught_exception(), (address)threadObj(), (address)group());
          {
            EXCEPTION_MARK;
            // Check if the method Thread.dispatchUncaughtException() exists. If so
            // call it.  Otherwise we have an older library without the JSR-166 changes,
            // so call ThreadGroup.uncaughtException()
            KlassHandle recvrKlass(THREAD, threadObj->klass());
            CallInfo callinfo;
            KlassHandle thread_klass(THREAD, SystemDictionary::Thread_klass());
            LinkResolver::resolve_virtual_call(callinfo, threadObj, recvrKlass, thread_klass,
                                               vmSymbols::dispatchUncaughtException_name(),
                                               vmSymbols::throwable_void_signature(),
                                               KlassHandle(), false, false, THREAD);
            CLEAR_PENDING_EXCEPTION;
            methodHandle method = callinfo.selected_method();
            if (method.not_null()) {
              JavaValue result(T_VOID);
              JavaCalls::call_virtual(&result,
                                      threadObj, thread_klass,
                                      vmSymbols::dispatchUncaughtException_name(),
                                      vmSymbols::throwable_void_signature(),
                                      uncaught_exception,
                                      THREAD);
            } else {
              KlassHandle thread_group(THREAD, SystemDictionary::ThreadGroup_klass());
              JavaValue result(T_VOID);
              JavaCalls::call_virtual(&result,
                                      group, thread_group,
                                      vmSymbols::uncaughtException_name(),
                                      vmSymbols::thread_throwable_void_signature(),
                                      threadObj,           // Arg 1
                                      uncaught_exception,  // Arg 2
                                      THREAD);
            }
            if (HAS_PENDING_EXCEPTION) {
              ResourceMark rm(this);
              jio_fprintf(defaultStream::error_stream(),
                    "\nException: %s thrown from the UncaughtExceptionHandler"
                    " in thread \"%s\"\n",
                    Klass::cast(pending_exception()->klass())->external_name(),
                    get_thread_name());
              CLEAR_PENDING_EXCEPTION;
            }
          }
        }

    {- -------------------------------------------
  (1.1) JavaCalls::call_virtual() 経由で java.lang.Thread.exit() メソッドを呼び出す.

        なお, java.lang.Thread.stop() と競合した場合のために, 失敗しても 3 回まではやり直す (以下の count の値を参照). 
        3 回失敗してもダメだった場合は java.lang.Thread.exit() を呼び出すのは諦める.
        (コメントによると, java.lang.Thread.stop() 自体が deprecated なのでそのくらいでいいだろう, とのこと)
        ---------------------------------------- -}

        // Call Thread.exit(). We try 3 times in case we got another Thread.stop during
        // the execution of the method. If that is not enough, then we don't really care. Thread.stop
        // is deprecated anyhow.
        { int count = 3;
          while (java_lang_Thread::threadGroup(threadObj()) != NULL && (count-- > 0)) {
            EXCEPTION_MARK;
            JavaValue result(T_VOID);
            KlassHandle thread_klass(THREAD, SystemDictionary::Thread_klass());
            JavaCalls::call_virtual(&result,
                                  threadObj, thread_klass,
                                  vmSymbols::exit_method_name(),
                                  vmSymbols::void_method_signature(),
                                  THREAD);
            CLEAR_PENDING_EXCEPTION;
          }
        }

    {- -------------------------------------------
  (1.1) (JVMTI のフック点)
        ---------------------------------------- -}

        // notify JVMTI
        if (JvmtiExport::should_post_thread_life()) {
          JvmtiExport::post_thread_end(this);
        }

    {- -------------------------------------------
  (1.1) (この時点で JVMTI agent への通知は終わっている.
        ただし, この先に進む前に, カレントスレッドが 
        suspend されていないかをチェックしておく必要がある)

        JavaThread::is_external_suspend() で suspend 状態をチェックし, 
        suspend されていた場合は JavaThread::java_suspend_self() による待機を行う.
        これを, JavaThread::is_external_suspend() が false を返すまで繰り返す.

        JavaThread::is_external_suspend() が false を返せば, 
        カレントスレッドの TerminatedTypes を _thread_exiting に変更して, この while ループを抜ける.


        (なお, 以下の while ループから脱出する寸前に 
         ThreadService::current_thread_exiting() を呼び出して(プロファイル情報の記録)処理を行っている (JMM 用).
         See: ThreadService::get_live_thread_count(), ThreadService::get_daemon_thread_count())
        ---------------------------------------- -}

        // We have notified the agents that we are exiting, before we go on,
        // we must check for a pending external suspend request and honor it
        // in order to not surprise the thread that made the suspend request.
        while (true) {
          {
            MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag);
            if (!is_external_suspend()) {
              set_terminated(_thread_exiting);
              ThreadService::current_thread_exiting(this);
              break;
            }
            // Implied else:
            // Things get a little tricky here. We have a pending external
            // suspend request, but we are holding the SR_lock so we
            // can't just self-suspend. So we temporarily drop the lock
            // and then self-suspend.
          }

          ThreadBlockInVM tbivm(this);
          java_suspend_self();

          // We're done with this suspend request, but we have to loop around
          // and check again. Eventually we will get SR_lock without a pending
          // external suspend request and will be able to mark ourselves as
          // exiting.
        }

    {- -------------------------------------------
  (1.1) (この段階以降では, このスレッドに対して外部からサスペンドをかけることは出来ない)
        ---------------------------------------- -}

        // no more external suspends are allowed at this point

  {- -------------------------------------------
  (1) (以下の else ブロックは, JDK バージョンが 1.2 以外で
       かつ Threads::destroy_vm() から呼び出された場合の処理.

       before_exit() で既に JVMTI への通知処理等は終わっているため, ここでは何もしない)
      ---------------------------------------- -}

      } else {
        // before_exit() has already posted JVMTI THREAD_END events
      }

  {- -------------------------------------------
  (1) ensure_join() を呼び出して, このスレッドに対して java.lang.Thread.join() で待っているスレッドに終了を通知する.

      (なおコメントによると, 
       この処理は java.lang.Thread.exit() の後で行わなければならない.
       理由は, このスレッドが ThreadGroup の最後のスレッドだった場合に, 
       通知が行われる前に ThreadGroup の destroyed bit を立てないといけないため, 
       とのこと.)
      ---------------------------------------- -}

      // Notify waiters on thread object. This has to be done after exit() is called
      // on the thread (if the thread is the last thread in a daemon ThreadGroup the
      // group should have the destroyed bit set before waiters are notified).
      ensure_join(this);

  {- -------------------------------------------
  (1) (assert)
      ---------------------------------------- -}

      assert(!this->has_pending_exception(), "ensure_join should have cleared");

  {- -------------------------------------------
  (1) この JavaThread::exit() が JNI の DetachCurrentThread() から呼ばれたものである場合, 
      (JNI の仕様でカレントスレッドが保持しているロックを全て解放しなければいけないと規定されているので)
      ObjectSynchronizer::release_monitors_owned_by_thread() を呼んでロック開放処理を行う.
      ---------------------------------------- -}

      // 6282335 JNI DetachCurrentThread spec states that all Java monitors
      // held by this thread must be released.  A detach operation must only
      // get here if there are no Java frames on the stack.  Therefore, any
      // owned monitors at this point MUST be JNI-acquired monitors which are
      // pre-inflated and in the monitor cache.
      //
      // ensure_join() ignores IllegalThreadStateExceptions, and so does this.
      if (exit_type == jni_detach && JNIDetachReleasesMonitors) {
        assert(!this->has_last_Java_frame(), "detaching with Java frames?");
        ObjectSynchronizer::release_monitors_owned_by_thread(this);
        assert(!this->has_pending_exception(), "release_monitors should have cleared");
      }

  {- -------------------------------------------
  (1) (assert)
      ---------------------------------------- -}

      // These things needs to be done while we are still a Java Thread. Make sure that thread
      // is in a consistent state, in case GC happens
      assert(_privileged_stack_top == NULL, "must be NULL when we get here");

  {- -------------------------------------------
  (1) カレントスレッドが JNI ローカル参照フレームを保持していれば, 破棄しておく.
      (JNIHandleBlock::release_block() で JNIHandleBlock を開放し, 
       Thread::set_active_handles() で active_handles フィールドを NULL に戻す.
       See: [here](no3059hRF.html) for details)
      ---------------------------------------- -}

      if (active_handles() != NULL) {
        JNIHandleBlock* block = active_handles();
        set_active_handles(NULL);
        JNIHandleBlock::release_block(block);
      }

  {- -------------------------------------------
  (1) カレントスレッドが JNIHandleBlock の thread local なフリーリストを保持していれば, 破棄しておく.
      (JNIHandleBlock::release_block() で JNIHandleBlock を開放し, 
       Thread::set_free_handle_block() で free_handle_block フィールドを NULL に戻す.
       See: [here](no3059hRF.html) for details)
      ---------------------------------------- -}

      if (free_handle_block() != NULL) {
        JNIHandleBlock* block = free_handle_block();
        set_free_handle_block(NULL);
        JNIHandleBlock::release_block(block);
      }

  {- -------------------------------------------
  (1) JavaThread::remove_stack_guard_pages() を呼んで, 
      カレントスレッドのスタック上の guard page を解除しておく.

      (なおコメントによると, 
       この解除操作はカレントスレッドが未だ valid thread である間に行わないといけない, 
       とのこと)
      ---------------------------------------- -}

      // These have to be removed while this is still a valid thread.
      remove_stack_guard_pages();

  {- -------------------------------------------
  (1) UseTLAB オプションが指定されている場合には, 
      ThreadLocalAllocBuffer::make_parsable() を呼んで
      現在の TLAB の残りのスペースを dummy の int 配列で埋めておく, 
      ---------------------------------------- -}

      if (UseTLAB) {
        tlab().make_parsable(true);  // retire TLAB
      }

  {- -------------------------------------------
  (1) もしカレントスレッドが JvmtiThreadState を持っている可能性があれば, 
      JvmtiExport::cleanup_thread() を呼んでメモリを開放しておく.
      ---------------------------------------- -}

      if (JvmtiEnv::environments_might_exist()) {
        JvmtiExport::cleanup_thread(this);
      }

  {- -------------------------------------------
  (1) G1GC 使用時には, カレントスレッドが使用していたキュー (ObjPtrQueue, DirtyCardQueue) に
      溜まっている内容を, 対応する PtrQueueSet に書き出しておく.
      ---------------------------------------- -}

    #ifndef SERIALGC
      // We must flush G1-related buffers before removing a thread from
      // the list of active threads.
      if (UseG1GC) {
        flush_barrier_queues();
      }
    #endif

  {- -------------------------------------------
  (1) Threads::remove() を呼び出して, カレントスレッドを Threads::_thread_list から外しておく.
      (ついでに, 終了処理を担当するスレッドが待っているかもしれないので, 
       残りの non-daemon thread の数が 1 つになっていたら, そのスレッドを起こす処理も行っている)
      ---------------------------------------- -}

      // Remove from list of active threads list, and notify VM thread if we are the last non-daemon thread
      Threads::remove(this);
    }

This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.