Libgdx安卓游戏添加爆炸动画后出现画面拉伸、运行崩溃报错问题
异常成因分析
问题触发场景
基于Libgdx开发安卓平台手机游戏时,加入爆炸动画逻辑后:
- 运行约10分钟出现画面拉伸的渲染异常,伴随第一组图形驱动报错
- 运行约20分钟游戏直接崩溃,触发Android Studio中的第二组运行时错误日志
第一阶段(10分钟左右)报错对应原因
触发的报错内容为:
E/IMGSRV: :0: PVRPMapKMem: mmap(2) failed: Mapping handle=0x55d: strerror returns no value. :0: PVRSRVAllocDeviceMem : PVRPMapKMem failed for buffer :0: PVRPMapKMem: mmap(2) failed: Mapping handle=0x5b1: strerror returns no value. :0: PVRSRVAllocDeviceMem : PVRPMapKMem failed for buffer
这是PowerVR系列GPU的内核驱动日志,PVRPMapKMem、PVRSRVAllocDeviceMem调用失败,代表GPU侧的可映射共享显存、内核态图形内存已经接近耗尽,无法完成新的图形缓冲区的内存映射。
出现这个问题的直接诱因是爆炸动画相关的纹理资源存在持续性显存泄漏:每触发一次爆炸就新建Texture实例加载explosion.png资源,动画播放结束后没有对不再使用的纹理调用dispose()释放显存,无效纹理不断堆积占满显存后:
- GPU无法为正常渲染流程分配足够的帧缓冲、顶点缓存内存,就会出现画面拉伸、采样错位的视觉异常
- 驱动层面无法完成新的内存映射请求,就会持续打出这类PVR驱动的错误日志
第二阶段(20分钟左右)崩溃对应原因
触发的报错内容为:
A/libc: malloc size: 67108864 malloc size: 67108864 E/IMGSRV: :0: PVRPMapKMem: mmap(2) failed: Mapping handle=0x40f: strerror returns no value. :0: PVRSRVAllocDeviceMem : PVRPMapKMem failed for buffer A/libc: malloc size: 1440000 W/dalvikvm: threadid=13: thread exiting with uncaught exception (group=0x430f2140) E/AndroidRuntime: FATAL EXCEPTION: GLThread 20166 Process: com.mygdx.game, PID: 12106 com.badlogic.gdx.utils.GdxRuntimeException: Couldn't load file: assets/animations/explosion.png at com.badlogic.gdx.graphics.Pixmap.<init>(Pixmap.java:148) at com.badlogic.gdx.graphics.TextureData$Factory.loadFromFile(TextureData.java:98) at com.badlogic.gdx.graphics.Texture.<init>(Texture.java:122) at com.badlogic.gdx.graphics.Texture.<init>(Texture.java:118) E/AndroidGraphics: waiting for pause synchronization took too long; assuming deadlock and killing I/Process: Sending signal. PID: 12106 SIG: 9
这是内存泄漏累积到临界值后的最终崩溃表现:
- 日志中出现单次64MB(67108864字节)的malloc申请失败,代表进程的用户态堆内存、GPU共享内存已经完全耗尽,系统无法为新的资源分配流程提供可用物理内存
- 此时再次触发爆炸逻辑尝试从assets加载
explosion.png时,Pixmap解码流程因为申请不到可用内存直接抛出未捕获的GdxRuntimeException,导致GLThread线程终止,游戏进程被系统强制杀死 - 这一阶段的所有报错,和10分钟时出现的画面拉伸异常是同一个内存泄漏问题的递进表现:显存占满初期出现渲染错乱,内存完全耗尽后直接触发进程崩溃
核心问题本质
- 没有对固定复用的爆炸动画纹理做全局初始化和缓存,而是在每次生成爆炸实例时,都重复从assets路径加载新的Texture实例
- 动画播放完成销毁爆炸实体时,没有对持有的纹理引用做回收复用,也没有调用
dispose()释放纹理占用的显存,导致无效资源持续累积 - 当泄漏的内存总量触碰到安卓系统给单应用分配的内存上限后,就会依次出现渲染异常、进程崩溃的连锁问题
内容的提问来源于stack exchange,提问作者keepupwiththetimes
相关产品推荐
相关产品推荐

