javascript - 使用 Javascript 对象池来避免垃圾回收值得吗?

标签 javascript garbage-collection object-pooling

我正在尝试构建一个在浏览器中运行的 Javascript 游戏套件。我已经遇到过由于过多的垃圾收集而导致的可怕的 100 毫秒以上的暂停。这往往会破坏用户体验。

正如我所读到的,对此的补救措施是首先避免创建垃圾,例如通过对象的池化和重用。我编写了一个简单的应用程序来测试这个概念:http://jsfiddle.net/gk6Gn/

向量类包含在源代码中,并且定义非常简单:

function Vector2()
{
    this.x = 0;

    this.y = 0;
}

Vector2.prototype.addUnpooled = function (other)
{
    var v = new Vector2();

    v.x = this.x + other.x;

    v.y = this.y + other.y;

    return v;
};

Vector2.prototype.addPooled = function (other)
{
    var v = acquireVector2();

    v.x = this.x + other.x;

    v.y = this.y + other.y;

    return v;
};

我使用 requestAnimationFrame 每秒计算大约六十次帧。每一帧,我都会进行 N 次迭代。在每次迭代中,我创建两个向量并将其相加,得到第三个向量。我慢慢增加迭代次数,直到性能降到每秒 59 帧以下,并考虑每帧的最大迭代次数:

function drawFrame(time)
{
    window.requestAnimationFrame(drawFrame);

    //testClassic();

    testPooled();

    framesSinceLastReport++;

    var timeSinceLastReport = time - lastReportTime;

    if (timeSinceLastReport >= 1000)
    {
        var framesPerSecond = Math.floor(framesSinceLastReport / (timeSinceLastReport / 10000)) / 10;

        output.innerHTML = framesPerSecond + ' fps @ ' + iterationsPerFrame + ' iter/frame';

        framesSinceLastReport = 0;

        lastReportTime = time;

        if (framesPerSecond >= 59) iterationsPerFrame = Math.floor(iterationsPerFrame * 1.2);
    }
}

drawFrame();

为了比较苹果与苹果,我设置了一种“经典”方法,其中我只新建向量对象并将它们留给垃圾收集器,以及一种“池化”方法,其中我使用非收缩数组存储矢量对象以供重用。在此示例中,池永远不会大于三个向量:

function testClassic()
{
    for (var i = 0; i < iterationsPerFrame; i++)
    {
        var a = new Vector2();

        a.x = 2;

        a.y = 3;

        var b = new Vector2();

        b.x = 1;

        b.y = 4;

        var r = a.addUnpooled(b);

        if (r.x != 2 + 1 || r.y != 3 + 4) throw 'Vector addition failed.';
    }
}

function testPooled()
{
    for (var i = 0; i < iterationsPerFrame; i++)
    {
        var a = acquireVector2();

        a.x = 2;

        a.y = 3;

        var b = acquireVector2();

        b.x = 1;

        b.y = 4;

        var r = a.addPooled(b);

        if (r.x != 2 + 1 || r.y != 3 + 4) throw 'Vector addition failed.';

        releaseVector2(a);

        releaseVector2(b);

        releaseVector2(r);
    }
}

对于我的汇总测试,以下是我的获取和释放函数:

var vector2Pool = [];

vector2Pool.topIndex = -1;

function acquireVector2()
{
    if (vector2Pool.topIndex >= 0)
    {
        var object = vector2Pool[vector2Pool.topIndex];

        vector2Pool[vector2Pool.topIndex] = null;

        vector2Pool.topIndex--;

        Vector2.apply(object);

        return object;
    }
    else
    {
        return new Vector2();
    }
}

function releaseVector2(vector2)
{
    vector2Pool.topIndex++;

    vector2Pool[vector2Pool.topIndex] = vector2;
}

这一切都可以在所需的浏览器中运行,但我看到的性能结果完全令人失望:

PC

  Chrome 33.0.1750.154 m

    unpooled  1077153 iter / frame

    pooled    100677 iter / frame

  Firefox 27.0.1

    unpooled  100677 iter / frame

    pooled    33718 iter / frame

  Internet Explorer 9.0.8112.16421

    unpooled  83898 iter / frame

    pooled    83898 iter / frame

iPhone 5, iOS 7.1

  Safari Mobile

    unpooled  208761 iter / frame

    pooled    144974 iter / frame

  Chrome

    unpooled  11294 iter / frame

    pooled    3784 iter / frame

iPad with Retina, iOS 7.1

  Safari Mobile

    unpooled  208761 iter / frame

    pooled    144974 iter / frame

在任何情况下,我都没有看到池化带来更好的性能,而且在许多情况下,性能明显更差。对于 Chrome 来说尤其如此,其性能差距为 10 比 1。

我在网上看到过其他文章,这些文章显示了这种技术可以提高他们的性能。我的测试有缺陷吗?

或者我可能错过了这种方法的要点?例如。是否最好预先承受性能损失(高达 90%!),以防止 GC 在随机时间中断超过 16 毫秒帧?

最佳答案

不确定您是否仍然关心这一点,但我相信使用 Vector2.apply(object); 正在实例化必须通过垃圾收集清理的向量对象的另一个实例。因此,本质上,您并没有通过使用该方法来清空池中的向量对象来保存任何对象清理。

所以只需手动清零: http://jsfiddle.net/gk6Gn/1/

关于javascript - 使用 Javascript 对象池来避免垃圾回收值得吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22846403/

相关文章:

javascript - 使用 webpack 处理图像的想法

javascript - 在哪里可以找到 Javascript 绘图 Canvas ?

Javascript,获取浏览器 URL 中未显示的默认页面名称

java - 查看 Java Mission Control 中垃圾收集器的类型

java - 这个基本的 Java 对象池有用吗?

javascript - 当浏览器未实现 HTMLUnknownElement 时,如何检查元素是否为未知类型

java - 为什么没有立即收集 'invisible' 对象?

java - 什么触发了 Java 中的完整垃圾回收?

c# - 无法从 Unity 中的对象池获取游戏对象

c++ - 自定义内存堆上的 boost::object_pool