springboot实战电商项目mall4j (https://gitee.com/gz-yami/mall4j)
内存泄漏
什么是内存泄漏 ?
程序的运行需要内存。只要程序提出要求,操作系统或者运行时(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的引用一直保存在内存中
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。