GetStringCritical可以导致死锁吗?

问题描述 投票:0回答:1

system_server中发生了死锁,并且PackageManager保持锁定。

"PackageManager" prio=5 tid=27 WaitingForGcToComplete
  | group="main" sCount=1 dsCount=0 obj=0x12d3e7b0 self=0xb865c1a0
  | sysTid=579 nice=0 cgrp=default sched=0/0 handle=0xb865c788
  | state=S schedstat=( 82300981 68593861 146 ) utm=6 stm=2 core=1 HZ=100
  | stack=0xa0fdb000-0xa0fdd000 stackSize=1036KB
  | held mutexes=
  native: #00 pc 00012960  /system/lib/libc.so (syscall+28)
  native: #01 pc 000a88ad  /system/lib/libart.so(art::Mutex::ExclusiveLock(art::Thread*)+364)
  native: #02 pc 0013aa7b  /system/lib/libart.so (art::gc::Heap::IncrementDisableMovingGC(art::Thread*)+90)
  native: #03 pc 001c0329  /system/lib/libart.so (art::JNI::GetStringCritical(_JNIEnv*, _jstring*, unsigned char*)+392)
  native: #04 pc 00082223  /system/lib/libandroid_runtime.so (???)
  native: #05 pc 00082291  /system/lib/libandroid_runtime.so (???)
  native: #06 pc 00263595  /data/dalvik-cache/arm/system@[email protected] (Java_android_os_Parcel_nativeWriteString__JLjava_lang_String_2+120)
  at android.os.Parcel.nativeWriteString(Native method)
  at android.os.Parcel.writeString(Parcel.java:542)
  at android.content.ComponentName.writeToParcel(ComponentName.java:267)
  at android.content.ComponentName.writeToParcel(ComponentName.java:282)
  at android.content.Intent.writeToParcel(Intent.java:7486)
  at android.app.ApplicationThreadProxy.scheduleUnbindService(ApplicationThreadNative.java:929)
  at com.android.server.am.ActiveServices.removeConnectionLocked(ActiveServices.java:1842)
  at com.android.server.am.ActiveServices.unbindServiceLocked(ActiveServices.java:943)
  at com.android.server.am.ActivityManagerService.unbindService(ActivityManagerService.java:15787)
  - locked <0x1d3e13e9> (a com.android.server.am.ActivityManagerService)

所以我google它并找出GetStringCritical和ReleaseStringCritical之间是否还有其他一些JNI调用,如果GC被阻止,可能会发生死锁。查看更多细节The Java Native Interface: Programmer's Guide and Specification

这是frameworks \ base \ core \ jni \ android_os_Parcel.cpp中方法android_os_Parcel_writeString的一部分。

    const jchar* str = env->GetStringCritical(val, 0);
    if (str) {
        err = parcel->writeString16(str, env->GetStringLength(val));
        env->ReleaseStringCritical(val, str);
    }

我无法确定死锁是由android_os_Parcel_writeString引起的,因为android_os_Parcel_writeString是一种非常常用的方法。

所以我问的是android_os_Parcel_writeString可能导致死锁吗?

谢谢你的回答,忘了我笨拙的英语。

java android java-native-interface deadlock
1个回答
0
投票

必须非常谨慎地使用GetStringCritical。您必须确保在保持通过GetStringCritical获取的指针时,本机代码不会在JVM中分配新对象或执行可能导致系统死锁的其他阻塞调用。

    /* This is not safe! */
const char *c_str = (*env)->GetStringCritical(env, j_str, 0);
if (c_str == NULL) {
... /* error handling */
}
fprintf(fd, "%s\n", c_str);
(*env)->ReleaseStringCritical(env, j_str, c_str);

代码的问题在于,当前线程禁用垃圾收集时,写入文件句柄并不总是安全的。例如,另一个thread T正在等待从fd文件句柄读取。让我们进一步假设操作系统缓冲的设置方式使得fprintf调用等待,直到thread T完成从fd读取所有未决数据。我们已经为死锁构建了一个可能的场景:如果thread T无法分配足够的内存来作为从文件句柄读取的缓冲区,它必须请求垃圾回收。垃圾收集请求将被阻塞,直到当前线程执行ReleaseStringCritical,这在fprintf调用返回之前不会发生。然而,fprintf调用正在等待thread T完成从文件句柄读取

在看了https://android.googlesource.com/platform/frameworks/base/+/master/core/jni/android_os_Parcel.cpp后,方法writeString16无死锁。到目前为止,我从未发现Parcel有任何问题。

© www.soinside.com 2019 - 2024. All rights reserved.