理解Android Tight loops / Spin-On-Suspend错误

5
我正在开发一款名为“Space RPG”的Android游戏 - 目前大多数Galaxy S4和HTC One手机上出现了这个错误。这是所有Java的问题。
游戏会卡住,当我尝试调试进程并暂停相关线程时,它不会暂停,并且会出现一个“悬而未决的”(spin-on-suspend)错误。线程转储让我看到它在一个while循环中,该循环以越来越大的距离步长迭代到所需的“结束位置”,以找到“起始位置”。
这就是事情变得烦人的地方。我可以验证,尽管条件是while(true),但循环不能无限期运行,它不可能运行超过200次,然后我的break语句被调用(代码在我尝试的每部其他电话上都可以工作,这是支持此断言的背景)。
为了帮助缓解我对此事情的担忧,我在循环中添加了一个简单的递增变量,如果它超过1000,它将记录一些内容,以便我可以看到它确实运行了太多次,以防某些变量设置错误或其他原因。 当计数器代码存在时,就不会出现任何崩溃/挂起。也没有看到任何日志表明它运行了超过1000次。 如果我删除此计数器,则每次播放5-10秒后都会出现挂起(在此期间while循环可能已运行10次,尽管这有所不同)。
因此,我的问题是,到底是怎么回事?为什么这些新手机(但似乎没有任何旧手机)在没有递增变量的循环中有问题,而且工作有效并且不持续太长时间?线程如何在该循环中停滞不前,添加额外的计数器变量如何修复问题?
这项工作是在opengl渲染线程上完成的,如果这很重要。
据我所知,大多数S4都会出现这种情况,但至少有一个S4没有出现。我今天使用的那个正在发生。这让我想知道它是否可能与特定的Android、Java、Dalvik或手机上的其他东西有关,但不幸的是,我没有从S4中获得任何详细信息(即不会发生错误的那个)。
如果您能提供任何帮助、指导、思路或进一步阅读等方面,将不胜感激,非常感谢。
float vel = 1.0f; // final velocity is 1. We are working backwards to find out what the starting velocity will need to be.
int i = 0;
double xmath = Math.sin(rot* (Math.PI/180.0f)); // component of velocity for x,y direction based on rotation
double ymath =  Math.cos(rot* (Math.PI/180.0f));
while (true) {
        /* with this section uncommented, the stall never happens...
         ++i;
        if (i>1000) {
            // Something went rather wrong
            vel = 91.0f; // LOG WAS HERE, now has a fallback value justincase
            break;
        }
        */
        vel *= 1.2f;            
        dx -= vel* xmath;
        dy += vel* ymath;           
        if (distance < (float)Math.sqrt(dx*dx+dy*dy)) {
            break;
        }
}
// Give the ship a velocity that is appropriate for the distance remaining
_AI.Ship.Velocity = vel;

你能展示一些代码吗? - Fildor
你肯定可以排除线程问题,比如死锁或类似的问题,我猜。但是再说一遍,如果是死锁,它应该会发生在不止全新的手机上...这很难说。 - Fildor
1
Dalvik线程在达到安全点时会自行暂停。Spin-on-suspend意味着线程未达到安全点,因此(a)您的循环不会退出,(b)VM中的某些内容出现了问题。您是否在logcat中看到类似于“JIT unchain all”的内容?我的初步猜测是JIT从紧密循环中剥离了挂起检查,这是允许的--其他线程可以“解除”段以重新引入检查--但某些内容无法正常工作。 - fadden
你是否发现在运行原生 Android 的 GS4/HTC1 上,与零售版 Samsung/HTC 版本相比存在问题? - fadden
1个回答

3

这可能是http://b.android.com/58726

该漏洞具有完整细节;简而言之:一些供应商似乎使用了修改版本的Dalvik VM。 JIT编译器所做的更改防止在线程悬停在某些情况下发生。

此问题的试金石是将标准零售设备与GS4和HTC1的“纯Android”Google Play版进行比较。如果前者显示出错误行为,但后者可以正常工作,则可能会看到特定于供应商的问题

解决方法是像您所做的那样:使代码效率低下,以便它不会进入“优化”状态。 理想情况下,应用程序应在运行时为没有该问题的设备选择不同的代码路径,但我不知道如何在运行时检测该问题的好方法。


这种情况是否也可能发生在Android原生代码中,抽象出哪一部分软件会导致它呢?这是我知道ffmpeg(静态二进制自定义编译,n2.0.1标签薄荷克隆)在某些设备上失败而在其他设备上不失败的唯一原因(但在使用valgrind启动时,在任何设备上都不会失败!现在我猜测,它经常捕获内部程序)。 - 1737973
本地代码是使用gcc编译的,这与Dalvik内部的JIT编译器完全独立。 - fadden

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接