3

快速背景:该应用程序是一个音频播放器,ffmpeg 编译为原生共享对象并用于解码,单独的原生库编译为共享对象并用于音频处理,AudioTrack 用于输出处理后的音频。所有音频功能都封装在一个类中,该类使用静态类变量来确保只有一个实例。在这个类中:一个ja​​va线程用于从ffmpeg获取数据,通过原生处理库完成音频处理。本机处理调用需要 2 到 3.5 毫秒,具体取决于配置。处理后的音频位于由读取和写入计数信号量管理的 ByteBuffer 数组中。一个单独的 java 线程 ByteBuffer.get 一个音频块,减少 sem 计数并将 byte[] 数据发送到 AudioTrack。AudioTrack 配置为流模式,缓冲区大小为 getMinBufferSize 返回的大小的 2 倍。音频数据以每通道 512 个样本块的形式通过系统,对于立体声,2048 字节。

问题:一切都很好,随机的时间,然后一切都停止了,没有崩溃,没有 SIGSEG,应用程序只是消耗了大部分 CPU 并且什么都不做。GUI 没有响应,ADT 中的调试器失去了与应用程序的连接。使用 shell 和 top -m 10 -t,我可以看到应用程序 GC 线程正在消耗 49%,我假设是单核。有时,AudioTrack 会以低利用率弹出。与应用程序相关的其他线程从未出现在前 10 名中。这在 4.2.2 上发生得非常快,在 4.0.1 和 4.1.1 上不太确定。

为了解决:我使用 libc.debug.malloc 10 来验证本机内存使用情况。发现并修复了一些泄漏,但根据 libc,没有任何内容超出缓冲区的末端。安装 ByteBuffers 作为解决方法的尝试,之前使用了 byte[] 并表现出相同的问题。我删除了 AudioTrack 并替换为基于 OpenSL ES 的本机代码,结果相同。毫不奇怪,因为 OpenSL ES 使用了 AudioTrack。Log.i 样式的调试消息显示写入 AudioTrack 线程停止消耗数据,ffmpeg 读取和处理线程继续,直到 ByteBuffer 数组填满。我已将 System.gc() 调用放在战略位置,有时需要更长时间,但仍会出现挂起。我在 THREAD_PRIORITY_URGENT_AUDIO 设置了“已处理的音频数据到 AudioTrack”线程的优先级,没有观察到任何变化。

我已经广泛搜索了类似问题的实例,但发现的信息很少。我为 Eclipse/ADT 安装了 ARM 的 DS-5 调试功能。该工具能够保持与旋转应用程序的连接。暂停表明 GC 线程处于 dlmalloc_inspect_all 内部的无限循环中,可能试图回收或合并本机堆。AudioTrack 线程在暂停时处于 nanosleep,从 usleep 调用,AudioTrack.cpp 中只有两个 this 实例,一个在 processAudiobuffer 中,另一个在 tryLock 中。tryLock 从 stepServer 和 framesReady 调用。我无法从挂起的应用程序中获取堆栈转储,kill -3 在挂起 #1 threadid=2 (pcf=0) 时产生自旋,该应用程序从未被声明为 ANR,因此没有 /data/ANR/traces.txt。

我的概要 - GC 正在做它的事情并检查本机堆。根据文档,GC 不会在 JNI 调用中间暂停。但是它必须暂停 AudioTrack 线程,并且当本机堆检查与 AudioTrack 的 processAudioBuffer 的执行同时发生时,就会发生死锁。

问题: 1) 我肯定会受益于原生组件的堆栈跟踪,缺少开发平台和 JTAG 调试器,还有其他方法可以尝试吗?2) 有没有人看到 GC 和 AudioTrack 进入死锁路线的任何问题?3) GC dlmalloc_inspect_all 调用是否有可能被抑制或以其他方式同步以避免此问题?4)对解决这个问题有什么建议吗?

如果有帮助,我很乐意发布一些代码

4

1 回答 1

0

Faced same issue (random hang - all stops, no crash, no SIG, consumes most of the CPU and does nothing). Problem was in corrupted memory in JNI code (like buffer overrun).

Also this issue seems very device dependent (100% repro on 2 of my devices, and no one issue on other 3) Other platforms (win32, iOS) also didn't spotted corrupted memory (i'm working on crossplatform game) So android's dalvik memory manager is nice "tool" to detect memory bugs, now we testing everyday build on "memory corruption sensitive" device. Helps to ensure new code stability

For more info try

And an excellent article by Dianne Hackborn

于 2013-12-27T03:51:28.557 回答