JavaScript闭包(内存泄漏、溢出以及内存回收),超直白解析
1 引言
变量作用域
首先我们先铺垫一个知识点——变量作用域:
变量根据作用域的不同分为两种:全局变量和局部变量。
- 函数内部可以使用全局变量。
- 函数外部不可以使用局部变量。
- 当函数执行完毕,本作用域内的局部变量会销毁。
如果我想在函数外部引用这个函数的局部变量呢?
2 闭包
闭包是什么?
闭包(closure)指有权访问另一个函数作用域中变量的函数。 ----- JavaScript 高级程序设计
闭包有什么用?
1延伸变量作用域范围,读取函数内部的变量
2让这些变量的值始终保持在内存中
简单理解就是 ,一个作用域可以访问另外一个函数内部的局部变量。
闭包案例一
我们来看一个简单的闭包案例。
function fn1() {
var num = 10;
function fn2() {
console.log(num);
}
fn2();
}
fn1(); //输出结果10
这样的一个函数写法我们已经见过或者用过很多次了,但其实这就是一个闭包的运用。
我们可以用Chrome的调试工具验证一下。
如图(看不清图片的伙伴们可以把图片放大)
在 Scope 选项(Scope 作用域的意思)中,有两个参数(global 全局作用域、local 局部作用域)。我在fn1函数调用的前面(21行)中设置了一个断点,进行单步调试,当执行到 fn2() 时,Scope 里面会多一个 Closure (闭包)参数 ,这就表明产生了闭包。被访问的变量是num,包含num的函数为fn1。
fn2的作用域当中访问到了fn1函数中的num这个局部变量 ,所以此时fn1 就是一个闭包函数(被访问的变量所在的函数就是一个闭包函数)
也有人说,闭包是一种现象,一个作用域访问了另外一个函数中的局部变量,如果有这种现象的产生,就有了闭包的发生。 我觉的这样理解也是没有什么问题的。
闭包案例二
接下来我们来看一个稍微复杂一点点的闭包
function fn() {
var num = 10;
return function() {
console.log(num);
}
}
var f = fn();
// 上面这步类似于
// var f = function() {
// console.log(num);
// }
f();//输出结果10
在f=fn()这步操作中,执行了num =10 的赋值,并且给f赋值了一个匿名函数,这个函数是fn中return 返回的那个匿名函数,注意此时只是赋值了,并没有调用。
然后在f()中调用了那个匿名函数,此时我们便做到了在 fn() 函数外面访问 fn() 中的局部变量 num 。
$闭包延伸了变量作用域范围,读取了函数内部的变量$
闭包案例三
首先我们看一下这个闭包案例
var fn =function(){
var sum = 0
return function(){
sum++
console.log(sum);
}
}
fn()() //1
fn()() //1
//fn()进行sum变量申明并且返回一个匿名函数,第二个()意思是执行这个匿名函数
这里出现了一个小问题,sum为什么没有自增?如果想要实现自增怎么操作?
回答这个问题需要先了解一下js中内存回收机制。(详细内容可以看文章后面的3 Js内存回收机制)
我这里直接简单解释一下,执行fn()() 后,fn()()已经执行完毕,没有其他资源在引用fn,此时内存回收机制会认为fn不需要了,就会在内存中释放它。
那如何不被回收呢?
var fn =function(){
var sum = 0
return function(){
sum++
console.log(sum);
}
}
fn1=fn()
fn1() //1
fn1() //2
fn1() //3
这种情况下,fn1一直在引用fn(),此时内存就不会被释放,就能实现值的累加。那么问题又来了,这样的函数如果太多,就会造成内存泄漏(内存泄漏、内存溢出的知识点在文章后面4 内存溢出、内存泄漏)
内存泄漏了怎么办呢?我们可以手动释放一下
var fn =function(){
var sum = 0
return function(){
sum++
console.log(sum);
}
}
fn1=fn()
fn1() //1
fn1() //2
fn1() //3
fn1 = null // fn1的引用fn被手动释放了
fn1=fn() //num再次归零
fn1() //1
3 Js内存回收机制
由于字符串、对象和数组没有固定大小,当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。
现在各大浏览器通常用采用的垃圾回收有两种方法:标记清除、引用计数。
1标记清除
这是javascript中最常用的垃圾回收方式。当变量进入执行环境是,就标记这个变量为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。当变量离开环境时,则将其标记为“离开环境”。
2引用计数
引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。
4 内存溢出、内存泄漏
内存溢出
内存溢出一般是指执行程序时,程序会向系统申请一定大小的内存,当系统现在的实际内存少于需要的内存时,就会造成内存溢出
内存溢出造成的结果是先前保存的数据会被覆盖或者后来的数据会没地方存
内存泄漏
内存泄漏是指程序执行时,一些变量没有及时释放,一直占用着内存
而这种占用内存的行为就叫做内存泄漏。
作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积。
内存泄漏如果一直堆积,最终会导致内存溢出问题
5 总结
- 闭包是什么?
闭包是一个函数 (一个作用域可以访问另外一个函数的局部变量)
- 闭包的作用是什么?
1延伸变量作用域范围,读取函数内部的变量
2让这些变量的值始终保持在内存中
JavaScript闭包(内存泄漏、溢出以及内存回收),超直白解析的更多相关文章
- JS内存泄漏 和Chrome 内存分析工具简介(摘)
原文地址:http://web.jobbole.com/88463/ JavaScript 中 4 种常见的内存泄露陷阱 原文:Sebastián Peyrott 译文:伯乐在线专栏作者 - AR ...
- Android 性能优化之内存泄漏检测以及内存优化(中)
https://blog.csdn.net/self_study/article/details/66969064 上篇博客我们写到了 Java/Android 内存的分配以及相关 GC 的详细分析, ...
- 内存泄漏 Memory Leaks 内存优化 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- 使用valgrind进行内存泄漏和非法内存操作检测
valgrind是一个强大的工具,最常用的功能是用它来检测内存泄漏和非法内存的使用.要想让valgrind报告的更加细致,请使用-g进行编译. 基本命令如下: $ valgrind --tool=me ...
- 使用 Android Studio 检测内存泄漏与解决内存泄漏问题
本文在腾讯技术推文上 修改 发布. http://wetest.qq.com/lab/view/63.html?from=ads_test2_qqtips&sessionUserType=BF ...
- JNI创建共享内存导致JVM terminated的问题解决(segfault,shared memory,内存越界,内存泄漏,共享内存)
此问题研究了将近一个月,最终发现由于JNI不支持C中创建共享内存而导致虚拟机无法识别这块共享内存,造成内存冲突,最终虚拟机崩溃. 注意:JNI的C部分所使用的内存也是由JVM创建并管理的,所以C创建了 ...
- 记录一个问题:macos High Sierra 10.13.6 内核内存泄漏,导致内存满而不得不重启
kernel_task进程占用内存10g以上,使用中突然提示内存不足,要求杀死工作进程,不得不强按电源键来关机重启. 升级之前,版本大约是macos High Sierra 10.13.4, 系统频繁 ...
- 了解 JavaScript 应用程序中的内存泄漏
简介 当处理 JavaScript 这样的脚本语言时,很容易忘记每个对象.类.字符串.数字和方法都需要分配和保留内存.语言和运行时的垃圾回收器隐藏了内存分配和释放的具体细节. 许多功能无需考虑内存管理 ...
- JavaScript内存泄漏知多少?
垃圾回收解放了我们,它让我们可将精力集中在应用程序逻辑(而不是内存管理)上.但是,垃圾收集并不神奇.了解它的工作原理,以及如何使它保留本应在很久以前释放的内存,就可以实现更快更可靠的应用程序.在本文中 ...
随机推荐
- PHP xml_get_current_byte_index() 函数
定义和用法 xml_get_current_byte_index() 函数获取 XML 解析器的当前字节索引.高佣联盟 www.cgewang.com 如果成功,该函数则返回当前字节索引.如果失败,则 ...
- PDOStatement::setAttribute
PDOStatement::setAttribute — 设置一个语句属性(PHP 5 >= 5.1.0, PECL pdo >= 0.2.0)高佣联盟 www.cgewang.com 说 ...
- Blob分析之board _components.hdev
*用立体方法分割板子组件的示例程序*Application program to illustrate the segmentation* of board _components.hdev wit ...
- recv & recvfrom
#include <sys/types.h> #include <sys/socket.h> ssize_t recv(int sockfd, void *buf, size_ ...
- log4net用法实例
内容转载自:https://www.cnblogs.com/youring2/archive/2011/04/27/2030424.html 1.引用log4net.dll 2.在AssemblyIn ...
- 用 Python 写个消消乐小游戏
提到开心消消乐这款小游戏,相信大家都不陌生,其曾在 2015 年获得过玩家最喜爱的移动单机游戏奖,受欢迎程度可见一斑,本文我们使用 Python 来做个简单的消消乐小游戏. 很多人学习python,不 ...
- Python实现 QQ 半自动发送情话,我追到了女神
之前追女神时候用的情话发送脚本,每次工作日上班开启电脑,就会唤醒QQ,然后发送一条情话给女神,自动唤醒QQ复制情话,不用担心遗忘,现在整理分享一下,妈妈再也不用担心我找不到对象了. 效果图: 很多人学 ...
- data argumentation 数据增强汇总
几何变换 flip:水平翻转,也叫镜像:垂直翻转 rotation:图片旋转一定的角度,这个可以通过opencv来操作,各个框架也有自己的算子 crop:随机裁剪,比如说,在ImageNet中可以将输 ...
- 【JVM】JVM优化过程全记录
请大神移步:https://segmentfault.com/a/1190000010510968?utm_source=tuicool&utm_medium=referral 今天看JVM群 ...
- java web应用启动报错:Several ports (8080, 8009) required by Tomcat v6.0 Server at localhost are already in use.
Several ports (8080, 8009) required by Tomcat v6.0 Server at localhost are already in use. The serve ...