JS内存空间分为栈内存、堆内存、池(一般会被归为栈中)。 其中栈内存存放变量,堆内存存放复杂对象,池存放常量,所以也叫常量池。
内存回收
JavaScript有自动垃圾收集机制,垃圾收集器会每隔一段时间就执行一次释放操作,找出那些不再继续使用的值,然后释放其占用的内存。
局部变量和全局变量的销毁
1、局部变量:局部作用域中,当函数执行完毕,局部变量也就没有存在的必要了,因此垃圾收集器很容易做出判断并回收。
2、全局变量:全局变量什么时候需要自动释放内存空间则很难判断,所以在开发中尽量避免使用全局变量。
垃圾回收算法
对垃圾回收算法来说,核心思想就是如何判断内存已经不再使用,常用垃圾回收算法有下面两种。
1、引用计数
2、标记清除(常用)
(1)引用计数算法定义“内存不再使用”的标准很简单,就是看一个对象是否有指向它的引用。如果没有其他对象指向它了,说明该对象已经不再需要了。
引用计数有一个致命的问题,那就是循环引用
如果两个对象相互引用,尽管他们已不再使用,但是垃圾回收器不会进行回收,最终可能会导致内存泄露。
例:
function fn() {
var n1 = {};
var n2 = {};
n1.x = n2;
ne.x = n1;
}
fn();
fn
函数执行完成之后,对象n1和n2
实际上已经不再需要了,但根据引用计数的原则,他们之间的相互引用依然存在,因此这部分内存不会被回收。所以现代浏览器不再使用这个算法。
但是IE依旧在使用。
(2)标记清除算法将“不再使用的对象”定义为“无法到达的对象”。即从根部(在JS中就是全局对象)出发定时扫描内存中的对象,凡是能从根部到达的对象,保留。那些从根部出发无法触及到的对象被标记为不再使用,稍后进行回收。
无法触及的对象包含了没有引用的对象这个概念,但反之未必成立。
所以现在对于主流浏览器来说,只需要切断需要回收的对象与根部的联系。最常见的内存泄露一般都与DOM元素绑定有关:
内存泄漏
对于持续运行的服务进程,必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。 对于不再用到的内存,没有及时释放,就叫做内存泄漏。
昨天的问题
(1)
对于问题1,num1、num2都是基本类型,它们的值是存储在栈中的,num1、num2分别有各自独立的栈空间,所以修改了num2的值以后,num1的值并不会发生变化。
var num1 = 10;
var num2 = num1;
num2 = 2;
console.log(num1) // 10
// 此时num1的值为多少
(2)
优先级。.
的优先级高于=
,所以先执行a.x
,堆内存中的{n: 1}
就会变成{n: 1, x: undefined}
,改变之后相应的b.x
也变化了,因为指向的是同一个对象。
赋值操作是从右到左
,所以先执行a = {n: 2}
,a
的引用就被改变了,然后这个返回值又赋值给了a.x
,需要注意的是这时候a.x
是第一步中的{n: 1, x: undefined}
那个对象,其实就是b.x
,相当于b.x = {n: 2}
var obj1 = {num: 1};
var obj2 = obj1;
obj1.n = obj1 = {num: 2};
console.log(obj1.n) // 此时 obj1.n 的值是 undefind
console.log(obj2.n) // 此时 obj2.n 的值是 {num: 2}