javascript高级程序设计阅读收获(4.3)——垃圾回收

1.标记清理

  1. 当变量进入上下文,比如在函数内部申明一个变量时,这个变量会被加上只存在于上下文中的标记。
  2. 在上下文中的变量,逻辑上讲,永远不应该释放他们的内存,因为只要上下文中的代码在运行,就有可能用到他们。当变量离开上下文时,也会被加上离开上下文的标记。
  3. 垃圾回收程序运行的时候,会标记内存中储存的所有变量(标记的方式有很多中)。
  4. 然后它会将所有在上下文中的变量,以及被在上下文中的变量的标记去掉。
  5. 在此之后再被加上标记的变量就是待删除的了。因为任何在上下文中的变量都访问不到他们了。随后垃圾回收程序做一次内部清理,销毁带标记的所有制并收回他们的内存。

2.引用计数

  1. 会对每个值都记录它被引用的次数。申明变量并给他赋一个引用值时,这个值的引用数为1.
  2. 如果同一个值又被赋给另一个变量,那么引用数加一。
  3. 如果保存对该值引用的变量被其他值给覆盖了,那么引用数减1。
  4. 当一个值的引用数为0时,就说明没办法再访问到这个值了,因此可以安全的收回其内存。垃圾回收程序下次运行的时候就会释放引用数为0的值的内存。
    (当这个值没被其它的变量用的时候,这个值的引用数就减少,最后等于0的时候就可以被清理了,因为没人用它)
function problem(){
	let objectA = new Object();//申明变量并给他赋一个引用值的时候,这个值的引用数为1
	let objectB = new Object();//申明变量并给他赋一个引用值的时候,这个值的引用数为1
	objectA.someOtherObject = objectB;
	objectB.anotherObject = objectA;
}
  1. 在上面的例子中objectA和objectB通过各自的属性相互引用,一位置他们的引用数都是2。在引用计数下,objectA和objectB在函数结束后还会存在,因为他们的引用数永远不会变成0;如果函数被多次调用,则会导致大量的内存永远不会被释放。
let element = document.getElementById("some_element");
let myObject = new Object();
myObject.element = element;
element.someObject = myObject;
  1. 在上面的例子中,DOM对象(element)和一个javascript对象(myObject)之间制造了循环引用,因此DOM元素的内存永远不会被回收,即使它已经被从页面上删除了也是如此。
myObject.element = null;
element.someObject = null;
  1. 为了避免类似的循环引用问题,可以通过以上代码来清除前面的例子所建立的循环引用。

3.内存管理,优化性能

  1. 将内存占用量保存在一个较小的值可以让页面性能更好。如果数据不再必要,那么把他设置为null,从而释放其引用。
  2. 解除对一个值的引用并不会自动导致相关内存被回收。解除引用的关键在于确保相关的值已经不在上下文里了,因此它在下次垃圾回收时会被回收。
  3. 使用let和const申明变量,相比于使用var,可能会更早的让垃圾回收程序介入,尽早回收应该回收的内存。

4.内存泄露

function setName(){
	name = 'Jake';
}
  1. 上面的变量没有申明,解释器会把name当做window的属性来创建,只要window没有被清理,name属性就不会被消失。
let name = 'Jake';
setInterval(
	()=>{console.log(namne)}
	,100
)
  1. 上例中是一个定时器,只要定时器一直运行,定时器中用到的name也不会被清理,会一直占用内存。
let outer = function(){
	let name = 'Jack';
	return function(){
		return name;
	}
}
  1. 上面代码创建了一个内部闭包,只要返回的函数存在就不能清理name,因为闭包在一直引用着它。

5.静态分配与对象池

  1. 开发者无法直接控制什么时候开始收集垃圾,但可以间接控制触发垃圾回收的条件。
  2. 如果能够合理使用分配的内存,同时避免多余的垃圾回收,就可以保住因释放内存而损失的性能。
function add1(a,b){
	let result = new Vector();
	result.x = a.x + b.x;
	result.y = a.y + b.y;
	return result;
}
function add2(a,b,result){
	result.x = a.x + b.x;
	result.y = a.y + b.y;
	return result;
}
  1. 上面代码中,add1中创建了一个result变量,然后add1函数执行完后这个对象会被回收。如果频繁调用这个函数,会频繁的触发垃圾回收调度程序,导致性能受到影响。可以将result变量作为参数,这意味着在调用这个函数的地方仍然会创建result这个对象。
  2. 我们可以通过对象池创建这个对象,应用程序可以向这个对象池请求一个对象,设置值,使用它,操作完后再把它归还给对象池。因为对象早就存在于对象池,所以垃圾回收调度不会探测到有对象更替,垃圾回收程序就不会那么频繁的执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木子 旭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值