关于lua与js性能、js内存泄漏、垃圾回收造成卡顿的测试

写在前面:公司使用cocos2d-js 3.9开发游戏,一款横版过关arpg游戏,目前已经在app store上线(感兴趣可以app store搜艾泽拉斯战记)。发现游戏出现有规律卡顿,并确认原因出在GC上面,没有找到合适的方法解决,比较烦恼。遂做一些测试,对于lua与js的性能比较好奇,也想与大家交流一下,如果有测试不合理的地方还望提出。

##环境
mac mini + OS X Yosemite(10.10.5)+xcode(Version 7.2.1)
cocos2d-x 13.1.1 + Simulator Iphone6(IOS 9.2)

##测试代码(代码均在cocos2d_tests中稍加修改得来)

js:

var scene = new cc.Scene();
var array = {};
var count = 0;
scene.update = function () {
  for (var i = 1; i < 100000; i++) {
    array[i+count*100000] = {value:[count]};
  }
  count++;
  if (count > 51) {
    for (var i = 1; i < 100000; i++) {
      array[i+(count-50)*100000] = null;
    }
  }
  cc.log("count="+count);
}
scene.scheduleUpdate();
cc.director.runScene(scene);

lua:

local sceneGame = cc.Scene:create()
local array = {}
local count = 0;
sceneGame.update = function ()
  for i=1, 100000 do
    array[i+count*100000] = {["value"]={count}}
  end
  count = count+1
  if count > 51 then
    for i=1, 100000 do
      array[i+(count-50)*100000] = nil;
    end
  end
cclog("count=" .. count);
end
sceneGame:scheduleUpdateWithPriorityLua(sceneGame.update,0)
cc.Director:getInstance():runWithScene(sceneGame)

分别运行一分钟。

##帧数对比
js 9fps
lua 2.5fps.
在这里 js的性能明显比lua要快。但是 如果不是这种赋值方式,而是简单的创建对象,lua比js要快,另外lua的加法和乘法也比js要快上许多。

##卡顿现象
js在循环稳定后,会出现周期卡顿,应该是由GC引起。
lua在循环稳定后,没有出现周期卡顿,但是本身lua帧数较低,这里也没有弄明白是lua的垃圾回收性能较好,还是本身比较卡所以也看不出来。

##内存占用
js在循环稳定后,在xcode中使用内存截图如下:

在时间增加的过程中,内存占用持续增高,会出现周期性回落,但是整体上是升高的。

lua:

内存占用会稳定在1GB。但是会越来越卡。

有一点我一直不是很明白,就是xcode里面的memory里面显示的究竟是什么内存。因为我跑js的测试代码时,最后内存会到3.xGB。望知道的告诉我一下,谢谢。

##Instruments:
js:


lua:

同样对于显示结果的解释不是很清楚的了解,直观上看js有内存泄漏。

以上是测试的现象,但是有些现象不能很好的解读,希望论坛内的大神能帮忙解读一下。
另外js游戏GC造成卡顿的现象,对于一个商业游戏应该是不能接受的。网上查到的方法有做对象池的,做缓存的(?),我自己想到的还有切换场景的时候手动调用gc函数进行gc。但是,在我们游戏的战斗过程中依然会产生让人不爽的卡顿(即使使用iphone7p)。这个问题只能是在代码编写中少新建对象,尽量重用已有对象来解决吗?感觉对编程的要求有些高,有时很难注意。
另外以上的结果是否证明使用lua还是比使用js要稳定一些?还是希望与大家进行讨论: )。

1赞

我认为脚步语言的性能对游戏实际运行的效率影响应该不是主要的。
游戏卡顿应该先找出是什么原因造成的(内存泄露、同时存在的对象过多、或者绘制效率低等等),针对问题去优化才能取到好的结果。

JS脚本本身的GC是不会造成卡顿的。
你如果有卡顿 是否后台有下载图片或者压缩包 解压的时候可能会卡顿
还有 切换场景 如果场景比较复杂 当前的读取CSB的方式是会卡顿的(同步的操作)
还有如果你的点击事件调用了getBoundingBoxInWorld也会容易引起卡顿(会去计算所有子节点的BOUNDS,而且是用JS来算的。。)
蛮多容易导致掉帧的 但是如果处理好了 根本就不会卡顿 目前没有遇到过

是这样的,我们游戏在主页面完全不动的情况下,有周期约为30秒的卡顿,每次大概0.1秒这样子。然后如果我等到20秒的时候手动调用一下gc方法,则这个卡顿时间会重置为30秒。因此我猜测是垃圾回收造成的卡顿。
我们公司另外一个项目也有同样的情况,而且我上面的测试代码也可以出现卡顿的现象。
你们的游戏类型是否较为简单所以轻微的卡顿看不出?我们公司另一款游戏在战斗结算页面,仔细观察,会有定时的轻微的卡顿,游戏没有我们项目的游戏逻辑复杂。

如果不调用网络 不知道是否会有卡顿? 我觉得你可以测试一下 如果你的游戏都有这个现象 我在想是不是用的你们自己写的同一套基本代码开发的 是不是有什么模块代码导致的。
我们做的是弱联网的单机游戏 一个场景里面全是碎图 ,大几十张吧 都有交互什么的(没有合成的) 没有见过这样的现象。 也许是你们的网络模块接收数据后干嘛了 比如本地存储数据(会导致卡顿) 解压了文件 或者每次接收处理的字节太多?

谢谢帮助。
我们这个应该跟网络没有关系。因为是网络问题的话,应该比较容易注意到。
你们的游戏逻辑有主循环吗?还是都是事件触发(点击,网络消息)的方式?
我想我们的游戏在逻辑循环中创建了太多的临时对象,才导致的这个问题。。 但是如果不创建临时对象,不会代码编写起来畏手畏脚的吗,而且感觉很麻烦。。

没有很重的主循环
基本上使用事件和点击交互事件完成 还有回调完成功能。
我的不是那种赛车 等需要每帧进行判断的游戏 不需要那样做

强制一次大gc肯定会卡顿的,而系统自己的步长gc应该不会太卡顿的。如果不是IO的原因的话,就要考虑有时候某一帧是否有太多复杂的逻辑了,可以考虑分多帧来处理一帧的复杂逻辑。

你这个图看起来 有明显的内存泄漏没有解决

我这个图的代码就在上面哦。不是我们项目的图。

顶上去,我们用js 也同样问题 内存越来越高

2d-js 3.16

lua中可用collectgarbage方法进行手动回收