【问题标题】:Memory leak when using angular's $compile with a new scope使用 Angular 的 $compile 和新范围时的内存泄漏
【发布时间】:2017-02-13 04:10:27
【问题描述】:

我想使用 javascript 动态创建 Angular 组件,然后使用 $compile 使用新创建的范围对它们进行 Angular 编译。然后当我不再使用该组件时,我想销毁该组件和新范围。

一切都按预期工作,除了即使我正在销毁新范围,它使用的所有内存都不会释放。

这是该代码的简化版本的一部分:

app.controller("mainCtrl", ["$scope", "$compile", function($scope, $compile) {
    var childScope;

    //call this every time the button is clicked
    this.createDirective = function() {
        //dynamically create a new instance of the custom directive
        var customDirective = document.createElement("custom-directive");

        //if another child scope exists, destroy it
        if (childScope) {
            childScope.$destroy();
            childScope = undefined;
        }

        //create a new child scope
        childScope = $scope.$new();

        //compile the custom directive
        $compile(customDirective)(childScope);
    };

}]);

此代码的完整工作示例是here

所有这些代码所做的,就是在每次单击按钮时创建一个新组件,但首先销毁任何已经存在的组件。 请注意,我实际上并没有在页面中添加已编译的组件,因为我注意到无论我是否使用它,泄漏仍然存在。

使用 Chrome 的开发工具(Profiles -> Record Allocation Timeline -> Start)点击按钮后我看到以下内存使用情况 几次:

Memory consumption

很明显,customDirective 占用的任何内存都不会真正释放,即使正在调用作用域的$destroy 函数。

我过去成功地使用了$compile,但没有创建新的范围,但在这种情况下我似乎遗漏了一些东西。我是否还应该做其他事情来确保没有对新范围的引用?

编辑

根据 JoelCDoyle 下面的回答,这里是修复(我在我创建的作用域中添加了一个 on destroy 函数):

app.controller("mainCtrl", ["$scope", "$compile", function($scope, $compile) {
    var childScope;

    //call this every time the button is clicked
    this.createDirective = function() {
        //dynamically create a new instance of the custom directive
        var customDirective = document.createElement("custom-directive");

        //if another child scope exists, destroy it
        if (childScope) {
            childScope.$destroy();
            childScope = undefined;
        }

        //create a new child scope
        childScope = $scope.$new();

        //compile the custom directive
        var compiledElement = $compile(customDirective)(childScope);

        //FIX: remove the angular element
        childScope.$on("$destroy", function() {
            compiledElement.remove();
        });
    };
}]);

Fixed fiddle

【问题讨论】:

    标签: javascript angularjs memory memory-leaks


    【解决方案1】:

    我想我已经找到了解决方案:https://jsfiddle.net/yqw1dk0w/8/

    app.directive('customDirective', function(){
      return {
        template: '<div ng-controller="customDirectiveCtrl"></div>',
        link: function(scope, element) {
          scope.$on('$destroy', function() {
            element.remove();
          });
        }
      };
    });
    

    我仍然对它的工作原理有点模糊,但是角度编译文档中的指令如何编译这部分提供了一个线索:https://docs.angularjs.org/guide/compiler

    $compile 通过调用组合模板来链接模板和作用域 上一步的链接函数。这反过来将调用 各个指令的链接功能,注册监听器 在元素上 并设置 $watchs 的范围为 each 指令被配置为做。\

    这样做的结果是作用域和DOM之间的实时绑定。所以在这个 > 点,模型在编译范围内的变化将反映在 DOM 中。

    我猜,销毁范围不会删除这些元素侦听器。上面的代码就是这样做的:destroy directive/child scope on scope destroy

    【讨论】:

    • 这确实看起来很有希望,因为它修复了小提琴示例。我会将它应用到我的应用程序中,看看它是否也能在那里工作
    • 是的!这也适用于我更复杂的应用程序..!虽然.. 我仍然不完全理解 为什么 这有效。我没有在 DOM 中添加它,那么为什么需要将其删除?此外,即使我将已编译的元素添加到 DOM 中然后将其删除,内存仍在泄漏。 $compile 是否也将其添加到其他地方?可能是缓存..?
    【解决方案2】:

    如果您将数组放入作用域并解除分配,它将开始解除分配

    $scope.array.length = 0;
    

    到析构函数。但是......很高兴知道。我将不得不密切关注内存消耗。似乎保留了范围。因为我只是取消分配内部变量。

    【讨论】:

    • this.array 在销毁侦听器内部未定义(使用var 而不是this 设置数组),但该方法是合理的。垃圾收集回收内存。不错!
    • 啊...是的,我将其重新分配给范围以便能够解除分配。编辑...
    • 这只是解决症状,并没有真正解决问题。该数组仅分配用于模拟更复杂的组件使用的内存,该组件无法手动删除它正在使用的所有变量
    • 您上面提到的两个答案都摆脱了数组使用的内存,但范围仍然存在,在内存中。它仍然没有发布
    猜你喜欢
    • 2014-07-18
    • 2012-10-25
    • 1970-01-01
    • 2022-01-11
    • 2013-05-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-03
    相关资源
    最近更新 更多