内存管理和内存泄露(闭包、作用域链)(三)

简介: 内存管理和内存泄露(闭包、作用域链)

内存管理和内存泄露(闭包、作用域链)(二)https://developer.aliyun.com/article/1470366

闭包的执行过程

以下是我们已经非常熟悉的闭包过程,这次我们来看下他是怎么进行执行的,这次会解开我们之前还不了解的,为什么闭包会让本该执行完的执行上下文的自由变量不会被销毁掉

执行之前一样是非常熟悉的流程,直接上图啦

image.png

当foo开始执行之后

image.png

当foo执行完了之后:这个时候,bar的内存地址已经存放到fn中了(也就是fn已经指向bar了),并且在后续被fn()给调用了,所以不管foo的函数执行上下文有没有被销毁,都不会影响到bar的函数对象了(因为GO根对象的fn已经指向了bar函数对象了上面有介绍JavaScript的垃圾回收,也就是标记清除部分,让bar函数对象不被销毁),然后bar函数对象连锁反应又跟foo的AO对象相互进行引用了(最关键的是bar指向foo的AO对象,这是可达的部分),所以foo的AO对象也不会被销毁。这就是为什么bar引用的父级自由变量会得以保留的原因

image.png

我们接下来就要继续执行fn的函数执行上下文(bar的)了

image.png

image.png

当bar的执行上下文被销毁掉的时候,也不会影响闭包,因为根对象依旧指向着fn,也就是bar的函数对象,而bar函数对象的父级作用域parentScope指着foo的AO对象,所以脱离了捕捉时的上下文,它也能照常运行。自由变量依旧存在而没有被销毁

image.png

function foo(){
    var name = "xiaoyu"
    var age = 20
    function test(){
        console.log("这是我的名字",name);
        console.log("这是我的年龄",age);
    }
    return test
}
var fn = foo()
fn()
//这是我的名字 xiaoyu
//这是我的年龄 20

04_函数执行作用域链和深入闭包

闭包的内存泄漏

从上面的代码块中,我们可以知道,当bar函数不被销毁的时候,foo的AO对象就永远不会被销毁,因为我们bar要访问foo的AO对象里面的内容


  • 目前因为在全局作用域下fn变量对0xb00的函数对象有引用,而0xb00的作用域中AO(0x200)有引用,所以会造成这些内存都是无法被释放的


但如果我们的bar函数只执行一次,后面就再也不需要了,那这个AO对象一直保存着就没有意义了,该销毁的却一直保留着,我们就叫这个是内存泄漏

闭包内存泄漏案例

image.png

image.png

  • 只要arrayFns数组不被销毁,则createFnArray函数也会一直保留着不被销毁

V8引擎源码可以看到对数字的处理:(是在后面回顾的时候进行补充说明的)

image.png

function createFnArray(){
    // 创建一个长度为1024*1024的数组,往里面每个位置填充1.观察占了多少的内存空间(int类型,整数1占4个字节byte)
    //4byte*1024=4kb,再*1024为4mb,占据的空间是4M × 100 + 其他的内存 = 400M+
    //在js里面不管是整数类型还是浮点数类型,看起来都是数字类型,这个时候占据的都是8字节,但是js引擎为了提高空间的利用率,对很多小的数字是用不到8个字节(byte)的,8字节 = 2的64次方,所以8字节是很大的,现在的js引擎大多数都会进行优化,对小的数字类型,在V8中称为Smi,小数字 2的32次方
    var arr = new Array(1024*1024).fill(1)
    
    return function(){
        console.log(arr.length);
    }
}
//var arrayFn = createFnArray()
//arrayFn()
var arrayFns = []
for(var i = 0 ; i<100 ; i++){
    //createFnArray()//我们通过for循环不断调用createFnArray这个函数,我们没有使用任何函数去接收他,所以当他创建进入下一个循环之后就会马上被销毁掉
    arrayFns.push(createFnArray())
}

image.png

内存泄漏解决方法

image.png

//内存泄漏解决方法
function foo(){
    var name = "xiaoyu"
    var age = 20
    function test(){
        console.log("这是我的名字",name);
        console.log("这是我的年龄",age);
    }
    return test
}
var fn = foo()
fn()
fn = null//将fn指向null,null的内存地址为0x0。此时fn指向bar的指针就会断开了,AO对象跟bar函数对象就形成了一个对于根对象的不可达的对象,将再下次被销毁掉。注意,你把它置为null之后,不会马上回收的,会在发现之后的下一轮进行回收

AO不使用的属性

  • 我们来研究一个问题:AO对象不会被销毁时,是否里面的所有属性都不会被释放?
  • 下面代码中的name属于闭包的父作用域里面的变量
  • 我们知道形成闭包之后count一定不会被销毁掉,那么name是否会被销毁掉呢?会,没有被使用到的会销毁掉,V8引擎做的优化
function makeAdder(count){
    let name ="why" 
    return function (num){
        debugger
        return count + num
    }
}
const add10 = makeAdder(10)
console.log(add10(5));
console.log(add10(8));
//15
//18

闭包的内存泄漏测试

内存回收案例测试如下

image.png

如果我们连foo函数对象都不想要了,我们也来个foo = null,断掉了foo与根对象GO的联系,那下次foo函数也会被销毁,或者说垃圾回收掉


image.png

回收一半的内存

JS闭包引用的自由变量销毁

当我们除了声明了fn来接收foo()之外,又声明了baz同样子接收foo(),这个时候是又执行了一遍foo函数里面的bar部分,fn跟baz不是同时指向同一个地方,而是又创建了一个新的foo的AO对象跟bar的函数对象,当我们将fn指向null,将内存进行回收时的时候,销毁的也只是fn对应的bar函数对象跟foo()对象,而对baz产生的bar函数对象跟foo的AO对象没有任何的影响,毕竟baz是又重新走了一遍流程,baz跟fn是互相独立的(PS:foo的AO对象是由bar的父级作用域内存地址指向而产生出来的)

image.png

  1. foo的AO对象有bar在指向着,因为bar函数内含父级作用域foo的AO对象的内存地址且正处于引用状态,这个内存地址指向着AO对象,让AO对象不会被销毁掉,但是我们只是引用name这个自由变量,age并没有使用到,按照ECMA规范,正规AO对象都不会被销毁,当然也就包含了我们没有用上的age变量了
  2. 但是js引擎是非常灵活的,为了提高内存的利用率,这个可能永远使用不上的age属性是会被回收掉的,从而提高空余的内存空间,提高性能

image.png

闭包引用的AO对象属性销毁

通过debugger我们可以看到未使用的父级作用域的变量会被js引擎回收掉,如果引用了则不会

function foo(){
    var name = "why"
    var age = 18
    function bar(){
        debugger
        console.log(name)
    }
    return bar
    
}
var fn = foo()
fn()

image.png

目录
相关文章
|
1月前
|
存储 监控 算法
Java内存管理深度剖析:从垃圾收集到内存泄漏的全面指南####
本文深入探讨了Java虚拟机(JVM)中的内存管理机制,特别是垃圾收集(GC)的工作原理及其调优策略。不同于传统的摘要概述,本文将通过实际案例分析,揭示内存泄漏的根源与预防措施,为开发者提供实战中的优化建议,旨在帮助读者构建高效、稳定的Java应用。 ####
39 8
|
2月前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
54 6
|
2月前
|
存储 JavaScript 前端开发
如何优化代码以避免闭包引起的内存泄露
本文介绍了闭包引起内存泄露的原因,并提供了几种优化代码的策略,帮助开发者有效避免内存泄露问题,提升应用性能。
|
2月前
|
Web App开发 缓存 JavaScript
如何检测和解决闭包引起的内存泄露
闭包引起的内存泄露是JavaScript开发中常见的问题。本文介绍了闭包导致内存泄露的原因,以及如何通过工具检测和代码优化来解决这些问题。
|
3月前
|
存储 程序员 编译器
C语言——动态内存管理与内存操作函数
C语言——动态内存管理与内存操作函数
|
3月前
|
存储 缓存 监控
深入了解MySQL内存管理:如何查看MySQL使用的内存
深入了解MySQL内存管理:如何查看MySQL使用的内存
476 1
|
3月前
|
存储 安全 程序员
【C++篇】深入内存迷宫:C/C++ 高效内存管理全揭秘
【C++篇】深入内存迷宫:C/C++ 高效内存管理全揭秘
117 3
|
4月前
|
Java
在 ArkTS 中,如何有效地进行内存管理和避免内存泄漏?
【9月更文挑战第25天】在ArkTS中,有效进行内存管理并避免内存泄漏的方法包括:及时释放不再使用的资源,如关闭监听器和清理定时器;避免循环引用,通过弱引用打破循环;合理使用单例模式,确保单例对象正确释放;及时处理不再使用的页面和组件,在卸载时清理相关资源。
153 9
|
3月前
|
JavaScript 前端开发 安全
如何避免闭包带来的内存消耗呢
【10月更文挑战第12天】如何避免闭包带来的内存消耗呢
44 0
|
3月前
|
Java C语言 iOS开发
MacOS环境-手写操作系统-16-内存管理 解析内存状态
MacOS环境-手写操作系统-16-内存管理 解析内存状态
55 0

热门文章

最新文章