91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

AsyncGetCallTrace源碼底層原理是什么

發布時間:2022-02-10 10:04:28 來源:億速云 閱讀:142 作者:iii 欄目:開發技術

這篇文章主要講解了“AsyncGetCallTrace源碼底層原理是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“AsyncGetCallTrace源碼底層原理是什么”吧!

    前言

    AsyncGetCallTrace 是由 OracleJDK/OpenJDK 內部提供的一個函數,該函數可以在 JVM 未進入 safepoint 時正常獲取到當前線程的調用棧(換句話說,使用該函數獲取線程棧時,不會要求 JVM 進入 safepoint。而進入 safepoint 對于 OpenJDK或者 OracleJDK 來說意味著會 STW 的發生,所以這意味著使用該函數獲取線程棧不會產生 STW,It’s amazing.)。目前該函數僅在 Linux X86、Solaris SPARC、Solaris X86 系統下支持。
    另外它還支持在 UNIX 信號處理器中被異步調用,那么我們只需注冊一個 UNIX 信號處理器,并在Handler中調用 AsyncGetCallTrace 獲取當前線程的調用棧即可。由于 UNIX 信號會被隨機的發送給進程的某一線程進行處理,因此可以認為獲取所有線程的調用棧樣本是均勻的。
    但是值得注意的是,該函數不是標準的 JVM API,所以使用的時候,可能存在以下問題:

    移植性問題,因為只能跑在 OpenJDK 或者 OracleJDK 上

    由于不是標準的 JVMTI API,所以使用者需要通過特殊一些方式來獲取該函數,這給使用者帶來了一些不便,但是這也無大礙。

    源碼實現

    關于怎么使用該函數去進行熱點方法采樣的方法,不在本節的討論范圍,在參考資料中,有一些描述,如果還有不清楚的,也可以給我留言交流。

    核心數據結構

    // call frame copied from old .h file and renamed
    //  Fields:
    //    1) For Java frame (interpreted and compiled),
    //       lineno    - bci of the method being executed or -1 if bci is not available
    //       method_id - jmethodID of the method being executed
    //    2) For native method
    //       lineno    - (-3)
    //       method_id - jmethodID of the method being executed
    typedef struct {
        jint lineno;                      // numberline number in the source file
        jmethodID method_id;              // method executed in this frame
    } ASGCT_CallFrame;
    // call trace copied from old .h file and renamed
    // Fields:
    //   env_id     - ID of thread which executed this trace.
    //   num_frames - number of frames in the trace.
    //                (< 0 indicates the frame is not walkable).
    //   frames     - the ASGCT_CallFrames that make up this trace. Callee followed by callers.
    typedef struct {
        JNIEnv *env_id;                   // Env where trace was recorded
        jint num_frames;                  // number of frames in this trace
        ASGCT_CallFrame *frames;          // frames
    } ASGCT_CallTrace;
    // These name match the names reported by the forte quality kit
    // 這些枚舉是對應到 ASGCT_CallTrace 中的 num_frames 的返回值的
    // 舉個例子,當 JVM 在進行 GC 時,返回值中
    // ASGCT_CallTrace.num_frames == ticks_GC_active
    enum {
      ticks_no_Java_frame         =  0,
      ticks_no_class_load         = -1,
      ticks_GC_active             = -2,
      ticks_unknown_not_Java      = -3,
      ticks_not_walkable_not_Java = -4,
      ticks_unknown_Java          = -5,
      ticks_not_walkable_Java     = -6,
      ticks_unknown_state         = -7,
      ticks_thread_exit           = -8,
      ticks_deopt                 = -9,
      ticks_safepoint             = -10
    };

    函數申明

    //   trace    - trace data structure to be filled by the VM.
    //   depth    - depth of the call stack trace.
    //   ucontext - ucontext_t of the LWP
    void AsyncGetCallTrace(ASGCT_CallTrace *trace, jint depth, void* ucontext)

    該函數的調用者獲取到的棧是屬于某一個特定線程的,這個線程是由trace->env_id 唯一標識的,而且 該標識的線程 必須和當前執行 AsyncGetCallTrace 方法的 線程 是同一線程。同時調用者需要為 trace->frames 分配足夠多的內存,來保存棧深最多為 depth 的棧。若獲取到了有關的棧,JVM 會自動把相關的堆棧信息寫入 trace 中。接下來我們通過源碼來看看內部實現。

    AsyncGetCallTrace 實現

    具體分析直接看代碼里面的注釋,為了保持完整性,該方法的任何代碼我都沒刪除,源碼位置:/hotspot/src/share/vm/prims/forte.cpp

    void AsyncGetCallTrace(ASGCT_CallTrace *trace, jint depth, void* ucontext) {
      JavaThread* thread;
      // 1. 首先判斷 jniEnv 是否為空,或者 jniEnv 對應的線程是否有效,或者該線程是否已經退出,
      // 任一條件滿足,則返回 ticks_thread_exit(對應為核心數據結構中枚舉類型所示)
      if (trace->env_id == NULL ||
        (thread = JavaThread::thread_from_jni_environment(trace->env_id)) == NULL ||
        thread->is_exiting()) {
        // bad env_id, thread has exited or thread is exiting
        trace->num_frames = ticks_thread_exit; // -8
        return;
      }
      if (thread->in_deopt_handler()) {
        // thread is in the deoptimization handler so return no frames
        trace->num_frames = ticks_deopt; // -9
        return;
      }
      // 2. 這里對 jniEnv 所指線程是否是當前線程進行斷言,如果不相等則直接報錯
      assert(JavaThread::current() == thread,
             "AsyncGetCallTrace must be called by the current interrupted thread");
      // 3. JVMTI_EVENT_CLASS_LOAD 事件必須 enable,否則直接返回 ticks_no_class_load
      if (!JvmtiExport::should_post_class_load()) {
        trace->num_frames = ticks_no_class_load; // -1
        return;
      }
      // 4. 當前 heap 必須沒有進行 GC ,否則直接返回 ticks_GC_active
      if (Universe::heap()->is_gc_active()) {
        trace->num_frames = ticks_GC_active; // -2
        return;
      }
      // 5. 根據線程的當前狀態來獲取對應的線程棧,只有在線程的狀態為 _thread_in_vm/_thread_in_vm_trans 
      // 和 _thread_in_Java/_thread_in_Java_trans 時才會進行線程棧的爬取
      switch (thread->thread_state()) {
      case _thread_new:
      case _thread_uninitialized:
      case _thread_new_trans:
        // We found the thread on the threads list above, but it is too
        // young to be useful so return that there are no Java frames.
        trace->num_frames = 0;
        break;
      case _thread_in_native:
      case _thread_in_native_trans:
      case _thread_blocked:
      case _thread_blocked_trans:
      case _thread_in_vm:
      case _thread_in_vm_trans:
        {
          frame fr;
          // 首先獲取當前線程的棧頂棧幀
          // param isInJava == false - indicate we aren't in Java code
          if (!thread->pd_get_top_frame_for_signal_handler(&fr, ucontext, false)) {
            trace->num_frames = ticks_unknown_not_Java;  // -3 unknown frame
          } else {
            //  該線程如果沒有任何的 Java 棧幀,直接返回 0 幀 
            if (!thread->has_last_Java_frame()) {
              trace->num_frames = 0; // No Java frames
            } else {
              trace->num_frames = ticks_not_walkable_not_Java;    // -4 non walkable frame by default
    
              // 如果存在合法的棧幀,則填充 trace 中的 frames 和 num_frames
              forte_fill_call_trace_given_top(thread, trace, depth, fr);
              ...
            }
          }
        }
        break;
      case _thread_in_Java:
      case _thread_in_Java_trans:
        {
          frame fr;
          // param isInJava == true - indicate we are in Java code
          if (!thread->pd_get_top_frame_for_signal_handler(&fr, ucontext, true)) {
            trace->num_frames = ticks_unknown_Java;  // -5 unknown frame
          } else {
            trace->num_frames = ticks_not_walkable_Java;  // -6, non walkable frame by default
            forte_fill_call_trace_given_top(thread, trace, depth, fr);
          }
        }
        break;
      default:
        // Unknown thread state
        trace->num_frames = ticks_unknown_state; // -7
        break;
      }
    }

    從以上的分析中,最終獲取線程棧的地方主要在這兩個方法 pd_get_top_frame_for_signal_handler 和 forte_fill_call_trace_given_top,接下來我們來看下這兩個方法的實現。

    pd_get_top_frame_for_signal_handler 實現

    從方法名不難看出,該方法的主要作用是獲取當前線程的棧頂幀。后面跟了個 signal_handler,最初的想法我猜應該是為響應 UNIX 下的 SIGPROF 信號的。因為本身 AsyncGetCallTrace 就是為此而生的。該方法的源碼位置 /hotspot/src/os_cpu/linux_x86/vm/thread_linux_x86.cpp

    bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr,
      void* ucontext, bool isInJava) {
      assert(Thread::current() == this, "caller must be current thread");
      return pd_get_top_frame(fr_addr, ucontext, isInJava);
    }

    很簡單,判斷一下是否是當前線程,至于 isInJava 入參是和當前的線程的狀態相關的,如果是跑在 java 代碼內,則為 true,否則為 false。

    pd_get_top_frame 實現

    bool JavaThread::pd_get_top_frame(frame* fr_addr, void* ucontext, bool isInJava) {
      assert(this->is_Java_thread(), "must be JavaThread");
      JavaThread* jt = (JavaThread *)this;
      // If we have a last_Java_frame, then we should use it even if
      // isInJava == true.  It should be more reliable than ucontext info.
      if (jt->has_last_Java_frame() && jt->frame_anchor()->walkable()) {
        *fr_addr = jt->pd_last_frame();
        return true;
      }
      // At this point, we don't have a last_Java_frame, so
      // we try to glean some information out of the ucontext
      // if we were running Java code when SIGPROF came in.
      if (isInJava) {
        ucontext_t* uc = (ucontext_t*) ucontext;
        intptr_t* ret_fp;
        intptr_t* ret_sp;
        ExtendedPC addr = os::Linux::fetch_frame_from_ucontext(this, uc,
          &ret_sp, &ret_fp);
        if (addr.pc() == NULL || ret_sp == NULL ) {
          // ucontext wasn't useful
          return false;
        }
        frame ret_frame(ret_sp, ret_fp, addr.pc());
        if (!ret_frame.safe_for_sender(jt)) {
    #ifdef COMPILER2
          // C2 uses ebp as a general register see if NULL fp helps
          frame ret_frame2(ret_sp, NULL, addr.pc());
          if (!ret_frame2.safe_for_sender(jt)) {
            // nothing else to try if the frame isn't good
            return false;
          }
          ret_frame = ret_frame2;
    #else
          // nothing else to try if the frame isn't good
          return false;
    #endif /* COMPILER2 */
        }
        *fr_addr = ret_frame;
        return true;
      }
      // nothing else to try
      return false;
    }

    實際上拿棧頂幀的函數,由于函數的源碼較長,我就簡短的描述一下邏輯

    • 當前線程只能是 java 線程

    • 判斷棧頂幀是否存在,并且當前的棧是 walkable 的,若二者的滿足,則返回 javaThread 的 pd_last_frame,即棧頂幀,結束;否則繼續;

    • 如果當前線程是跑 java 代碼,那么我們嘗試在 ucontext_t 內收集一些我們需要的信息,比如說棧幀

    forte_fill_call_trace_given_top 實現

    當我們獲取到棧頂的幀之后,接下來的事情就順理成章了,只要從棧頂開始,遍歷整個堆棧就能把所有的方法都獲取到了,同時將獲取到的結果保存到ASGCT_CallTrace,源碼位置:/hotspot/src/share/vm/prims/forte.cpp

    static void forte_fill_call_trace_given_top(JavaThread* thd,
                                                ASGCT_CallTrace* trace,
                                                int depth,
                                                frame top_frame) {
      NoHandleMark nhm;
      frame initial_Java_frame;
      Method* method;
      int bci = -1; // assume BCI is not available for method
                    // update with correct information if available
      int count;
      count = 0;
      assert(trace->frames != NULL, "trace->frames must be non-NULL");
      // 1. 獲取到棧頂的第一個 java 棧幀
      // Walk the stack starting from 'top_frame' and search for an initial Java frame.
      find_initial_Java_frame(thd, &top_frame, &initial_Java_frame, &method, &bci);
      // Check if a Java Method has been found.
      if (method == NULL) return;
      //  2. 如果不是合法的方法,直接返回
      if (!method->is_valid_method()) {
        trace->num_frames = ticks_GC_active; // -2
        return;
      }
      vframeStreamForte st(thd, initial_Java_frame, false);
      // 循環迭代棧上的所有棧幀,一一獲取每個方法 bci 和 方法 id,這里會用上從外面傳入的最大棧深 depth
      for (; !st.at_end() && count < depth; st.forte_next(), count++) {
        bci = st.bci();
        method = st.method();
        if (!method->is_valid_method()) {
          // we throw away everything we've gathered in this sample since
          // none of it is safe
          trace->num_frames = ticks_GC_active; // -2
          return;
        }
        // 根據方法對象獲取方法 id,如果方法 id 在此時還未產生,則返回 NULL
        trace->frames[count].method_id = method->find_jmethod_id_or_null();
    
        // 如果方法是不是 native 方法,則把 lineno 設置為 bci 的值,否則置 -3 
        if (!method->is_native()) {
          trace->frames[count].lineno = bci;
        } else {
          trace->frames[count].lineno = -3;
        }
      }
      trace->num_frames = count;
      return;
    }

    感謝各位的閱讀,以上就是“AsyncGetCallTrace源碼底層原理是什么”的內容了,經過本文的學習后,相信大家對AsyncGetCallTrace源碼底層原理是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    太原市| 康马县| 通榆县| 青海省| 连南| 织金县| 曲阜市| 紫金县| 吉木乃县| 红原县| 泰兴市| 榕江县| 阿拉善盟| 视频| 大连市| 虞城县| 营山县| 淮阳县| 岑溪市| 沾益县| 安乡县| 普宁市| 临武县| 靖江市| 方山县| 藁城市| 寻甸| 美姑县| 全南县| 梨树县| 久治县| 东乡族自治县| 怀化市| 清苑县| 芦山县| 芮城县| 竹山县| 保山市| 邮箱| 本溪市| 东至县|