内存泄漏 & JavaScript-GC垃圾回收机制 - springboot实战电商项目mall4j

544 阅读3分钟

springboot实战电商项目mall4j (https://gitee.com/gz-yami/mall4j)

java开源商城系统

内存泄漏

什么是内存泄漏 ?

程序的运行需要内存。只要程序提出要求,操作系统或者运行时(runtime)就必须供给内存。对于持续运行的服务进程,必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。

简单地说:不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)

有一些语言(比如C语言)必须手动释放内存,即内存管理由程序员来负责,所有的内存都需要手动释放,想想就很繁琐,绝大多数的语言提供自动的内存管理,我们称之为 ”垃圾回收机制“

JS的垃圾回收机制-两种收集策略

垃圾收集机制原理:垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间), 周期性地去找出那些不再继续使用的变量,然后释放其占用的内存。

1. 标记清除

标记清除(mark-and-sweep) 是 JavaScript中最重用的垃圾收集方式

当一个变量在使用时,垃圾收集会给变量添加标记为 进入环境,理论上来说进入环境中的变量,断然是不能被释放的,因为在环境中的的变量很大概率正在使用;

当一个变量离开环境时,垃圾收集会将变量标记为 离开环境 当变量被标记为此状态,在垃圾收集器定时执行时,就会释放掉对应的变量占用的内存

// 声明一个 加1 的函数
function addOne(num){  
  // 垃圾收集 将 sum 标记为“进入环境”
  let sum += num
  // 垃圾收集 将 sum 标记为“离开环境”
  return sum
}
addOne(1)  // 输出2

目前所有的现代浏览器几乎都使用 标记清除 这种垃圾回收算法

2. 引用计数

引用计数是最初级的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。

这种策略存在缺陷:当发生循环引用时,计数永远不会归零

// 示例
function fn(){
  var ojb1 = {};
  var ojb2 = {};
  ojb1.aa = ojb2; // o 引用 o2
  ojb2.aa = ojb1; // o2 引用 o

  return "循环引用";
}
// 调用fn
fn();

示例中 ojb1 和 ojb2 通过自身的属性互相引用对方,即它们的被引用此时都为2

那么在引用计数这种策略下,obj1 和 obj2 将不会被释放,因为它们引用次数不为0,所以此方法存在内存泄漏的风险

JS中常见的内存泄漏

了解了垃圾回收机制,回到内存泄漏的问题,日常开发中有哪些情况容易造成内存泄漏呢?

全局变量

function fn() {
    bar1 = 'some text'; // 没有声明但直接赋值 实际上是全局变量 => window.bar1
    this.bar2 = 'some text' // 全局变量 => window.bar2
}
fn()

未清除的定时器

function fn() {
    setInterval(function() {
        var box = document.querySelector('#box');
        if(box) {
            box.innerHTML = JSON.stringify(serverData);
        }
	}, 5000); // 每 5 秒调用一次
}
fn()

事件监听

添加了事件监听但未移除

window.addEventListener('scroll', this.handleScroll)

闭包

var closure = function(){
    var count = 0;
    return function(){
       return count ++;
    }
}
const fn = closure(); 
console.log(fn()); // 0
console.log(fn()); // 1
console.log(fn()); // 2

每次调用fn时,count 值都基于上一次的值增加1,即count的引用一直保存在内存中

springboot实战电商项目mall4j (https://gitee.com/gz-yami/mall4j)

java开源商城系统