低级语言,例如C,有低级内存管理命令,例如:()和free(),需要开发人员手动释放内存。 然而,在像这样的高级语言中情况就不同了。 对象(等)在创建时分配内存。 当不再使用它们时,内存会自动回收。 这种自动回收过程称为垃圾收集。 由于垃圾回收的存在,高级语言开发人员和其他开发人员产生了错误的认识,认为不需要关心内存管理。
内存生命周期
无论何种编程语言,内存的生命周期基本相同。
1.分配你需要的内存
2.利用他进行读写操作
3、不再需要内存时释放资源
步骤1和2对于所有语言都是相同的,可以清楚地注意到。 至于第3步,低级语言需要开发人员显式地执行此操作。 对于像这样的高级语言,这部分操作是由解析器完成的,所以你不会注意到。
分配操作
值初始化
当给变量赋值时,内存分配就完成了。
复制代码代码如下:
var n = 123; // 为数字分配内存
var s = ""; // 为字符串分配内存
var o = {
答:1,
b:空
}; // 为包含属性值的对象分配内存
var a = [1, null, "abra"]; // 为包含值的数组分配内存
F A){
一个+ 2;
} // 为函数分配内存(函数是一个可调用对象)
// 函数表达式也是对象,可以分配内存。
。('点击', (){
。风格。 =“蓝色”;
}, 错误的);
分配是通过函数调用完成的
执行某些函数后,还会发生对象分配。
复制代码代码如下:
var d = new Date();
var e = .('div'); // 分配一个 DOM 元素
有些方法分配新值或对象。
复制代码代码如下:
变量=“”;
var s2 = s.(0, 3); // s2 是一个新字符串
// 由于字符串不变,因此将为 [0, 3] 范围内的内容创建一个新字符串
var a = ["ouais ouais", "楠楠"];
var a2 = ["", "楠楠"];
var a3 = a.(a2); // 将a和a2组合起来创建一个新数组
价值观的运用
值的使用实际上就是对分配的内存进行读写操作。 这些操作包括:对变量或对象属性的读写操作,或者向函数传递参数。
不再需要时释放内存
大多数内存管理问题都发生在这个阶段。 最难的事情是如何确定何时不再需要分配的内存。 这往往需要开发人员确定程序何时不再需要内存并释放其占用的资源。
高级语言解析器中嵌入了一个称为“垃圾收集器”的程序。 它的工作是跟踪内存的分配和使用情况,判断是否需要该内存,当不再需要时执行资源释放操作。 他只能得到一个近似值,因为确定是否需要内存是一个不确定的问题(无法通过算法解决)。
垃圾收集
如上所述,我们无法自动准确地确定“不再需要内存”。 因此,垃圾收集作为该问题的解决方案具有局限性。 本节解释了理解主要垃圾收集算法及其局限性所需的概念。
引用
垃圾收集中的一个主要概念是引用。 在内存管理中,当一个对象使用另一个对象时,无论是显式还是隐式,我们都说它引用了另一个对象。 例如,对象具有对其原型的隐式引用和对其属性值的显式引用。
这里,对象的概念超出了传统的对象概念,还包括函数作用域和全局作用域。
使用引用计数算法进行垃圾收集
下面介绍的是一种最优算法,引入了“不再需要该对象”和“没有其他对象引用该对象”的概念。 当对象的引用指针变为0时,就认为它准备好回收了。
例子:
复制代码代码如下:
var o = {
A: {
乙:2
}; // 创建了两个对象。 一个对象(a)被另一个对象(o引用的对象)引用,并使用a作为其属性
// 该对象也被变量 o 引用
//显然,此时没有对象可以被回收
var o2 = o; // 变量o2再次引用该对象
o = 1; // o 不再引用该对象,只有 o2 仍然引用该对象
var oa = o2.a; // oa指的是o2的属性对象a
// 这个对象被另外两个对象引用,即属性a和o2的oa变量
o2 =“哟”; // 该对象不再被其他对象引用,但它的属性a仍然被oa变量引用,所以还不能释放
oa=空; // 现在属性a不再被其他对象引用,该对象可以被回收
限制:循环
该算法有其局限性。 当一个对象引用另一个对象时,形成循环引用,即使不再需要,垃圾收集器也不会回收它们。
复制代码代码如下:
F(){
var o = {};
var o2 = {};
oa=o2; // o 指的是 o2
o2.a = o; // o2 指的是 o
“”;
F();
// 创建两个对象并互相引用
// 函数调用结束后,不会离开函数作用域。 虽然不会被使用,但也不会被释放。
// 这是因为引用计数算法决定了只要对象被引用,就不能对其进行垃圾回收。
现实生活中的例子
在IE6和7中,对DOM对象使用引用计数算法,会出现内存泄漏问题。
复制代码代码如下:
var div = .("div");
分区 =(){
();
}; // div通过click属性引用事件处理程序
// 在事件处理函数中访问div变量时,会形成循环引用,会导致两个对象都无法回收,造成内存泄漏。
标记-清除算法
他引入了“对象不再需要”和“对象不可达( )”的概念。 该算法假设存在一系列根对象(中的根对象是全局对象)。 每隔一段时间,垃圾收集器就会从根对象开始,遍历它引用的所有对象,然后遍历引用对象所引用的对象。 等等。 使用这种方法,垃圾收集器可以获得所有可访问的对象并回收那些不可访问的对象。
这个算法比之前的算法要好。 0 引用的对象将被设置为不可访问的对象。 同时也避免了循环引用带来的麻烦。
截至 2012 年,大多数现代浏览器都使用这种“标记和清除”垃圾收集器。 垃圾收集(分代/增量/并发/并行垃圾收集)领域在过去几年中改进了与之相关的算法,但是垃圾收集算法本身(标记-清除算法)以及“如何确定一个对象是否是不再需要了” “并没有改善。
周期时间不再是问题
第一个例子中,函数调用结束后,这两个对象将不会被全局对象引用,也不会被全局对象引用的对象引用。 因此,它们被垃圾收集器标记为不可访问的对象。 同样的事情发生在第二个示例中,当 div 和事件处理程序被垃圾收集器标记为不可访问时,它们将被释放。
限制:对象需要显式标记为不可访问
这种标记方式有局限性,但我们在编程中没有接触过,所以很少关心垃圾收集相关的内容。