什么是内存泄漏?=> 是什么导致的内存泄漏?=> 怎么解决内存泄漏?=> 垃圾回收机制的策略(两种) => 如何管理好内存?
面试官:什么是内存泄漏?为什么会导致内存泄漏?
不再用到的内存,没有及时释放,就叫做内存泄漏。
内存泄漏是指我们已经无法再通过 js 代码来引用到某个对象,但垃圾回收器却认为这个对象还在被引 用,因此在回收的时候不会释放它。
导致了分配的这块内存永远也无法被释放出来。如果这样的情况越 来越多,会导致内存不够用而系统崩溃。
JavaScript 具有自动垃圾收集机制(GC:GarbageCollecation),也就是说,执行环境会负责管理代码执行过程中使用的内存。开发人员不用再关心内存使用问题,所需内存的分配以及无用内存的回收完全实现了自动管理。
面试官:怎么解决内存泄漏?说一说 JS 垃圾回收机制的运行机制的原理?
需要我们手动管理好内存,但是对于 JS 有自动垃圾回收机制,自动对内存进行管理。
JS环境中分配的内存一般有如下生命周期:
JavaScript 中最常用的垃圾收集方式是标记清除(mark-and-sweep)。
它的实现原理就是通过判断一个变量是否在执行环境中被引用,来进行标记删除。
垃圾回收器给所有内存变量进行标记,然后去掉执行环境中和被引用的标记,剩余的标记变量是 不会被用到的变量,会被垃圾回收器回收。
这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。
该算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象)。垃圾回收器将
定期从根开始
(在JS中就是全局对象)扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的
。那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。
此算法可以分为两个阶段,一个是标记阶段(mark),一个是清除阶段(sweep)。
在标记阶段,从根对象1可以访问到B,从B又可以访问到E,那么B和E都是可到达对象,同样的道理,F、G、J和K都是可到达对象。
在回收阶段,所有未标记为可到达的对象都会被垃圾回收器回收。
通常来说,在使用标记清除算法时,未引用对象并不会被立即回收。取而代之的做法是,垃圾对象将一直累计到内存耗尽为止。当内存耗尽时,程序将会被挂起,垃圾回收开始执行。
补充: 从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。所有对JavaScript垃圾回收算法的改进都是基于标记-清除算法的改进,并没有改进标记-清除算法本身和它对“对象是否不再需要”的简化定义。
这是最初级的垃圾收集算法.现在已经没有浏览器会用这种算法.
此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它
”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。
引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。
缺陷
该算法有个限制:无法处理循环引用。如果两个对象被创建,并
互相引用,形成了一个循环
。它们被调用之后会离开函数作用域,所以它们已经没有用了,可以被回收了。然而,引用计数算法考虑到它们互相都有至少一次引用,所以它们不会被回收。
参考 Chrome V8回收算法
主要注意以下两点:
JS 的内存自动管理一个最主要的问题就是分配给 Web 浏览器的可用内存数量通常比分配给桌面应用程 序的少。
为了能够让页面获得最好的性能,必须确保 js 变量占用最少的内存,最好的方式就是将不用的变量引用 释放掉,也叫做解除引用。
var a = 20; // 在栈内存中给数值变量分配空间
alert(a + 100); // 使用内存
var a = null; // 使用完毕之后,释放内存空间
补充:因为通过上边的垃圾回收机制的标记清除法的原理得知,只有与环境变量失去引用的 变量才会被标记回收,所用上述例子通过将对象的引用设置为 null ,此变量也就失去了引 用,等待被垃圾回收器回收。