【问题标题】:Why does Game code keep running instead of being garbage collected?为什么游戏代码继续运行而不是被垃圾收集?
【发布时间】:2015-12-22 13:06:39
【问题描述】:

我有这段代码在窗口 onload 事件触发后创建一个匿名游戏实例。

由于整个函数是匿名的,并且对游戏对象的引用没有保存在任何地方,我真的很惊讶这个游戏居然能运行!

为什么 myGame 在 window.onload 之后没有被释放? 我知道垃圾收集不会立即发生,但我也从未见过 myGame 实例在随机时刻突然消失。 Game 类中的哪种代码能够防止实例被垃圾收集?是对 DOM 的引用吗?

Game.js

(function(window){
    window.onload = function() { 
        // create a game instance but don't save the reference anywhere
        var myGame = new Game();
    };

    function Game() {

        var myCar;
        this.name = "Main App";

        this.init = function(){
            this.logMessage("creating a game instance");
        }

        this.logMessage = function(msg) {
            document.getElementById("message").innerHTML += msg + "</br>";
        }

        this.init();
    }
})(window);  

【问题讨论】:

  • “我也从未见过 myGame 实例在某个随机时刻突然消失” 你有什么证据表明它一直存在?出示证据,以便我们帮助您解释您所看到的。

标签: javascript garbage-collection


【解决方案1】:

为什么游戏代码继续运行而不是被垃圾回收?

它没有。它设置消息,然后立即符合 GC 条件。消息仍然显示的事实并不意味着您的游戏实例仍在内存中或其代码仍在运行。

该代码中没有任何内容阻止 Game instance 被 GC'd。除非有原因(内存压力等),否则 GC 可能不会发生,但使用如此简单的代码,现代引擎可能甚至不需要应用 GC(例如,V8 可能会在 on 上分配您的实例堆栈 然后只是弹出堆栈;如果您的代码在onload 返回后保留了引用,它将在退出时将其复制到堆中。

是对 DOM 的引用吗?

不,引用 from 你的游戏实例 to DOM 不会将你的游戏实例保存在内存中,因为方向(from你的游戏实例 DOM元素)。它必须是另一种方式。 (此外,您不会保留任何正在检索的参考资料。)

Game 类中的哪种代码能够防止实例被垃圾回收?

导致直接或间接引用游戏实例的任何内容。例如,只是改变

var myGame = new Game();

window.myGame = new Game();

会这样做,因为现在window 有一个直接引用游戏实例的属性。

或者在init中添加这样的代码:

var self = this;
document.getElementById("message").addEventListener("click", function() {
    this.innerHTML = self.name;
}, false);

因为现在,该 DOM 元素具有对该事件处理函数的引用,而该事件处理函数又具有对创建它的上下文的引用,该上下文具有对 Game 实例的引用。


请注意,在您的代码中,Game(函数)保留,因为分配给 window.onload 的闭包会关闭创建 Game 的上下文。我们可以看到,如果我们在 Chrome 的堆快照中挖掘得足够深:

...但是 Game instance 创建并分配给(简要)myGame 没有保留。


FWIW,在 Chrome 中,如果我运行您的代码并进行堆快照并搜索 Game 的实例,我看不到任何实例:

如果我修改您的代码以保留实例:

window.myGame = new Game();

然后我做:

【讨论】:

  • 谢谢!所以 myGame 实际上可能已经消失了,只是我的代码太简单了,无法注意到差异。假设我要添加一个带有监听器的 DOM 按钮。这会阻止实例被 GC,还是 DOM 按钮会在一段时间后停止工作? (假设 GC 确实运行了?)
  • @Kokodoko:如果你在init 中添加了var self = this; document.getElementById("message").addEventListener("click", function() { this.innerHTML = self.name; }, false);,这会将实例保留在内存中,因为DOM 保留了对处理函数的引用,而处理函数又保留了引用到创建它的上下文,而后者又引用了游戏实例。
猜你喜欢
  • 1970-01-01
  • 2010-12-19
  • 2014-08-08
  • 1970-01-01
  • 2013-04-25
  • 1970-01-01
  • 1970-01-01
  • 2015-12-17
  • 1970-01-01
相关资源
最近更新 更多