异常

原理图

graph LR
解释执行-->异常投递-->|case Instruction::THROW|setException("异常对象赋值给Thread的tlsPtr_exception的成员变量")
解释执行-->异常处理-->HANDLE_PENDING_EXCEPTION-->|找到能处理该异常的指令码|ArtMethod::FindCatchBlock

机器码执行-->InstructionCodeGeneratorX86::VisitThrow-->artDeliverExceptionFromCode-->setException
artDeliverExceptionFromCode-->|找到异常处理处的指令位置|findCatch("exception_handler.FindCatch(exception);")

解释执行异常抛出和处理

被抛出的异常对象赋值给Thread tlsPtr_exception的成员变量,异常投递的工作就算完成;

接下来就是异常处理的过程。以switch/case方式来解释执行的代码中,下面这个宏用于判断是否有异常发生并处理它。

Dex_instruction_list.h

#define DEX_INSTRUCTION_LIST(V) \
  V(0x27, THROW, "throw", k11x, false, kIndexNone, kThrow, kVerifyRegA) \

interpreter_switch_impl.cc

ExecuteSwitchImpl

template<bool do_access_check, bool transaction_active>
JValue ExecuteSwitchImpl(......) {
    ...... //该函数详情见10.2.3.1节
    do {
        dex_pc = inst->GetDexPc(insns);
        ......
        inst_data = inst->Fetch16(0);
        switch (inst->Opcode(inst_data)) {      //switch/case方式执行不同的dex指令
            ......
            case Instruction::THROW: { //抛异常
                PREAMBLE();
                Object* exception = shadow_frame.GetVRegReference( inst->VRegA_11x(inst_data));
                if (UNLIKELY(exception == nullptr)) {
                    //Throw抛出的异常对象为空,则重新抛一个空指针异常
                    ThrowNullPointerException("throw with null exception");
                } else if (.....) {.....}
                else {
                /*注意:Thread tlsPtr_有一个名为exception的成员变量,其类型为Throwable*。
                  其他代码逻辑要检查是否有异常发生的话,只要判断tlsPtr_exception是否为nullptr
                  即可。如果tlsPtr_ exception不为空指针,则表明有异常发生。
                  下面的SetException函数将把抛出的异常对象赋值给tlsPtr_exception*///main
                    self->SetException(exception->AsThrowable());
                }
            HANDLE_PENDING_EXCEPTION();      //检查是否有异常发生,并做对应处理。main
            break;
        }
    .....
}

HANDLE_PENDING_EXCEPTION

define HANDLE_PENDING_EXCEPTION()                         \
    do {                                                    \
        self->AllowThreadSuspension();       \
        /*下面这个函数用于从当前正在执行的方法里找到对应的catch处理语句,如果能处理所抛出
          的异常,则返回异常处理对应的dex指令码的位置*/
        uint32_t found_dex_pc = FindNextInstructionFollowingException(self,\
            shadow_frame,    inst->GetDexPc(insns),  instrumentation);      \
        //如果本方法无法处理这个异常,则要退出整个方法的执行
        if (found_dex_pc == DexFile::kDexNoIndex) {         \
                ......\
            }                            \
        return JValue(); /* 退出本方法的执行,调用者将继续检查并处理异常 */   \
        } else  {                                                   \
            //如果本方法catch住了所抛出的异常,则找到对应的处理指令
            int32_t displacement = static_cast<int32_t>(found_dex_pc) - \static_cast<int32_t>(dex_pc); \
            inst = inst->RelativeAt(displacement);            \
        }                      \
    } while (false)

interpreter_common.cc

FindNextInstructionFollowingException

uint32_t FindNextInstructionFollowingException(
        Thread* self, ShadowFrame& shadow_frame, uint32_t dex_pc,
        const instrumentation::Instrumentation* instrumentation) {
    self->VerifyStack();
    StackHandleScope<2> hs(self);
    Handle<mirror::Throwable> exception(hs.NewHandle(self->GetException()));
    ......
    bool clear_exception = false;
    //调用ArtMethod的FindCatchBlock。
    uint32_t found_dex_pc = shadow_frame.GetMethod()->FindCatchBlock(
            hs.NewHandle(exception->GetClass()), dex_pc, &clear_exception);
    if (found_dex_pc == DexFile::kDexNoIndex && instrumentation != nullptr) {
        //如果本方法无法处理这个异常,则表示要进行栈回溯。此时将触发Instrumentation的
        //MethodUnwindEvent函数
        instrumentation->MethodUnwindEvent(self, shadow_frame.GetThisObject(),
                                    shadow_frame.GetMethod(), dex_pc);
    } else {......}
    return found_dex_pc;
}
ArtMethod::FindCatchBlock

art_quick_to_interpreter_bridge

quick_entrypoints_x86.S

RETURN_OR_DELIVER_PENDING_EXCEPTION

MACRO0(RETURN_OR_DELIVER_PENDING_EXCEPTION)
    #检查Thread tlsPtr_ exception变量是否为空,如果不为空,则跳转到DELIVER_PENDING_EXCEPTION宏处,main
    cmpl MACRO_LITERAL(0),%fs:THREAD_EXCEPTION_OFFSET
    jne 1f
    ret
1:
    DELIVER_PENDING_EXCEPTION                #异常处理宏
END_MACRO

DELIVER_PENDING_EXCEPTION

MACRO0(DELIVER_PENDING_EXCEPTION)            #异常处理宏定义
    #该宏内部将设置tlsPtr_ managed_stack top_quick_frame_的值
    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME ebx, ebx
    subl MACRO_LITERAL(12), %esp
    pushl %fs:THREAD_SELF_OFFSET
    #调用artDeliverPendingExceptionFromCode函数,main
    call SYMBOL(artDeliverPendingExceptionFromCode)
    UNREACHABLE
END_MACRO

quick_throw_entrypoints.cc

extern "C" NO_RETURN void artDeliverPendingExceptionFromCode(Thread* self) {
    ScopedQuickEntrypointChecks sqec(self);
    self->QuickDeliverException(); //调用Thread QuickDeliverException函数
}

机器码异常抛出和处理

code_generator_x86.cc

void InstructionCodeGeneratorX86::VisitThrow(HThrow* instruction) {
  codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pDeliverException),
                          instruction,
                          instruction->GetDexPc(),
                          nullptr);
  CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
}

quick_entrypoints_x86.S

art_quick_deliver_exception

ONE_ARG_RUNTIME_EXCEPTION art_quick_deliver_exception, \
            artDeliverExceptionFromCode      #调用C++层artDeliverExceptionFromCode
//ONE_ARG_RUNTIME_EXCEPTION是一个宏,其内部调用SETUP_SAVE_CALLEE_SAVE_FRAME
//由于artDeliverExceptionFromCode位于虚拟机执行层,所以需要设置tlsPtr_managed_stack top_quick_frame_
MACRO2(ONE_ARG_RUNTIME_EXCEPTION, c_name, cxx_name)
    DEFINE_FUNCTION VAR(c_name)
    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME ebx, ebx     mov %esp, %ecx
    // Outgoing argument set up
    subl MACRO_LITERAL(8), %esp
    pushl %fs:THREAD_SELF_OFFSET
    PUSH eax
    call CALLVAR(cxx_name)
    UNREACHABLE
    END_FUNCTION VAR(c_name)
END_MACRO

quick_throw_entrypoints.cc

artDeliverExceptionFromCode

extern "C" NO_RETURN void artDeliverExceptionFromCode(
            mirror::Throwable* exception, Thread* self){
    ScopedQuickEntrypointChecks sqec(self);
    if (exception == nullptr) {
        self->ThrowNewException("Ljava/lang/NullPointerException;",
            "throw with null exception");
    } else {
        self->SetException(exception); //设置tlsPtr_ exception
    }
    self->QuickDeliverException(); //还是调用Thread类的QuickDeliverException
}

thread.cc

QuickDeliverException

void Thread::QuickDeliverException() {
    mirror::Throwable* exception = GetException();
    //判断是否为Deoptimize相关的异常。此处不讨论Deoptimize的情况,所以
    //is_deoptimization为false
    bool is_deoptimization = (exception == GetDeoptimizationException());
    if (!is_deoptimization) {
            instrumentation::Instrumentation* instrumentation =
                    Runtime::Current()->GetInstrumentation();
        if (instrumentation->HasExceptionCaughtListeners() &&
                            IsExceptionThrownByCurrentMethod(exception)) {
                StackHandleScope<1> hs(this);
            HandleWrapper<mirror::Throwable> h_exception(hs.NewHandleWrapper(
                                &exception));
            instrumentation->ExceptionCaughtEvent(this, exception);
        }
        ......
    }
    ClearException();
    QuickExceptionHandler exception_handler(this, is_deoptimization);
    if (is_deoptimization) {
        //DeoptimizeStack函数的详情见10.4.2.2节
        exception_handler.DeoptimizeStack();
    } else {
        //FindCatch非常关键,它将找到异常处理处的指令位置(pc),main
        exception_handler.FindCatch(exception);
    }
    exception_handler.UpdateInstrumentationStack();
    exception_handler.DoLongJump();      //跳转到异常处理对应的地址
}

quick_exception_handler.cc

QuickExceptionHandler::FindCatch

void QuickExceptionHandler::FindCatch(mirror::Throwable* exception) {
    StackHandleScope<1> hs(self_);
    Handle<mirror::Throwable> exception_ref(hs.NewHandle(exception));
    //关键类
    CatchBlockStackVisitor visitor(self_, context_, &exception_ref, this);
    visitor.WalkStack(true);            //输入参数为true

    if (clear_exception_) {}
    else {
        self_->SetException(exception_ref.Get());
    }
    //如果异常处理的代码位于机器码中,则再补充设置一些信息。这部分内容和机器码编译有一些关系,建
    //议读者暂时不要理会
    if (*handler_quick_frame_ != nullptr &&
        handler_method_header_ != nullptr &&
        handler_method_header_->IsOptimized()) {
        SetCatchEnvironmentForOptimizedHandler(&visitor);
    }
}
class CatchBlockStackVisitor FINAL : public StackVisitor {
    ......
    bool VisitFrame() (Locks::mutator_lock_) {//main
  /*获取当前正在访问的方法。根据图10-14所示,我们依次会访问到Runtime ArtMethod、方法B、方
      法A。最后将进入虚拟机执行X1。 */
    ArtMethod* method = GetMethod();
    exception_handler_->SetHandlerFrameDepth(GetFrameDepth());
    //如果method为空,表示当前所访问的方法为虚拟机执行层
    if (method == nullptr) {
        /*①注意,如果if条件满足,则表明栈回溯到了虚拟机执行层,这时我们不能再继续回溯虚拟机执行层的函数了,只能先jump回(Quick-ExceptionHandler的主要功能就是long jump,即长跳转到目标指令位置)X1调用方法A的返回处。*/
        exception_handler_->SetHandlerQuickFramePc(GetCurrentQuickFramePc());//返回X1中方法A的返回地址
        exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
        exception_handler_->SetHandlerMethodHeader(
                        GetCurrentOatQuickMethodHeader());
        uint32_t next_dex_pc;
        ArtMethod* next_art_method;
        /*GetNextMethodAndDexPc函数内部也会做WalkStack进行栈回溯操作,找到紧挨着当前
          虚拟机执行层中的上一个方法。在图10-14中,X1往上没有调用者了,所以下面这个函数
          返回false。如果X1之上还有调用者,则返回那个函数的一些信息。  */
        bool has_next = GetNextMethodAndDexPc(&next_art_method, &next_dex_pc);
        exception_handler_->SetHandlerDexPc(next_dex_pc);
        exception_handler_->SetHandlerMethod(next_art_method);
        ......
        return false; // End stack walk.
    }
    //略过Runtime ArtMethod
    if (method->IsRuntimeMethod()) { return true; }
    /*从method中catch语句中找到是否有能处理该异常的地方。该函数和解释执行层中的FindNextInstruc-
      tionFollowingException函数类似,请读者自行阅读它。假设图10-14中的方法B、方法A均无法
      处理此异常,则HandleTryItems将返回true *///main
    return HandleTryItems(method);
}

image-20210212115122207

应用层异常处理

图解

graph TB
mJavaVM.DetachCurrentThread-->Thread::Destroy-->Thread::HandleUncaughtExceptions-->|Jni调用java层Thread的uncaughtExceptionHandler|Thead.uncaughtExceptionHandler-->TheadGroup-->ThreadStaticUncaughtExceptionHandler

AndroidRuntime

AndroidRuntime::start

void AndroidRuntime::start(const char* className,
                const Vector<String8>& options, bool zygote){
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    //启动虚拟机
    if (startVm(&mJavaVM, &env, zygote) != 0) { return; }
    ......
    char* slashClassName = toSlashClassName(className);
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {.....    }
    else {
        //假设startMeth是图10-14中的方法A
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {......}
        else {
            /*下面的CallStaciVoidMethod将触发图10-14的虚拟机执行X1,其内部调用方法
              A。根据上文所述,Thread QuickDeliverException的执行完后,longjmp将跳
              转到X1调用方法A返回后的指令处,就好像方法A执行完了一样(实际上没有)。
              所以,下面的CallStaticVoidMethod将返回。  main*/
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
        }
    }
    free(slashClassName);
    if (mJavaVM->DetachCurrentThread() != JNI_OK)//main
    ALOGW("Warning: unable to detach main thread\n");
    if (mJavaVM->DestroyJavaVM() != 0)
    ALOGW("Warning: VM did not shut down cleanly\n");

java_vm_ext.cc

DetachCurrentThread

static jint DetachCurrentThread(JavaVM* vm) {
        ......
        JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
        Runtime* runtime = raw_vm->GetRuntime();
        runtime->DetachCurrentThread();      //内部将调用当前线程的Destroy方法,main
        return JNI_OK;
}

Thread.cc

Thread::Destroy

void Thread::Destroy() {
    Thread* self = this;
    ......
    if (tlsPtr_.opeer != nullptr) {
    ScopedObjectAccess soa(self);
    //调用为该线程设置的UncaughtExceptionHandler对象。感兴趣的读者可自行研究该函数。main
    HandleUncaughtExceptions(soa);
    RemoveFromThreadGroup(soa);
    }
    ......
}

Thread::HandleUncaughtExceptions

void Thread::HandleUncaughtExceptions(ScopedObjectAccess& soa) {
  if (!IsExceptionPending()) {
    return;
  }
  ScopedLocalRef<jobject> peer(tlsPtr_.jni_env, soa.AddLocalReference<jobject>(tlsPtr_.opeer));
  ScopedThreadStateChange tsc(this, kNative);

  // Get and clear the exception.
  ScopedLocalRef<jthrowable> exception(tlsPtr_.jni_env, tlsPtr_.jni_env->ExceptionOccurred());
  tlsPtr_.jni_env->ExceptionClear();

  // If the thread has its own handler, use that.
  ScopedLocalRef<jobject> handler(tlsPtr_.jni_env,
                                  tlsPtr_.jni_env->GetObjectField(peer.get(),
                                      WellKnownClasses::java_lang_Thread_uncaughtHandler));
  if (handler.get() == nullptr) {
    // Otherwise use the thread group's default handler.
    handler.reset(tlsPtr_.jni_env->GetObjectField(peer.get(),
                                                  WellKnownClasses::java_lang_Thread_group));
  }

  // Call the handler.
  tlsPtr_.jni_env->CallVoidMethod(handler.get(),//main
      WellKnownClasses::java_lang_Thread__UncaughtExceptionHandler_uncaughtException,
      peer.get(), exception.get());

  // If the handler threw, clear that exception too.
  tlsPtr_.jni_env->ExceptionClear();
}

Jni.h / jni_internal.cc

ExceptionOccurred

/*
 * C++ object wrapper.
 *
 * This is usually overlaid on a C struct whose first element is a
 * JNINativeInterface*.  We rely somewhat on compiler behavior.
 */
struct _JNIEnv {
    jthrowable ExceptionOccurred()
    { return functions->ExceptionOccurred(this); }
  
static jthrowable ExceptionOccurred(JNIEnv* env) {
    ScopedObjectAccess soa(env);
    mirror::Object* exception = soa.Self()->GetException();
    return soa.AddLocalReference<jthrowable>(exception);
  }

Thread.cc

tlsPtr_.jni_env

{    
  ...
    // Every thread may have an associated JNI environment
    JNIEnvExt* jni_env;
} tlsPtr_;

GetException

mirror::Throwable* GetException() const SHARED_REQUIRES(Locks::mutator_lock_) {
    return tlsPtr_.exception;
}

IsExceptionPending

  bool IsExceptionPending() const {
    return tlsPtr_.exception != nullptr;
  }

Thread.java.uncaughtExceptionHandler

// null unless explicitly set
 private volatile UncaughtExceptionHandler uncaughtExceptionHandler;

ThreadGroup.java.uncaughtException

public void uncaughtException(Thread t, Throwable e) {
   if (parent != null) {
     parent.uncaughtException(t, e);
   } else {
     Thread.UncaughtExceptionHandler ueh =
       Thread.getDefaultUncaughtExceptionHandler();
     if (ueh != null) {
       ueh.uncaughtException(t, e);
     } else if (!(e instanceof ThreadDeath)) {
       System.err.print("Exception in thread \""
               \+ t.getName() + "\" ");
       e.printStackTrace(System.err);
     }
   }
 }

Thread.java.defaultUncaughtExceptionHandler

// null unless explicitly set
 private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;