android - 究竟是什么导致 Android 出现 'spin on suspend' 错误?

标签 android java-native-interface dalvik

我目前在调试一些依赖 native 库的 Android 代码时遇到问题。特别是一个 native 调用似乎容易出现这种“暂停时自旋”错误。一般表现为:

threadid=2: spin on suspend #2 threadid=48 (pcf=3)

到目前为止,我还不能确定到底是什么失败了,除了在大约 10 条这样的消息之后,我的应用程序遇到了 SIGSTKFLT 并退出。每次,第一个线程是 GC,第二个线程是当前正在执行 native 代码的任何线程。与此消息一起打印的堆栈部分始终在堆栈顶部有一个本地方法。

当 Dalvik 提示这个时到底发生了什么,我该如何开始调试原因以便修复它?

编辑:一个有趣的问题——在 native 开发人员进行了一些更改后,我现在有时也会看到以下错误:

PopFrame missed the break
VM aborting
Fatal signal 11 (SIGSEGV) at 0xdeadd00d (code=1)

线程转储在堆栈顶部显示我的 native 方法,但线程状态是RUNNABLE,而不是NATIVE,这对我来说也非常奇怪 - 如何这可能吗?

最佳答案

基本问题是 Dalvik 是一个安全点挂起 VM,并使用“停止世界”垃圾收集。这意味着,要让 GC 运行,它必须等待所有线程都达到可以确保它们不会更改堆的程度。

出于某种原因,您的线程之一没有响应 GC 线程的暂停请求。它实际上并不是在 native 代码中执行的;如果是,线程将在 NATIVE 中状态,这被认为是安全的。 (所有对 native 堆的访问都通过 JNI 调用进行控制,并且所有 JNI 调用都会进行挂起检查。)

出于性能原因,JIT 能够以跳过挂起检查的方式将已编译代码块链接在一起。如果一个线程挂起的时间太长,挂起的线程将“解开” block ,并等待更长的时间。最终它开始提示,最终它放弃并中止 VM。

有些设备使用 vendor-modified version of Dalvik这是错误的,并且中止可能发生在紧密的循环中。在这种情况下,我不希望在堆栈顶部看到本地方法。

最好的调试方法是在 gdb 出现问题时附加它并尝试弄清楚目标线程在做什么。 native 代码有可能以某种方式破坏了 VM 状态或返回堆栈,因此在从 native 代码返回时,线程会被阻塞。

编辑后更新: dvmPopFrame()函数用于从托管堆栈中弹出堆栈帧。当 VM 调用您的 native 方法时,它会插入一个“中断”帧,以便在展开堆栈以进行异常处理时,VM 不会越过调用站点。 (它也用于 VM 发出的托管代码方法调用,例如用于反射或 <clinit> 。)消息 PopFrame missed the break表示未找到中断帧。

断帧有一个空方法指针。展开堆栈时,dvmPopFrame()只要它看到一个非空的方法指针(意味着它不是中断帧)和一个非空的前一帧指针(意味着你还没有到达堆栈的顶部),它就会继续。如果您到达堆栈的顶部,您就错过了休息时间——所有 Dalvik 堆栈都以一个真正的方法开始(如果线程是使用 JNI 附加到 VM,有时是一个“假”方法)。

所以我的猜测是 native 代码破坏了堆栈,使前一帧指针无效。解决这个问题的一种技术是让 VM 调用调用实际 native 方法的 native 方法; “中间人”在堆栈上分配一些东西,将其设置为已知值,调用实际方法,然后在返回之前验证其堆栈分配是否未更改。

(可能有必要使用这些值以防止编译器优化它们;如果您使用类似的东西:

if (jniEnv == NULL) {
    printf("my stuff is ...", ...);
}

那么它将永远不会真正运行,因为 JNIEnv*永远不会为空...但编译器不知道这一点。)

有关 Dalvik 堆栈布局的完整描述,请参阅 dalvik/vm/interp/Stack.h .

线程在RUNNABLE是正常的从 native 代码返回时。您的 native 方法仍然位于顶部,因为弹出它的代码失败并中止了 VM。

关于android - 究竟是什么导致 Android 出现 'spin on suspend' 错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20790471/

相关文章:

java - 从 jobject 数组 JNI 获取 Jclass 或 jobject

java - 如何从微调器的项目中调用 Activity

android - MPAndroidChart:如何隐藏图表背景?

android - 以编程方式在文件中获取应用程序 sqlite 数据库

Android GCM 未授权 401 错误与 PHP

java - jni 使用 NewDirectByteBuffer 在 java 中看到随机的东西

java - JNI : What signature to use when returning a user defined java class?

android - 为什么 Android 需要虚拟机 (DVM)?

将 TabSpec 添加到 TabHost 时 Android 应用程序崩溃 - "Resource ID not valid"

javascript - 在 Javascript 上使用 Rhino 的 evaluateString() 的 Android StackOverflow 错误