最近公司的游戏项目中出现了一个奇怪的bug。安卓端当旁观玩家过多,比如达到150人的时候,直接崩溃。
出现这个bug的时候,我也很崩溃。仔细看了一下log,提示
`local reference table overflow (max=512)`
百度一下,说是Android JNI局部引用表溢出。
根据操作步骤,定位到了lua代码中的引起报错的代码块。仔细查看了代码,发现是lua调用android端方法,获取旁观者的昵称与备注。因为lua调用android端方法是通过luaj.callStaticMethod
去调用,再捋一下实现方法,发现是在lua-bindings的CCC代码中去实现。好,按照网上的说法,是因为循环创建新的字符串并返回指向这个字符串的局部引用,这样造成局部引用表被填满。而Android中局部引用表默认最大容量是512个。
这样一来,思路很清晰了,根据网上教程,字符串使用完成之后,调用DeleteLocalRef删除局部引用。一看到这很激动,马上就能解决问题了。然后返回到实际C++代码中一看,发现并没有网上说的创建新的字符串的情况,死马当活马医医,依葫芦画瓢一下。改好C++之后,重新编译so文件,测试一下,然并卵,该崩溃照样崩溃。
后来再仔细看了一下lua代码,发现代码中根据旁观者人数集合长度,循环的在调用JNI。转变思路一想,我把集合直接传给android端,android端查找好昵称之后,再传回给lua,这样JNI只需要调用一次了。按照思路修改了之后,果然可以了。
其实还有更好的解决方案:本局游戏中进入了玩家之后,服务端发送进入通知,带上所有玩家的信息,缓存好所有玩家信息,这样需要显示时,只需要查找缓存,不需要从app端调用方法查询。这样省时省力省内存。
修改bug主要是思路上要清晰。虽然看起来我这写的很简单,但是其中走了很多弯路。定位问题不够准确,导致浪费了很多时间。以后要注意