内存生命周期
1 .分配需要的内存
1 .js自己进行内存分配同时声明值,在解析定义的变量的时候就在分配内存
2 .为数字分配内存
3 .为字符串分配内存
4 .为对象及其包含值分配内存
5 .为数组及其包含的值分配内存
6 .为函数分配内存
7 .为函数表达式分配一个对象 someElement.addEventListener('click',()=>{})
8 .某些方法也会导致对象的内存分配:为日期分配对象,为dom节点分配对象
9 .一些数组或者对象的方法调用也会导致内存的重新分配
10 .
2 .使用内存读写
3 .不需要时将其释放/归还
1 .最难的地方时确定何时不在需要分配的内存,它通常要求开发人员确定程序中哪些地方不在需要内存并释放它
2 .高级语言嵌入了一种垃圾回收机制,跟踪内存分配和使用,一边发现任何一块不需要的已分配内存,并自动释放
3 .
4 .c语言中,有专门的内存管理接口,mallos,free。js中是没有专门的内存管理接口,所有的内存管理都是自动的,js在创建变量时,自动分配内存,并在不使用的时候自动释放
js中的内存回收
1 .引用
1 .垃圾回收算法主要依赖于引用的概念。在内存管理环境中,一个对象如果有访问另一个对象的权限,叫做一个对象引用另一个对象
2 .例如一个js对象具有对他原型的引用,隐式调用,和对他属性的引用,显示调用
3 .循环引用的问题无法解决
2 .引用计数垃圾收集
1 .如果对象没有其他对象引用到他,对象将被垃圾回收机制回收
2 .arr=null
3 .
3 .循环使用
1 .两个对象被创建并相互引用,就造成了循环引用,他们调用之后不会离开函数作用域,现在他们已经没有了作用,可以被回收。但是引用计数算法考虑到他们互相都有至少一次引用,所以他们不会被回收
2 .function f() {
var o1 = {};
var o2 = {};
o1.p = o2; // o1 引用 o2
o2.p = o1; // o2 引用 o1. 这里会形成一个循环引用
}
f();
3 .实际例子
var div;
window.onload = function(){
div = document.getElementById("myDivElement");
div.circularReference = div;
div.lotsOfData = new Array(10000).join("*");
};
4 .标记-清除算法
1 .对象是否可以获得
2 .假定设置一个叫做根root的对象。定期的,垃圾回收器将从根开始,找所有根开始引用的对象,然后找这些对象引用的对象,从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象
3 .所有现代浏览器都使用了标记-清除内存回收算法。所有对js垃圾回收算法的改进都是基于标记-清除算法的改进
4 .最后,垃圾收集器释放所有未标记的为活动的内存块
5 .问题:内存回收是不可控制的。当分配一个非常大的数组,数组被标记为不可访问
5 .自动GC的问题
1 .虽然自动GC很方便,但是不知道什么时候GC会进行,这意味着如果我们在使用过程中使用了大量的内存,而gc没有运行的情况下,或者gc无法回收这些内存的情况下,程序就可能假死。所以我们需要在程序中手动做一些操作触发内存回收
2 .内存泄漏:不在被需要的内存,由于某种原因,无法被释放
3 .
常见的内存泄露案例
1 .全局变量:在js中处理未被声明的变量,会被定义到全局对象中去,在浏览器中就是window,在页面中的全局变量,只有当页面被关闭后才会被销毁。所以就会造成内存泄露
function foo(arg) {
bar = "some text";
}
//此时泄露的只是个字符串。但是实际情况可能更加复杂
2 .意外创建全局变量的情况
function foo() {
this.var1 = "potential accidental global";
}
// Foo 被调用时, this 指向全局变量(window)
foo();
//window意外创建了全局变量var1
// 如果我们使用这些全局变量来暂存大量的数据,一定要在使用之后,对其重新赋值为null
3 .为销毁的定时器和回调函数
1 .如果没有回收计时器,整个第十七依然有效,不但定时器无法被内存回收,定时器中的依赖也无法回收。
2 .
4 .闭包
1 .闭包之间共享作用域,如果这个函数反复执行时,内存会持续增长
5 .dom引用
1 .把对dom的引用保存在一个数组或者Map中
2 .这个时候虽然我们进行了元素移除,但是任然有对元素的引用,那么任然是无法进行内存回收的
3 .对于一个dom树的叶子节点的引用。如果我们引用了一个子节点的元素,但是删除了整个父元素,直观的觉得内存回收应该回收除了被引用的子元素外的其他元素,但是实际上。改子元素是整个父元素的一个子元素,还保留着对父元素的引用。这就会导致整个父元素都不会被回收
WeakSet,WeakMap
1 .在weakset,weakmap中,可以解决内存回收问题,他们对于值的引用可以忽略不计,是弱引用,内存回收机制不会考虑这种引用,当其他引用被消除后,引用就会从内存中被释放。
2 .所以之后操作dom的数据结构都存在这种结构里面算了
3 .主动删除与业务无关的变量
第二波
1 .计算机内存由大量的触发器缓存而成,每个触发器包含几个晶体管。单个触发器都可以通过唯一的标识符寻址,因此我们可以读取和覆盖他们。
2 .程序使用的所有变量和其他数据。程序的代码,包括操作系统的代码都存在内存中
3 .编译代码的时候,编译器可以检查基本数据类型,并提前计算他们需要多少内存。然后将所需大小分配给调用堆栈空间中的程序,分配这些变量的空间就是堆栈空间。当调用空间的时候,他们的内存被添加在现有内存之上,当他们终止时,他们按照后进先出的顺序被移除
4 .编译器是直到每个变量的确切内存地址的。事实上,每当我们写入变量的时候,内部都会被转换成类似 内存地址4127963这样的信息
5 .当函数调用其他函数时,每个函数在调用堆栈时获得自己的块,他保存所有的局部变量,但是也会有一个程序计数器来记住他在执行过程中的位置。当函数完成时,他的内存块将再次用于其他地方
6 .动态编译
1 .当编译时不知道一个变量需要多少内存时,事情就会变得非常复杂了
2 .他的大小不需要在编译的时候知道
3 .在运行的时候执行
4 .分配给堆
5 .