将资源的被引用次数保存起来,当被引用次数变为零时就将其释放的过程。

会导致更多的内存泄漏,已不被采用。

循环引用导致内存不能正常被回收

  1. // 函数 a 执行完后,本来 x, y 对象都应该在垃圾回收阶段被回收, 可是由于存在循环引用,也不能被回收。
  2. function a () {
  3. var x = {};
  4. var y = {};
  5. x.z = y;
  6. y.z = x;
  7. }
  8. a();

IE 6, 7 对DOM对象进行引用计数回收,这样简单的垃圾回收机制,非常容易出现循环引用问题导致内存不能被回收, 进行导致内存泄露等问题。

  1. !function(){
  2. // IE 6, 7 中下列代码会导致 btn 不能被回收
  3. var btn = document.getElementsByTagName('button');
  4. btn.onclick = function(){
  5. console.log(btn.innerHTML);
  6. };
  7. }();

标记清除的方式需要对程序的对象进行两次扫描,第一次从根(Root)开始扫描,被根引用了的对象标记为不是垃圾,不是垃圾的对象引用的对象同样标记为不是垃圾,以此递归。所有不是垃圾的对象的引用都扫描完了之后。就进行第二次扫描,第一次扫描中没有得到标记的对象就是垃圾了,对此进行回收。

大多数浏览器采用此回收机制。

  1. function foo(arg) {
  2. // 没有 var 时,默认全局
  3. bar = {};
  4. }
  1. function foo() {
  2. this.bar = {};
  3. }
  4. // 此时执行 foo 函数,this 其实是指代 window
  5. foo();
  1. // 未清除定时器
  2. !function(){
  3. var node = document.getElementById('search');
  4. setInterval(function() {
  5. console.log(node);
  6. if(node) {
  7. node.innerHTML = new Date();
  8. }
  9. }, 1000);
  10. // 移除node节点
  11. node.remove();
  12. }();
  13. // 未清除回调函数
  14. !function(){
  15. var element = document.getElementById('button');
  16. function onClick(event) {
  17. element.innerHtml = 'text';
  18. }
  19. element.addEventListener('click', onClick);
  20. // 在 IE6、IE7 浏览器上,移除 node 之前需要手动的 removeEventListener,这样才能保证 element 被正常回收(其实就是循环引用)
  21. element.removeEventListener('click', onClick);
  22. element.parentNode.removeChild(element);
  23. }();
  1. <div id="root">
  2. <div id="app">
  3. <div id="son"></div>
  4. </div>
  5. </div>
  6. var app = document.getElementById('app');
  7. var son = document.getElementById('son');
  8. app.remove(); // 移除 app 节点
  9. app = null; // 释放 app 引用
  10. console.log(son.parentNode); // app 可访问,son 在全局的引用,导致 app 的 dom 没有被释放
  1. var replaceThing = function () {
  2. // 为了方便观察内存情况,创建了一个很大的字符串,这样数组本身会占用很大的内存
  3. var originalThing = new Array(100000000).join('*');
  4. var outer = 'outer str';
  5. console.log('new...');
  6. return function () {
  7. if (originalThing)
  8. console.log(outer);
  9. };
  10. };
  11. // 由于生成的函数被 closureFn 引用,所以内存不释放
  12. var closureFn = replaceThing();
  13. closureFn();
  14. // 使用以下方式可以得到释放
  15. replaceThing()();
  1. var theThing = null;
  2. var replaceThing = function () {
  3. var originalThing = theThing;
  4. var unused = function () {
  5. if (originalThing)
  6. console.log('hi');
  7. };
  8. theThing = {
  9. // 为了方便观察内存情况,创建了一个很大的字符串,这样数组本身会占用很大的内存
  10. longStr: new Array(100000000).join('*'),
  11. someMethod: function () {
  12. console.log('123');
  13. }
  14. };
  15. };
  16. // 每1秒执行一次,看内存变化
  17. setInterval(replaceThing, 1000);
  18. // 由于 unused 和 theThing.someMethod 在一个闭包内,导致共享一个闭包作用域,从而关联两个函数,但是不会造成对 originalThing 的关联。originalThing 的关联是由 unused 内部引用导致的。由此可见 replaceThing 内部的变量都被紧密联系在一起。
  • 尽量避免使用全局变量,例如使用立即执行函数的形式。
  • 使用“严格模式”开发,避免因为我们的疏忽导致意外产生全局变量。
  • 对于一些占用内存较大的对象,在变量不在使用后,手动将其赋值为 null,例如前面例子中的超大的数组。
  • 尽量避免把闭包内部与外部产生关联引用,例如上面例子中的 theThing 变量

版权声明:本文为xingkongbj原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/xingkongbj/p/9237650.html