垃圾回收机制代码实例讲解
垃圾回收
垃圾收集机制:找出那些不再继续使用的变量,然后释放其占用的空间。垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间),周期性地执行这一操作。
标记清除
标记清除是最常用的垃圾收集方式。
当变量进入环境(如在函数内声明了一个变量)时,就将其标记为“进入环境”。进入环境的变量,其内存不能被释放。当变量离开环境时(函数执行完毕,闭包除外),就将其标记为“离开环境”。
在函数内声明一个变量—变量为进入环境的变量
函数执行完毕–变量为离开环境的变量
垃圾收集器运行时会先为所有存储在内存中的变量加上标记。然后再去掉那些进入环境的变量和被环境中的变量引用的变量的标记。之后若这些去掉标记的变量若再被加上标记,那么它们就被视为将要删除的变量,因为环境中的变量已经访问不到这些变量了。最后,垃圾收集器就销毁带标记的变量并回收它们占用的内存空间。
引用计数
引用计数的含义是记录每个值被引用的次数。
当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是 1。如果同一个值又被赋给另一个变量,则该值的引用次数加 1。
相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减 1。
当这个值的引用次数变成 0 时,就说明没有变量来引用了这值了,说明不再需要这个值,因而就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。
// 创建一个对象person,栈内存中的变量person指向堆内存中的对象。 var person = { age: 22, name: 'ifcode' }; person.name = null; // 把对象的name设置为null,但变量person仍然指向对内存中的对象,该对象不会回收,对象的name属性也就不会被回收。 var person1 = person; person = {name:"Alex"}; //重写了person,变量person在堆内存中的引用改变,但是变量person1仍然保持着对原person对象的引用,因此原person对象不会被回收。 person1 = null; //person1也被重写了,它不再对原person对象进行引用,原person对象很快会被回收。
引用计数存在的问题:循环引用
如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄露。
内存泄露指一块被分配的内存,即不能使用,又不能被回收,这样泄露越多,占用的内存就越多,最终使应用崩溃。
function cycle() { var o1 = {}; var o2 = {}; o1.a = o2; o2.a = o1; return "Cycle reference!" } cycle();
cycle函数内包含两个相互引用的对象。在调用函数结束后,对象o1和o2就不再需要了。但根据引用计数的原则,他们之间的相互引用依然存在,因此这部分内存不会被回收,导致内存泄露。
如果要解决循环引用带来的内存泄露问题,只需要把循环引用中的变量设为 null
即可。将变量设置为 null 意味着切断变量与它此前引用的值之间的连接。
上一篇: [.NET] 浅谈可扩展性框架:MEF