究竟是什么原因导致了Android的“暂停”错误?

我目前无法debugging一些依赖本机库的Android代码。 特别是一个本地调用似乎倾向于这种“旋转中止”错误。 它通常performance如下:

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

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

当Dalvik抱怨这个事情到底发生了什么,我该如何开始debugging原因,以便我能解决这个问题?

编辑 :一个有趣的皱纹 – 原生开发者做了一些改变后,我现在也有时会看到下面的错误:

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

线程转储在堆栈顶部显示我的本地方法也是非常奇怪,但是线程状态是RUNNABLE ,不是NATIVE – 这怎么可能?

Solutions Collecting From Web of "究竟是什么原因导致了Android的“暂停”错误?"

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

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

出于性能原因,JIT能够以一种跳过暂停检查的方式链接已编译代码块。 如果一个线程挂起的时间太长,挂起的线程将“解除链接”,等待一段时间。 最终它开始抱怨,最终终于放弃并放弃虚拟机。

有些设备使用Dalvik的供应商修改版本,导致这个错误,并可能在紧密循环中发生中止。 在这种情况下,我不希望在堆栈顶部看到一个本地方法。

debugging的最好方法就是在gdb的registry达不愉快的地方附加gdb,并试图找出目标线程正在做什么。 本地代码可能以某种方式抛出虚拟机状态或返回堆栈,如果从本地代码返回,则线程被阻塞。

编辑后更新: dvmPopFrame()函数用于从堆栈popup堆栈帧。 当VM调用你的本地方法时,它会插入一个“break”帧,这样当堆栈被展开以进行exception处理时,VM不会通过呼叫站点。 (也用于虚拟机发出的托pipe代码方法调用,例如用于reflection或<clinit> 。) PopFrame missed the break意味着没有find中断帧。

中断帧有一个空方法指针。 展开堆栈时, 只要dvmPopFrame()看到一个非空的方法指针(意思是它不是一个断开帧)和一个非空的前一帧指针(意味着你没有碰到堆栈的顶部) 。 如果你点击堆栈顶部,你已经错过了破解 – 所有Dalvik堆栈都从一个真正的方法开始(如果线程使用JNI连接到VM,有时候是一个“假”方法)。

所以我的猜测是本机代码会抛弃堆栈,将前一帧指针置零。 一种技术可以让VM调用本地方法来调用实际的本地方法; “中间人”在堆栈上分配一些东西,将其设置为已知值,调用实际方法,然后validation其堆栈分配在返回之前是否保持不变。

(可能有必要使用这些值来防止编译器优化它们;如果使用如下所示的内容:

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

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

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

从本地代码返回时,线程处于RUNNABLE状态是正常的。 您的本地方法仍然处于顶端,因为popup它的代码失败并中止虚拟机。