亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android性能優(yōu)化死鎖監(jiān)控知識(shí)點(diǎn)詳解

 更新時(shí)間:2022年10月31日 15:17:08   作者:Pika  
這篇文章主要為大家介紹了Android性能優(yōu)化死鎖監(jiān)控知識(shí)點(diǎn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

“死鎖”,這個(gè)從接觸程序開(kāi)發(fā)的時(shí)候就會(huì)經(jīng)常聽(tīng)到的詞,它其實(shí)也可以被稱(chēng)為一種“藝術(shù)”,即互斥資源訪問(wèn)循環(huán)的藝術(shù),在Android中,如果主線程產(chǎn)生死鎖,那么通常會(huì)以ANR結(jié)束app的生命周期,如果是兩個(gè)子線程的死鎖,那么就會(huì)白白浪費(fèi)cpu的調(diào)度資源,同時(shí)也不那么容易被發(fā)現(xiàn),就像一顆“腫瘤”,永遠(yuǎn)藏在app中。當(dāng)然,本篇介紹的是業(yè)內(nèi)常見(jiàn)的死鎖監(jiān)控手段,同時(shí)也希望通過(guò)死鎖,去挖掘更加底層的知識(shí),同時(shí)讓我們更加了解一些常用的監(jiān)控手段。

我們很容易模擬一個(gè)死鎖操作,比如

val lock1 = Object()
val lock2 = Object()
Thread ({
    synchronized(lock1){
        Thread.sleep(2000)
        synchronized(lock2){
        }
    }
},"thread222").start()
Thread ({
    synchronized(lock2) {
        Thread.sleep(1000)
        synchronized(lock1) {
        }
    }
},"thread111").start()

因?yàn)閠hread111跟thread222都同時(shí)持有著對(duì)方想要的臨界資源(互斥資源),因此這兩個(gè)線程都處在互相等待對(duì)方的狀態(tài)。

死鎖檢測(cè)

我們?cè)趺磁袛嗨梨i:是否存在一個(gè)線程所持有的鎖被另一個(gè)線程所持有,同時(shí)另一個(gè)線程也持有該線程所需要的鎖,因此我們需要知道以下信息才能進(jìn)行死鎖分析:

  • 線程所要獲取的鎖是什么
  • 該鎖被什么線程所持有
  • 是否產(chǎn)生循環(huán)依賴(lài)的限制(本篇就不涉及了,因?yàn)槲覀冎懒饲皟蓚€(gè)就可以自行分析了)

線程Block狀態(tài)

通過(guò)我們對(duì)synchronized的了解,當(dāng)線程多次獲取不到鎖的時(shí)候,此時(shí)線程就會(huì)進(jìn)入悲觀鎖狀態(tài),因此線程就會(huì)嘗試進(jìn)入阻塞狀態(tài),避免進(jìn)一步的cpu資源消耗,因此此時(shí)兩個(gè)線程都會(huì)處于block 阻塞的狀態(tài),我們就能知道,處于被block狀態(tài)的線程就有可能產(chǎn)生死鎖(只是有可能),我們可以通過(guò)遍歷所有線程,查看是否處于block狀態(tài),來(lái)進(jìn)行死鎖判斷的第一步

val threads = getAllThread()
threads.forEach {
    if(it?.isAlive == true && it.state == Thread.State.BLOCKED){
       進(jìn)入死鎖判斷
    }
}

獲取所有線程

private fun getAllThread():Array<Thread?>{
    val threadGroup = Thread.currentThread().threadGroup;
    val total = Thread.activeCount()
    val array = arrayOfNulls<Thread>(total)
    threadGroup?.enumerate(array)
    return array
}

通過(guò)對(duì)線程的判斷,我們能夠排除大部分非死鎖的線程,那么下一步我們要怎么做呢?如果線程發(fā)生了死鎖,那么一定擁有一個(gè)已經(jīng)持有的互斥資源并且不釋放才有可能造成死鎖對(duì)不對(duì)!那么我們下一步,就是要檢測(cè)當(dāng)前線程所持有的鎖,如果兩個(gè)線程同時(shí)持有對(duì)方所需要的鎖,那么就會(huì)產(chǎn)生死鎖

獲取當(dāng)前線程所請(qǐng)求的鎖

雖然我們?cè)趈ava層沒(méi)有相關(guān)的api提供給我們獲取線程當(dāng)前想要請(qǐng)求的鎖,但是在我們的native層,卻可以輕松做到,因?yàn)樗赼rt中得到更多的支持。

ObjPtr<mirror::Object> Monitor::GetContendedMonitor(Thread* thread) {
    // This is used to implement JDWP's ThreadReference.CurrentContendedMonitor, and has a bizarre
    // definition of contended that includes a monitor a thread is trying to enter...
    ObjPtr<mirror::Object> result = thread->GetMonitorEnterObject();
    if (result == nullptr) {
        // ...but also a monitor that the thread is waiting on.
        MutexLock mu(Thread::Current(), *thread->GetWaitMutex());
        Monitor* monitor = thread->GetWaitMonitor();
        if (monitor != nullptr) {
            result = monitor->GetObject();
        }
    }
    return result;
}

其中第一步嘗試著通過(guò)thread->GetMonitorEnterObject()去拿

mirror::Object* GetMonitorEnterObject() const REQUIRES_SHARED(Locks::mutator_lock_) {
        return tlsPtr_.monitor_enter_object;
}

其中tlsPtr_ 其實(shí)就是art虛擬機(jī)中對(duì)于線程ThreadLocal的代表,即代表著只屬于線程的本地對(duì)象,會(huì)先嘗試從這里拿,拿不到的話(huà)通過(guò)Thread類(lèi)中的wait_mutex_對(duì)象去拿

Mutex* GetWaitMutex() const LOCK_RETURNED(wait_mutex_) {
        return wait_mutex_;
}

GetContendedMonitor 提供了一個(gè)方法查詢(xún)當(dāng)前線程想要的鎖對(duì)象,這個(gè)鎖對(duì)象以O(shè)bjPtrmirror::Object對(duì)象表示,其中mirror::Object類(lèi)型是art中相對(duì)應(yīng)于java層的Object類(lèi)的代表,我們了解一下即可??吹竭@里我們可能還有一個(gè)疑問(wèn),這個(gè)Thread* thread的入?yún)⑹鞘裁茨??(其?shí)是nativePeer,下文我們會(huì)了解)

我們有辦法能夠查詢(xún)到線程當(dāng)前請(qǐng)求的鎖,那么這個(gè)鎖被誰(shuí)持有呢?只有解決這兩個(gè)問(wèn)題,我們才能進(jìn)行死鎖的判斷對(duì)不對(duì),我們繼續(xù)往下

通過(guò)鎖獲取當(dāng)前持有的線程

我們還記得上文中返回的鎖對(duì)象是以O(shè)bjPtrmirror::Object表示的,當(dāng)然,art中同樣提供了方法,讓我們通過(guò)這個(gè)鎖對(duì)象去查詢(xún)當(dāng)前是哪個(gè)線程持有

uint32_t Monitor::GetLockOwnerThreadId(ObjPtr<mirror::Object> obj) {
    DCHECK(obj != nullptr);
    LockWord lock_word = obj->GetLockWord(true);
    switch (lock_word.GetState()) {
        case LockWord::kHashCode:
            // Fall-through.
        case LockWord::kUnlocked:
            return ThreadList::kInvalidThreadId;
        case LockWord::kThinLocked:
            return lock_word.ThinLockOwner();
        case LockWord::kFatLocked: {
            Monitor* mon = lock_word.FatLockMonitor();
            return mon->GetOwnerThreadId();
        }
        default: {
            LOG(FATAL) << "Unreachable";
            UNREACHABLE();
        }
    }
}

這里函數(shù)比較簡(jiǎn)單,如果當(dāng)前調(diào)用正常,那么執(zhí)行的就是LockWord::kFatLocked,返回的是native層的Thread的tid,最終是以u(píng)int32_t類(lèi)型表示

注意這里GetLockOwnerThreadId中返回的Thread id千萬(wàn)不要跟Java層的Thread對(duì)象的tid混淆,這里的tid才是真正的線程id標(biāo)識(shí)

線程啟動(dòng)

我們來(lái)看一下native層主線程的啟動(dòng),它隨著art虛擬機(jī)的啟動(dòng)隨即啟動(dòng),我們都知道java層的線程其實(shí)在沒(méi)有跟操作系統(tǒng)的線程綁定的時(shí)候,它只能算是一塊內(nèi)存!只要經(jīng)過(guò)與native線程綁定后,這時(shí)的Thread才能真正具備線程調(diào)度的能力,下面我們以主線程啟動(dòng)舉例子:

thread.cc
void Thread::FinishStartup() {
Runtime* runtime = Runtime::Current();
CHECK(runtime->IsStarted());
// Finish attaching the main thread.
ScopedObjectAccess soa(Thread::Current());
// 這里是關(guān)鍵,為什么主線程稱(chēng)為“main線程”的原因
soa.Self()->CreatePeer("main", false, runtime->GetMainThreadGroup());
soa.Self()->AssertNoPendingException();
runtime->RunRootClinits(soa.Self());
soa.Self()->NotifyThreadGroup(soa, runtime->GetMainThreadGroup());
soa.Self()->AssertNoPendingException();
}

可以看到,為什么主線程被稱(chēng)為“主線程”,是因?yàn)樵赼rt虛擬機(jī)啟動(dòng)的時(shí)候,通過(guò)CreatePeer函數(shù),創(chuàng)建的名稱(chēng)是“main”,CreatePeer是native線程中非常重要的存在,所有線程創(chuàng)建都經(jīng)過(guò)它,這個(gè)函數(shù)有點(diǎn)長(zhǎng),筆者這里做了刪減

void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group) {
    Runtime* runtime = Runtime::Current();
    CHECK(runtime->IsStarted());
    JNIEnv* env = tlsPtr_.jni_env;
    if (thread_group == nullptr) {
        thread_group = runtime->GetMainThreadGroup();
    }
    // 設(shè)置了線程名字
    ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF(name));
    // Add missing null check in case of OOM b/18297817
    if (name != nullptr && thread_name.get() == nullptr) {
        CHECK(IsExceptionPending());
        return;
    }
    // 設(shè)置Thread的各種屬性
    jint thread_priority = GetNativePriority();
    jboolean thread_is_daemon = as_daemon;
    // 創(chuàng)建了一個(gè)java層的Thread對(duì)象,名字叫做peer
    ScopedLocalRef<jobject> peer(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
    if (peer.get() == nullptr) {
        CHECK(IsExceptionPending());
        return;
    }
    {
        ScopedObjectAccess soa(this);
        tlsPtr_.opeer = soa.Decode<mirror::Object>(peer.get()).Ptr();
    }
    env->CallNonvirtualVoidMethod(peer.get(),
                                  WellKnownClasses::java_lang_Thread,
                                  WellKnownClasses::java_lang_Thread_init,
                                  thread_group, thread_name.get(), thread_priority, thread_is_daemon);
    if (IsExceptionPending()) {
        return;
    }
    // 看到這里,非常關(guān)鍵,self 指向了當(dāng)前native Thread對(duì)象 self->Thread
    Thread* self = this;
    DCHECK_EQ(self, Thread::Current());
    env->SetLongField(peer.get(),
                      WellKnownClasses::java_lang_Thread_nativePeer,
                      reinterpret_cast64<jlong>(self));
    ScopedObjectAccess soa(self);
    StackHandleScope<1> hs(self);
   ....
}

這里其實(shí)就是一次jni調(diào)用,把java中的Thread 的nativePeer 進(jìn)行了賦值,而賦值的內(nèi)容,正是通過(guò)了這個(gè)調(diào)用SetLongField

env->SetLongField(peer.get(),
                      WellKnownClasses::java_lang_Thread_nativePeer,
                      reinterpret_cast64<jlong>(self));

這里我們簡(jiǎn)單了解一下SetLongField,如果進(jìn)行過(guò)jni開(kāi)發(fā)的同學(xué)應(yīng)該能過(guò)明白,其實(shí)就是把peer.get()得到的對(duì)象(其實(shí)就是java層的Thread對(duì)象)的nativePeer屬性,賦值為了self(native層的Thread對(duì)象的指針),并強(qiáng)轉(zhuǎn)換為了jlong類(lèi)型。我們接下來(lái)回到j(luò)ava層

Thread.java
private volatile long nativePeer;

說(shuō)了一大堆,那么這個(gè)nativePeer究竟是個(gè)什么?通過(guò)上面的代碼分析,我們能夠明白了,Thread.java中的nativePeer就是一個(gè)指針,它所指向的內(nèi)容正是native層中的Thread

nativePeer 與 native Thread tid 與java Thread tid

經(jīng)過(guò)了上面一段落,我們了解了nativePeer,那么我們繼續(xù)對(duì)比一下java層Thread tid 與native層Thread tid。我們通過(guò)在kotlin/java中,調(diào)用Thread對(duì)象的id屬性,其實(shí)得到的是這個(gè)

private long tid;

它的生成方法如下

/* Set thread ID */
tid = nextThreadID();
private static synchronized long nextThreadID() {
    return ++threadSeqNumber;
}

可以看到,雖然它的確能代表一個(gè)java層中Thread的標(biāo)識(shí),但是生成其實(shí)可以看到,他也僅僅是一個(gè)普通的累積id生成,同時(shí)也并沒(méi)有在native層中被當(dāng)作唯一標(biāo)識(shí)進(jìn)行使用。

而native Thread 的 tid屬性,才是真正的線程id

在art中,通過(guò)GetTid獲取

pid_t GetTid() const {
    return tls32_.tid;
}

同時(shí)我們也可以注意到,tid 是保存在 tls32_結(jié)構(gòu)體中,并且其位于Thread對(duì)象的開(kāi)頭,從內(nèi)存分布上看,tid位于state_and_flags、suspend_count、think_lock_thread_id之后,還記得我們上面說(shuō)過(guò)的nativePeer嘛?我們一直強(qiáng)調(diào)native是Thread的指針對(duì)象

因此我們可以通過(guò)指針的偏移,從而算出nativePeer到tid的換算公式,即nativePeer指針向下偏移三位就找到了tid(因?yàn)閟tate_and_flags,state_and_flags,think_lock_thread_id都是int類(lèi)型,那么對(duì)應(yīng)的指針也就是int * )這里有點(diǎn)繞,因?yàn)樯婕爸羔樀膬?nèi)容

int *pInt = reinterpret_cast<int *>(native_peer);
//地址 +3,得到tid
pInt = pInt + 3;
return *pInt;

nativePeer對(duì)象因?yàn)榫驮趈ava層,我們很容易通過(guò)反射就能拿到

val nativePeer = Thread::class.java.getDeclaredField("nativePeer")
nativePeer.isAccessible = true
val currentNativePeer = nativePeer.get(it)

這里我們通過(guò)nativePeer換算成tid可以寫(xiě)成一個(gè)jni方法

external fun nativePeer2Threadid(nativePeer:Long):Int

實(shí)現(xiàn)就是

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_signal_MainActivity_nativePeer2Threadid(JNIEnv *env, jobject thiz,
                                                         jlong native_peer) {
    if (native_peer != 0) {
            //long 強(qiáng)轉(zhuǎn) int
            int *pInt = reinterpret_cast<int *>(native_peer);
            //地址 +3,得到 native id
            pInt = pInt + 3;
            return *pInt;
        }
    }
}

dlsym與調(diào)用

我們上面終于把死鎖能涉及到的點(diǎn)都講完,比如如何獲取線程所請(qǐng)求的鎖,當(dāng)前鎖又被那個(gè)線程持有,如何通過(guò)nativePeer獲取Thread id 做了分析,但是還有一個(gè)點(diǎn)我們還沒(méi)能解決,就是如何調(diào)用這些函數(shù)。我們需要調(diào)用的是GetContendedMonitor,GetLockOwnerThreadId,這個(gè)時(shí)候dlsym系統(tǒng)調(diào)用就出來(lái)了,我們可以通過(guò)dlsym 進(jìn)行調(diào)用我們想要調(diào)用的函數(shù)

void* dlsym(void* __handle, const char* __symbol);

這里的symbol是什么呢?其實(shí)我們所有的elf(so也是一種elf文件)的所有調(diào)用函數(shù)都會(huì)生成一個(gè)符號(hào),代表著這個(gè)函數(shù),它在elf的.text中。而我們android中,就會(huì)通過(guò)加載so的方式加載系統(tǒng)庫(kù),加載的系統(tǒng)庫(kù)libart.so里面就包含著我們想要調(diào)用的函數(shù)GetContendedMonitor,GetLockOwnerThreadId的符號(hào)

我們可以通過(guò)objdump -t libart.so 查看符號(hào)

這里我們直接給出來(lái)各個(gè)符號(hào),讀者可以直接用objdump查看符號(hào)

GetContendedMonitor 對(duì)應(yīng)的符號(hào)是

_ZN3art7Monitor19GetContendedMonitorEPNS_6ThreadE

GetLockOwnerThreadId 對(duì)應(yīng)的符號(hào)

sdk <= 29
_ZN3art7Monitor20GetLockOwnerThreadIdEPNS_6mirror6ObjectE
>29是這個(gè)
_ZN3art7Monitor20GetLockOwnerThreadIdENS_6ObjPtrINS_6mirror6ObjectEEE

系統(tǒng)限制

然后到這里,我們還是沒(méi)能完成調(diào)用,因?yàn)閐lsym等dl系列的系統(tǒng)調(diào)用,因?yàn)閺腁ndroid 7.0開(kāi)始,Android系統(tǒng)開(kāi)始阻止App中直接使用dlopen(), dlsym()等函數(shù)打開(kāi)系統(tǒng)動(dòng)態(tài)庫(kù),好家伙!谷歌大兄弟為了安全的考慮,做了很多限制。但是這個(gè)防君子不防程序員,業(yè)內(nèi)依舊有很多繞過(guò)系統(tǒng)的限制的方法,我們看一下dlsym

__attribute__((__weak__))
void* dlsym(void* handle, const char* symbol) {
    const void* caller_addr = __builtin_return_address(0);
    return __loader_dlsym(handle, symbol, caller_addr);
}

__builtin_return_address是Linux一個(gè)內(nèi)建函數(shù)(通常由編譯器添加),__builtin_return_address(0)用于返回當(dāng)前函數(shù)的返回地址。

在__loader_dlsym 會(huì)進(jìn)行返回地址的校驗(yàn),如果此時(shí)返回地址不是屬于系統(tǒng)庫(kù)的地址,那么調(diào)用就不成功,這也是art虛擬機(jī)保護(hù)手段,因此我們很容易就得出一個(gè)想法,我們是不是可以用系統(tǒng)的某個(gè)函數(shù)去調(diào)用dlsym,然后把結(jié)果給到我們自己的函數(shù)消費(fèi)就可以了?是的,業(yè)內(nèi)已經(jīng)有很多這個(gè)方案了,比如ndk_dlopen

我們拿arm架構(gòu)進(jìn)行分析,arm架構(gòu)中LR寄存器就是保存了當(dāng)前函數(shù)的返回地址,那么我們是不是在調(diào)用dlsym時(shí)可以通過(guò)匯編代碼直接修改LR寄存器的地址為某個(gè)系統(tǒng)庫(kù)的函數(shù)地址就可以了?嗯!是的,但是我們還需要把原來(lái)的LR地址給保存起來(lái),不然就沒(méi)辦法還原原來(lái)的調(diào)用了。

這里我們拿ndk_dlopen的實(shí)現(xiàn)舉例子

if (SDK_INT <= 0) {
    char sdk[PROP_VALUE_MAX];
    __system_property_get("ro.build.version.sdk", sdk);
    SDK_INT = atoi(sdk);
    LOGI("SDK_INT = %d", SDK_INT);
    if (SDK_INT >= 24) {
        static __attribute__((__aligned__(PAGE_SIZE))) uint8_t __insns[PAGE_SIZE];
        STUBS.generic_stub = __insns;
        mprotect(__insns, sizeof(__insns), PROT_READ | PROT_WRITE | PROT_EXEC);
        // we are currently hijacking "FatalError" as a fake system-call trampoline
        uintptr_t pv = (uintptr_t)(*env)->FatalError;
        uintptr_t pu = (pv | (PAGE_SIZE - 1)) + 1u;
        uintptr_t pd = (pv & ~(PAGE_SIZE - 1));
        mprotect((void *)pd, pv + 8u >= pu ? PAGE_SIZE * 2u : PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
        quick_on_stack_back = (void *)pv;
  // arm架構(gòu)匯編實(shí)現(xiàn)
#elif defined(__arm__)
            // r0~r3
            /*
             0x0000000000000000:     08 E0 2D E5     str lr, [sp, #-8]!
             0x0000000000000004:     02 E0 A0 E1     mov lr, r2
             0x0000000000000008:     13 FF 2F E1     bx r3
            */
            memcpy(__insns, "\x08\xE0\x2D\xE5\x02\xE0\xA0\xE1\x13\xFF\x2F\xE1", 12);
            if ((pv & 1u) != 0u) { // Thumb
                /*
                 0x0000000000000000:     0C BC   pop {r2, r3}
                 0x0000000000000002:     10 47   bx r2
                */
                memcpy((void *)(pv - 1), "\x0C\xBC\x10\x47", 4);
            } else {
                /*
                 0x0000000000000000:     0C 00 BD E8     pop {r2, r3}
                 0x0000000000000004:     12 FF 2F E1     bx r2
                */
                memcpy(quick_on_stack_back, "\x0C\x00\xBD\xE8\x12\xFF\x2F\xE1", 8);
            } //if

其中我們拿(*env)->FatalError作為了混淆系統(tǒng)調(diào)用的stub,我們參照著流程圖去理解上述代碼:

  • 02 E0 A0 E1 mov lr, r2 把r2寄存器的內(nèi)容放到了lr寄存器,這個(gè)r2存的東西就是FatalError的地址
  • 0x0000000000000008: 13 FF 2F E1 bx r3 ,通過(guò)bx指令調(diào)轉(zhuǎn),就可以正常執(zhí)行我們的dlsym了,r3就是我們自己的dlsym的地址
  • 0x0000000000000000: 0C 00 BD E8 pop {r2, r3} 調(diào)用完r3寄存器的方法把r2寄存器放到調(diào)用棧下,提供給后面的執(zhí)行進(jìn)行消費(fèi)
  • 0x0000000000000004: 12 FF 2F E1 bx r2 ,最后就回到了我們的r2,完成了一次調(diào)用

總之,我們想要做到dl系列的調(diào)用,就是想盡方法去修改對(duì)應(yīng)架構(gòu)的函數(shù)返回地址的數(shù)值。

死鎖檢測(cè)所有代碼

const char *get_lock_owner_symbol_name() {
    if (SDK_INT <= 29) {
        return "_ZN3art7Monitor20GetLockOwnerThreadIdEPNS_6mirror6ObjectE";
    } else {
        return "_ZN3art7Monitor20GetLockOwnerThreadIdENS_6ObjPtrINS_6mirror6ObjectEEE";
    }
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_signal_MyHandler_deadLockMonitor(JNIEnv *env, jobject thiz,
                                                  jlong native_thread) {
    //1、初始化
    ndk_init(env);
    //2、打開(kāi)動(dòng)態(tài)庫(kù)libart.so
    void *so_addr = ndk_dlopen("libart.so", RTLD_NOLOAD);
    void * get_contended_monitor = ndk_dlsym(so_addr, "_ZN3art7Monitor19GetContendedMonitorEPNS_6ThreadE");
    void * get_lock_owner_thread = ndk_dlsym(so_addr, get_lock_owner_symbol_name());
    int monitor_thread_id = 0;
    if (get_contended_monitor != nullptr && get_lock_owner_thread != nullptr) {
        //1、調(diào)用一下獲取monitor的函數(shù),返回當(dāng)前線程想要競(jìng)爭(zhēng)的monitor
        int monitorObj = ((int (*)(long)) get_contended_monitor)(native_thread);
        if (monitorObj != 0) {
            // 2、獲取這個(gè)monitor被哪個(gè)線程持有,返回該線程id
            monitor_thread_id = ((int (*)(int)) get_lock_owner_thread)(monitorObj);
        } else {
            monitor_thread_id = 0;
        }
    }
    return monitor_thread_id;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_signal_MainActivity_nativePeer2Threadid(JNIEnv *env, jobject thiz,
                                                         jlong native_peer) {
    if (native_peer != 0) {
        if (SDK_INT > 20) {
            //long 強(qiáng)轉(zhuǎn) int
            int *pInt = reinterpret_cast<int *>(native_peer);
            //地址 +3,得到 native id
            pInt = pInt + 3;
            return *pInt;
        }
    }
}
extern "C" jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    char sdk[PROP_VALUE_MAX];
    __system_property_get("ro.build.version.sdk", sdk);
    SDK_INT = atoi(sdk);
    return JNI_VERSION_1_4;
}

對(duì)應(yīng)java層

external fun deadLockMonitor(nativeThread:Long):Int
private fun getAllThread():Array<Thread?>{
    val threadGroup = Thread.currentThread().threadGroup;
    val total = Thread.activeCount()
    val array = arrayOfNulls<Thread>(total)
    threadGroup?.enumerate(array)
    return array
}
external fun nativePeer2Threadid(nativePeer:Long):Int

總結(jié)

我們通過(guò)死鎖這個(gè)例子,去了解了native層Thread的相關(guān)方法,同時(shí)也了解了如何使用dlsym打開(kāi)函數(shù)符號(hào)并調(diào)用。本篇Android性能優(yōu)化就到此結(jié)束,更多關(guān)于Android性能優(yōu)化死鎖監(jiān)控的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論