You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ART卸载Native库时死锁的原因及调试方法咨询

问题背景

我正在调试应用中一个用户无感知的后台崩溃问题,该崩溃源于Android Runtime(ART)的死锁/线程竞争,相关回溯信息如下:

backtrace:
  #00  pc 0x000000000004f560  /apex/com.android.runtime/lib64/bionic/libc.so (syscall+32)
  #01  pc 0x000000000030d9a8  /apex/com.android.art/lib64/libart.so (art::Mutex::ExclusiveLock(art::Thread*)+280)
  #02  pc 0x00000000005fe8b4  /apex/com.android.art/lib64/libart.so (art::Libraries::UnloadNativeLibraries()+96)
  #03  pc 0x00000000003ba968  /apex/com.android.art/lib64/libart.so (art::gc::Heap::CollectGarbageInternal(art::gc::collector::GcType, art::gc::GcCause, bool, unsigned int)+876)
  #04  pc 0x00000000003ba50c  /apex/com.android.art/lib64/libart.so (art::gc::Heap::ConcurrentGC(art::Thread*, art::gc::GcCause, bool, unsigned int)+164)

Caused DefaultDispatcher-worker-9 failure : SuspendThreadByPeer timed out: 14060 (HeapTaskDaemon), state&flags: 0x9, priority: 5, barriers: 0x<sanitized>, ours: 0x<sanitized>, barrier value: 1, nsusps: 0, ncheckpts: 0, thread_info: Target states: [14060 (HeapTaskDaemon) S 1097 1097 0 0 -1 1077936192 231570 372 345 3 170 481 0 1 24 4 74, 14060 (HeapTaskDaemon) S 1097 1097 0 0 -1 1077936192 390055 372 345 3 219 832 0 1 24 4 74]1@517142581380 Final wait time: 4.010s

我查看了UnloadNativeLibraries的源码,了解到加载Native共享库时会获取jni_libraries_lock_锁,但仍无法理解死锁发生的机制。我的应用会在运行时加载自定义Native库,不确定是否是该库导致问题,想请教:

  1. Native库可能存在哪些行为会导致ART无法获取锁?
  2. 还有哪些调试手段可以排查该问题?
Native库导致ART锁获取失败的常见行为
  • 持有Native锁时触发JNI调用或GC:如果Native库在持有自定义互斥锁(比如pthread_mutex)的情况下,调用JNI方法或触发GC(比如分配大对象、显式调用System.gc()),ART的UnloadNativeLibraries在GC流程中需要获取jni_libraries_lock_,此时两种锁形成交叉持有,会触发死锁。
  • 在JNI_OnLoad/JNI_OnUnload中执行阻塞操作:这两个函数是ART加载/卸载Native库的回调,如果库在这两个函数里做长时间阻塞(比如等待外部资源、睡眠、死循环),会导致ART无法完成库的加载/卸载流程,进而在GC时等待锁超时。
  • Native线程直接操作ART内部结构:如果Native库创建的线程未经过ART注册,就直接调用ART内部API(比如手动操作JNIEnv、访问ART锁结构),可能破坏ART的锁状态,导致jni_libraries_lock_无法正常获取。
  • 卸载时Native库仍有活跃线程:如果Native库被标记为可卸载,但库内还有正在运行的Native线程,ART尝试获取jni_libraries_lock_执行卸载时,会被这些活跃线程持有的资源阻塞,最终导致超时。
排查该问题的调试手段
  • 启用ART锁日志:设置系统属性debug.art.lockprof.threshold=0,ART会记录所有锁的获取/释放操作,日志包含锁的持有者、等待者信息,可直接定位死锁的交叉持有关系。
  • 使用gdb/lldb附加进程分析:崩溃发生时,附加进程后执行thread apply all bt查看所有线程的调用栈,找到持有jni_libraries_lock_的线程,以及该线程当前阻塞的原因。
  • 监控Native库加载/卸载与GC时机:在应用中添加日志,记录自定义Native库的System.loadLibrary调用时机,以及ART触发GC的时机(可通过监听android.os.Debug.MemoryInfo或设置GC日志),对比时间线看是否有重叠导致锁竞争。
  • 简化Native库代码排查:逐步注释Native库中非核心逻辑,尤其是涉及锁、JNI调用、线程创建的部分,验证问题是否消失,从而定位具体代码块。
  • 检查JNI_OnLoad/JNI_OnUnload实现:确保这两个回调函数没有阻塞操作,所有资源能正确释放,不存在死循环或无限等待逻辑。

内容的提问来源于stack exchange,提问作者peakpeakt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 23:44:52