内存泄漏:高速缓存中的剩余元素和AngularJs中的data_user

bes*_*der 7 javascript jquery svg angularjs

我用ng-repeat创建元素(有些是SVG标签,有些是简单的HTML).在数据模型的变化 - 在新数据到达时重置的对象 - 总是留下作为分离的DOM元素的元素.它们是这样的:

Chrome开发者工具的屏幕截图

Elements是data_user的一部分,它似乎是jquery的一部分.在数据更改的几个地方会出现此问题.似乎观察者是问题,因为他们一直在参考他们的表达.

元素的创建如下所示:

.directive('svgGraphic', ['$compile', function ($compile) {
    return {
        restrict: 'E',
        replace: false,
        link: function (scope, element, attrs) {
            var svgData = scope.model.getAttribute("svgGraphic");
            var svgDomElement =  $(svgData.svg);
            scope.layers = svgData.layers;

            svgDomElement.append('<svg-layer ng-repeat="layer in layers"></svg-layer>');
            element.append($compile(svgDomElement)(scope));

            scope.$on("$destroy", function() {
                scope.$$watchers = null;
                scope.$$listeners = null;
            })
        }
    };
}])
Run Code Online (Sandbox Code Playgroud)

解决方法是手动删除观察者和听众,如上所示 - 我认为什么是不好的解决方案!

当来自服务器的新数据到达时,它设置如下:

$scope.model = model;
$scope.$digest();
Run Code Online (Sandbox Code Playgroud)

仅更换模型数据是一个问题吗?

是否有任何想法如何角度不会删除旧元素上的听众?当ng-repeat接收新数据并重建所有元素时,Angular应删除所有观察者.

小智 1

我发现了同样的问题。我创建了一个 Watcher 类,因此使用探查器我可以对 Watcher 实例进行计数。我看到当我导航应用程序时实例继续增加,一些实例由 data_user 缓存保留:(。

此外,我还修复了 childScopes 观察程序的删除问题,并向范围添加了一些元数据,例如 childScope 列表。

这是我更改的角度代码(仅更改了我更改的功能)。我希望这可以帮助您找到错误,我仍在与之斗争:)

function $RootScopeProvider() {


 this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
  function($injector, $exceptionHandler, $parse, $browser) {

 var watcherCount = 0;

function Watcher(listener, initWatchVal, get, watchExp, objectEquality, scope) {
  this.fn = isFunction(listener) ? listener : noop;
  this.last = initWatchVal;
  this.get = get;
  this.exp = watchExp;
  this.eq = !!objectEquality;
  this.scope = scope;
  this.id = watcherCount++;
}

Watcher.prototype = {
  constructor: Watcher
}

function Scope() {
  this.$id = nextUid();
  this.$$phase = this.$parent = this.$$watchers =
                 this.$$nextSibling = this.$$prevSibling =
                 this.$$childHead = this.$$childTail = null;
  this.$root = this;
  this.$$destroyed = false;
  this.$$listeners = {};
  this.$$listenerCount = {};
  this.$$isolateBindings = null;
  this.childsScopes = [];
}


Scope.prototype = {
  constructor: Scope,

  $new: function(isolate, parent) {
    var child;

    parent = parent || this;

    if (isolate) {
      child = new Scope();
      child.$root = this.$root;
    } else {
      // Only create a child scope class if somebody asks for one,
      // but cache it to allow the VM to optimize lookups.
      if (!this.$$ChildScope) {
        this.$$ChildScope = function ChildScope() {
          this.$$watchers = this.$$nextSibling =
              this.$$childHead = this.$$childTail = null;
          this.$$listeners = {};
          this.$$listenerCount = {};
          this.$id = nextUid();
          this.$$ChildScope = null;
        };
        this.$$ChildScope.prototype = this;
      }
      child = new this.$$ChildScope();
    }
    //window.scopes = window.scopes || {};
    //window.scopes[child.$id] = child;
    this.childsScopes.push(child);
    child.$parent = parent;
    child.$$prevSibling = parent.$$childTail;
    if (parent.$$childHead) {
      parent.$$childTail.$$nextSibling = child;
      parent.$$childTail = child;
    } else {
      parent.$$childHead = parent.$$childTail = child;
    }

    // When the new scope is not isolated or we inherit from `this`, and
    // the parent scope is destroyed, the property `$$destroyed` is inherited
    // prototypically. In all other cases, this property needs to be set
    // when the parent scope is destroyed.
    // The listener needs to be added after the parent is set
    if (isolate || parent != this) child.$on('$destroy', destroyChild);

    return child;

    function destroyChild() {
      child.$$destroyed = true;
      child.$$watchers = null;
      child.$$listeners = {};
      //child.$parent = null;
      child.$$nextSibling = null;
      child.$$childHead = null;
      child.$$childTail = null;
      child.$$prevSibling = null;
      child.$$listenerCount = {};
      if (child.$parent) {
        var index = child.$parent.childsScopes.indexOf(child);
        child.$parent.childsScopes.splice(index, 1);
      }

      console.log("Destroying childScope " + child.$id);

    }
  }

  $destroy: function() {
    // we can't destroy the root scope or a scope that has been already destroyed
    if (this.$$destroyed) return;
    var parent = this.$parent;
    console.log('Destroying Scope '+ this.$id);
    //delete window.scopes[this.$id];
    this.$broadcast('$destroy');
    this.$$destroyed = true;
    if (this === $rootScope) return;

    for (var eventName in this.$$listenerCount) {
      decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
    }

    // sever all the references to parent scopes (after this cleanup, the current scope should
    // not be retained by any of our references and should be eligible for garbage collection)
    if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
    if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
    if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
    if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;

    // Disable listeners, watchers and apply/digest methods
    this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
    this.$on = this.$watch = this.$watchGroup = function() { return noop; };
    this.$$listeners = {};

    // All of the code below is bogus code that works around V8's memory leak via optimized code
    // and inline caches.
    //
    // see:
    // - https://code.google.com/p/v8/issues/detail?id=2073#c26
    // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
    // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451

    this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
        this.$$childTail = this.$root = this.$$watchers = null;
  }


}];
}
Run Code Online (Sandbox Code Playgroud)