【易客吧】_全网激活码总代_激活码商城

您现在的位置是:首页 > 热门资讯 > 正文

热门资讯

深入了解 JavaScript 废品回收:揭示内存管理的奥秘 (深入了解javascript特性读后感)

用户投稿2024-04-12热门资讯21
深入了解 JavaScript 废品回收:揭示内存管理的奥秘导言JavaScript 是一种强大的编程语言,在 Web 开发中无处不在。理解 JavaScript 的内存管理机制,特别是其废品回收系统,对于开发健壮可靠的应用程序至关重要。本篇文章将深入探讨 JavaScript 废品回收,揭示其运作方式以及如何管理内存分配和释放任务。JavaScript 内存管理JavaScript 采用自动内存管理机制,这意味着开发人员不必手动管理内存分配和释放。该任务由 JavaScript 废品回收器负责,它是一个在后台运行的特殊算法。废品回收概述废品回收的主要目标是识别并回收不再被程序使用的内存空间。它通过以下步骤实现:1. 标记:废品回收器首先扫描内存,并标记所有仍被程序引用的对象。 2. 清理:标记完成后,废品回收器清理所有未标记的对象,释放其占用的内存。废品回收算法JavaScript 废品回收器使用两种主要的算法:标记清除算法:这种算法逐个遍历内存,标记仍然可达的对象。标记完成之后,清除所有未标记的对象。分代收集算法:这种算法将对象分为不同代(例如,年轻代和老年代)。它优先清理年轻代对象,因为它们更有可能不再被使用。触发废品回收废品回收是由 JavaScript 引擎自动触发的。但是,开发人员也可以手动触发废品回收,使用 `gc()` 函数。不过,通常情况下,自动废品回收足以应付大多数场景。优化废品回收为了提高 JavaScript 废品回收的效率,可以通过以下最佳实践进行优化:避免内存泄漏:确保对象不再需要时释放其引用。例如,使用弱引用或废品回收管理器清除循环引用。避免过度分配:尽可能减少内存分配,并释放不再需要的对象。这将减少废品回收的频率和开销。使用缓存:缓存经常访问的数据可以降低内存分配的开销。监视内存使用情况:使用工具(如 Chrome DevTools 或 Node.js 的 `process.memoryUsage()` 函数)监视内存使用情况,并采取必要措施优化代码。废品回收限制虽然废品回收提供了自动内存管理的便利,但它也有一些限制:暂停:废品回收是暂停操作,这意味着在废品回收运行期间代码执行将暂停。这可能会导致应用程序短暂冻结。开销:废品回收会消耗一些 CPU 和内存资源。在内存使用量较大或应用程序性能至关重要的场景中,这可能成为瓶颈。替代方案:在某些情况下,避免使用废品回收可能是有利的,例如:嵌入式系统:在资源受限的环境中,确定性内存管理可能更可取。高性能应用程序:对于需要避免暂停的应用程序,可以考虑手动内存管理或使用其他非废品回收语言。结论理解 JavaScript 废品回收是开发健壮可靠的 Web 应用程序的关键。通过遵循最佳实践并知道废品回收的限制,开发人员可以最大化其效率并避免潜在的性能问题。通过采用自动内存管理机制,JavaScript 为开发人员提供了创建无负载且易于维护的应用程序的能力。

JavaScript是如何工作的

关于JavaScript是如何工作的,本文我们将重点介绍引擎、运行时、调用栈。 第二篇揭示了谷歌V8 JavaScript引擎的内部机制,并且提供了一些关于如何写出更好的JavaScript代码的建议。 我们也会提供一些关于如何处理JavaScript内存泄露的技巧。 在SessionStack,我们需要确保不会造成内存泄露或者不会增加我们集成的Web应用的内存消耗。 概述某些语言,比如C有低级的原生内存管理原语,像 malloc() 和 free() 。 开发人员使用这些原语可以显式分配和释放操作系统的内存。 相对地,JavaScript会在创建变量(对象、字符串)时自动分配内存,并在这些变量不被使用时自动释放内存,这个过程被称为 废品回收 。 这个“自动”释放资源的特性带来了很多困惑,让JavaScript(和其他高级级语言)开发者误以为可以不关心内存管理。 这是一个很大的错误即使使用高级级语言,开发者也应该对于内存管理有一定的理解(至少有基本的理解)。 有时自动内存管理存在一些问题(例如废品回收实现可能存在缺陷或者不足),开发者必须弄明白这些问题,以便找一个合适解决方法。 内存生命周期无论你用哪一种编程语言,内存生命周期几乎总是一样的:Here is an overview of what happens at each step of the cycle:这是对生命周期中的每一步大概的说明:分配内存 — 内存是被操作系统分配,这允许程序使用它。 在低级语言中(例如C),这是一个作为开发者需要处理的显式操作。 在高级语言中,然而,这些操作都代替开发者进行了处理。 使用内存。 实际使用之前分配的内存,通过在代码操作变量对内在进行读和写。 释放内存 。 不用的时候,就可以释放内存,以便重新分配。 与分配内存操作一样,释放内存在低级语言中也需要显式操作。 想要快速的了解堆栈和内存的概念,可以阅读本系列第一篇文章。 什么是内存在直接探讨Javascript中的内存之前,我们先简要的讨论一下什么是内存、内存大概是怎么样工作的。 在硬件中,电脑的内存包含了大量的触发电路,每一个触发电路都包含一些<span>能够储存1位数据的</span>晶体管。 触发器通过 唯一标识符 来寻址,从而可以读取和覆盖它们。 因此,从概念上来讲,可以认为电脑内存是一个巨大的可读写阵列。 人类不善于把我们所有的思想和算术用位运算来表示,我们把这些小东西组织成一个大家伙,这些大家伙可以用来表现数字:8位是一个字节。 字节之上是字(16位、32位)。 许多东西被存储在内存中:所有的变量和程序中用到的数据;程序的代码,包括操作系统的代码。 编译器和操作系统共同工作帮助开发者完成大部分的内存管理,但是我们推荐你了解一下底层到底发生了什么。 编译代码的时候,编译器会解析原始数据类型,提前计算出它们需要多大的内存空间。 然后将所需的数量分配在 栈空间 中。 之所以称为栈空间,是因在函数被调用的时候,他们的内存被添加在现有内存之上(就是会在栈的最上面添加一个栈帧来指向存储函数内部变量的空间)。 终止的时候,以LIFO(后进先出)的顺序移除这些调用。 例如:int n; // 4字节int x[4]; // 4个元素的数组,每个元素4字节double m; // 8字节编译器马上知道需要内存4 + 4 × 4 + 8 = 28字节。 这是当前整型和双精度的大小。 大约20年以前,整型通常只需要2个字节,双精度需要4个字节,你的代码不受基础数据类型大小的限制。 编译器会插入与操作系统交互的代码,来请求栈中必要大小的字节来储存变量。 在上面的例子中,编辑器知道每个变量准确的地址。 事实上,无论什么时候我们写变量 n ,将会在内部被翻译成类似“memory address ”的语句。 注意,如果我们尝试访问 x[4] 的内存(开始声明的x[4]是长度为4的数组, x[4] 表示第五个元素),我们会访问m的数据。 那是因为我们正在访问一个数组里不存在的元素,m比数组中实际分配内存的最后一个元素 x[3] 要远4个字节,可能最后的结果是读取(或者覆盖)了 m 的一些位。 这肯定会对其他程序产生不希望产生的结果。 当函数调用其他函数的时候,每一个函数被调用的时候都会获得自己的栈块。 在自己的栈块里会保存函数内所有的变量,还有一个程序计数器会记录变量执行时所在的位置。 当函数执行完之后,会释放它的内存以作他用。 动态分配不幸的是,事情并不是那么简单,因为在编译的时候我们并不知道一个变量将会需要多少内存。 假设我们做了下面这样的事:int n = readInput(); //读取用户的输入...//创建一个有n个元素的数组编译器不知道这个数组需要多少内存,因为数组大小取决于用户提供的值。 因此,此时不能在栈上分配空间。 程序必须在运行时向操作系统请求够用的空间。 此时内存从 堆空间 中被分配。 静态与动态分配内存之间的不同在下面的表格中被总结出来:静态分配内存与动态分配内存的区别。 为了完全理解动态内存是如何分配的,我们需要花更多的时间在 指针 上,这个可能很大程度上偏离了这篇文章的主题。 如果你有兴趣学习更多的知识,那就在评论中让我知道,我就可以在之后的文章中写更多关于指针的细节。 JavaScript中的内存分配现在我们来解释JavaScript中的第一步( 分配内存 )是如何工作的。 JavaScript在开发者声明值的时候自动分配内存。 var n = 374; // 为数值分配内存var s = sessionstack; //为字符串分配内存var o = { a: 1, b: null}; //为对象和它包含的值分配内存var a = [1, null, str]; //为数组和它包含的值分配内存function f(a) { return a + 3;} //为函数(可调用的对象)分配内存//函数表达式也会分配一个对象(click, function() { = blue;}, false); //一些函数调用也会导致对象分配`var d = new Date(); // allocates a Date object` //分配一个Date对象的内存`var e = (p); //分配一个DOM元素的内存//方法可以分配新的值或者对象var s1 = sessionstack;var s2 = (0, 3); //s2是一个新的字符串// 因为字符串是不可变的// JavaScript可能决定不分配内存// 而仅仅存储 0-3的范围var a1 = [str1, str2];var a2 = [str3, str4];var a3 = (a2); //新的数组有4个元素是a1和a2连接起来的。 在JavaScript中使用内存在JavaScript中使用被分配的内存,本质上就是对内在的读和写。 比如,读、写变量的值或者对象的属性,抑或向一个函数传递参数。 内存不在被需要时释放内存大部分的内存管理问题都在这个阶段出现。 这里最难的任务是找出这些被分配的内存什么时候不再被需要。 这常常要求开发者去决定程序中的一段内存不在被需要而且释放它。 高级语言嵌入了一个叫 废品回收 的软件,它的工作是跟踪内存的分配和使用,以便于发现一些内存在一些情况下不再被需要,它将会自动地释放这些内存。 不幸的是,这个过程是一个近似的过程,因为一般关于知道内存是否是被需要的问题是不可判断的(不能用一个算法解决)。 大部分的废品回收器会收集不再被访问的内存,例如指向它的所有变量都在作用域之外。 然而,这是一组可以收集的内存空间的近似值。 因为在任何时候,一个内存地址可能还有一个在作用域里的变量指向它,但是它将不会被再次访问。 废品收集由于找到一些内存是否是“不再被需要的”这个事实是不可判定的,废品回收的实现存在局限性。 本节解释必要的概念去理解主要的废品回收算法和它们的局限性。 内存引用废品回收算法依赖的主要概念是 引用。 在内存管理的语境下,一个对象只要显式或隐式访问另一个对象,就可以说它引用了另一个对象。 例如,JavaScript对象引用其Prototype( 隐式引用 ),或者引用prototype对象的属性值( 显式引用 )。 在这种情况下,“对象”的概念扩展到比普通JavaScript对象更广的范围,并且还包含函数作用域。 (或者global 词法作用域 )词法作用域定义变量的名字在嵌套的函数中如何被解析:内部的函数包含了父级函数的作用域,即使父级函数已经返回。 引用计数废品回收这是最简单的废品回收算法。 一个对象在没有其他的引用指向它的时候就被认为“可被回收的”。 看一下下面的代码:var o1 = { o2: { x: 1 }};//2个对象被创建/o2被o1作为属性引用//谁也不能被回收var o3 = o1; //o3是第二个引用o1指向对象的变量o1 = 1; //现在,o1只有一个引用了,就是o3var o4 = o3.o2; // 引用o3对象的o2属性 //o2对象这时有2个引用: 一个是作为对象的属性 //另一个是o4o3 = 374; //o1原来的对象现在有0个对它的引用 //o1可以被废品回收了。 //然而它的o2属性依然被o4变量引用,所以o2不能被释放。 o4 = null; //最初o1中的o2属性没有被其他的引用了 //o2可以被废品回收了循环引用创造麻烦在涉及循环引用的时候有一个限制。 在下面的例子中,两个对象被创建了,而且相互引用,这样创建了一个循环引用。 它们会在函数调用后超出作用域,应该可以释放。 然而引用计数算法考虑到2个对象中的每一个至少被引用了一次,因此都不可以被回收。 function f() { var o1 = {}; var o2 = {}; o1.p = o2; // o1 引用 o2 o2.p = o1; // o2 引用 o1\. 形成循环引用}f();标记清除算法为了决定一个对象是否被需要,这个算法用于确定是否可以找到某个对象。 这个算法包含以下步骤。 废品回收器生成一个根列表。 根通常是将引用保存在代码中的全局变量。 在JavaScript中,window对象是一个可以作为根的全局变量。 所有的根都被检查和标记成活跃的(不是废品),所有的子变量也被递归检查。 所有可能从根元素到达的都不被认为是废品。 所有没有被标记成活跃的内存都被认为是废品。 废品回收器就可以释放内存并且把内存还给操作系统。 上图就是标记清除示意。 这个算法就比之前的(引用计算)要好些,因为“一个对象没有被引用”导致这个对象不能被访问。 相反,正如我们在循环引用的示例中看到的,对象不能被访问到,不一定不存在引用。 2012年起,所有浏览器都内置了标记清除废品回收器。 在过去几年中,JavaScript废品回收领域中的所有改进(代/增量/并行/并行废品收集)都是由这个算法(标记清除法)改进实现的,但并不是对废品收集算法本身的改进,也没有改变它确定对象是否可达这个目标。 推荐 一篇文章 ,其中有关于跟踪废品回收的细节,包括了标记清除法和它的优化算法。 循环引用不再是问题在上面的例子中(循环引用的那个),在函数执行完之后,这个2个对象没有被任何可以到达的全局对象所引用。 因此,他们将会被废品回收器发现为不可到达的。 尽管在这两个对象之间有相互引用,但是他们不能从全局对象上到达。 废品回收器的反常行为尽管废品回收器很方便,但是他们有一套自己的方案。 其中之一就是不确定性。 换句话说,GC是不可预测的。 你不可能知道一个回收器什么时候会被执行。 这意味着程序在某些情况下会使用比实际需求还要多的内存。 在其他情况下,在特别敏感的应用程序中,可能会出现短停顿。 尽管不确定意味着不能确定回收工作何时执行,但大多数GC实现都会在分配内存的期间启动收集例程。 如果没有内存分配,大部分废品回收就保持空闲。 参考下面的情况。 执行相当大的一组分配。 这些元素中的大部分(或者所有的)都被标记为不可到达的(假设我们清空了一个指向我们不再需要的缓存的引用。 )没有更多的分配被执行。 在这种情况下,大多数废品回收实现都不会做进一步的回收。 换句话说,尽管这里有不可达的引用变量可供回收,回收器也不会管。 严格讲,这不是泄露,但结果却会占用比通常情况下更多的内存。 什么是内存泄漏内存泄漏基本上就是不再被应用需要的内存,由于某种原因,没有被归还给操作系统或者进入可用内存池。 编程语言喜欢不同的管理内存方式。 然而,一段确定的内存是否被使用是一个不可判断的问题。 换句话说,只有开发者才能弄清楚,是否一段内存可以被还给操作系统。 某些编程语言为开发者提供了释放内存功能。 另一些则期待开发者清楚的知道一段内存什么时候是没用的。 Wikipedia有一篇非常好的关于内存管理的文章。 4种常见的JavaScript内存泄漏1:全局变量JavaScript用一个有趣的方式管理未被声明的变量:对未声明的变量的引用在全局对象里创建一个新的变量。 在浏览器的情况下,这个全局对象是 window 。 换句话说:function foo(arg) { bar = some text;}等同于function foo(arg) { = some text;}如果 bar 被假定只在 foo 函数的作用域里引用变量,但是你忘记了使用 var 去声明它,一个意外的全局变量就被声明了。 在这个例子里,泄漏一个简单的字符串不会造成很大的伤害,但是它确实有可能变得更糟。 另外一个意外创建全局变量的方法是通过 this :function foo() { 1 = potential accidental global;}// Foo作为函数调用,this指向全局变量(window)// 而不是undefinedfoo();为了防止这些问题发生,可以在你的JaveScript文件开头使用 use strict; 。 这个可以使用一种严格的模式解析JavaScript来阻止意外的全局变量。 除了意外创建的全局变量,明确创建的全局变量同样也很多。 这些当然属于不能被回收的(除非被指定为null或者重新分配)。 特别那些用于暂时存储数据的全局变量,是非常重要的。 如果你必须要使用全局变量来存储大量数据,确保在是使用完成之后为其赋值 null或者重新赋其他值。 2: 被遗忘的定时器或者回调在JavaScript中使用 setInterval 是十分常见的。 大多数库,特别是提供观察器或其他接收回调的实用函数的,都会在自己的实例无法访问前把这些回调也设置为无法访问。 但涉及 setInterval 时,下面这样的代码十分常见:var serverData = loadData();setInterval(function() { var renderer = (renderer); if(renderer) { = (serverData); }}, 5000); //每5秒执行一次定时器可能会导致对不需要的节点或者数据的引用。 renderer 对象在将来有可能被移除,让interval处理器内部的整个块都变得没有用。 但由于interval仍然起作用,处理程序并不能被回收(除非interval停止)。 如果interval不能被回收,它的依赖也不可能被回收。 这就意味着 serverData ,大概保存了大量的数据,也不可能被回收。 在观察者的情况下,在他们不再被需要(或相关对象需要设置成不能到达)的时候明确的调用移除是非常重要的。 在过去,这一点尤其重要,因为某些浏览器(旧的IE6)不能很好的管理循环引用(更多信息见下文)。 如今,大部分的浏览器都能而且会在对象变得不可到达的时候回收观察处理器,即使监听器没有被明确的移除掉。 然而,在对象被处理之前,要显式地删除这些观察者仍然是值得提倡的做法。 例如:var element = (launch-button);var counter = 0;function onClick(event) { counter++; = text + counter;}(click, onClick);// 做点事(click, onClick);(element);// 当元素被销毁//元素和事件都会即使在老的浏览器里也会被回收如今的浏览器(包括IE和Edge)使用现代的废品回收算法,可以立即发现并处理这些循环引用。 换句话说,先调用 removeEventListener 再删节点并非严格必要。 jQuery等框架和插件会在丢弃节点前删除监听器。 这都是它们内部处理,以保证不会产生内存泄漏,甚至是在有问题的浏览器(没错,IE6)上也不会。 3: 闭包闭包是JavaScript开发的一个关键方面:一个内部函数使用了外部(封闭)函数的变量。 由于JavaScript运行时实现的不同,它可能以下面的方式造成内存泄漏:var theThing = null;var replaceThing = function () { var originalThing = theThing; var unused = function () { if (originalThing) // 引用originalThing (hi); }; theThing = { longStr: new Array()(*), someMethod: function () { (message); } };};setInterval(replaceThing, 1000);这段代码做了一件事:每次 ReplaceThing 被调用, theThing 获得一个包含大数组和新的闭包( someMethod )的对象。 同时,变量 unused 保持了一个引用 originalThing ( theThing 是上次调用 replaceThing 生成的值)的闭包。 已经有点困惑了吧?最重要的事情是 一旦为同一父域中的作用域产生闭包,则该作用域是共享的。 这里,作用域产生了闭包, someMethod 和 unused 共享这个闭包中的内存。 unused 引用了 originalThing 。 尽管 unused 不会被使用, someMethod 可以通过 theThing 来使用 replaceThing 作用域外的变量(例如某些全局的)。 而且 someMethod 和 unused 有共同的闭包作用域, unused 对 originalThing 的引用强制 oriiginalThing 保持激活状态(两个闭包共享整个作用域)。 这阻止了它的回收。 当这段代码重复执行,可以观察到被使用的内存在持续增加。 废品回收运行的时候也不会变小。 从本质上来说,闭包的连接列表已经创建了(以 theThing 变量为根),这些闭包每个作用域都间接引用了大数组,导致大量的内存泄漏。 这个问题被Meteor团队发现,他们有描述了闭包大量的细节。 4: DOM外引用有的时候在数据结构里存储DOM节点是非常有用的,比如你想要快速更新一个表格几行的内容。 此时存储每一行的DOM节点的引用在一个字典或者数组里是有意义的。 此时一个DOM节点有两个引用:一个在dom树中,另外一个在字典中。 如果在未来的某个时候你想要去移除这些排,你需要确保两个引用都不可到达。 var elements = { button: (button), image: (image)};function doStuff() { =}function removeImage() { //image是body元素的子节点 ((image)); //这个时候我们在

2022年完全最全JVM讲解(最新版2W字总结)

深入解析:2022年全面解读JVM内存管理与废品回收机制

深入了解 JavaScript 废品回收:揭示内存管理的奥秘 (深入了解javascript特性读后感) 第1张

Java虚拟机(JVM)内存区域划分为两大部分:私有区域和共享区域。每个线程拥有独立的程序计数器(用于跟踪当前执行的字节码)、虚拟机栈(存储方法调用上下文),以及本地方法区(存储本地代码和常量)。而共享区域主要涉及JAVA堆和方法区,堆用于存放对象和数组,分为新生代和老年代,分别对应着不同的生命周期和废品回收策略。

新生代与老年代

新生代是对象的诞生地,Eden区主要用于对象的创建。经过Minor GC(复制算法)后,存活的对象会被移动到Survivor区或老年代。新生代的废品回收旨在快速响应,而老年代则负责处理生命周期长的对象,当空间不足或新对象较大时,会触发Major GC(标记清除或标记整理),这可能导致内存碎片和较长的暂停时间。

元数据区与废品回收算法

Java 8以后,元数据区取代了永久代,存储类和元数据信息。废品回收器通过引用计数或根搜索算法来识别可回收对象。复制算法高效但不压缩内存,标记整理算法兼顾效率与碎片减少。元数据区的持久性可能导致oom(内存溢出),因此需要谨慎管理。

HotSpot VM采用分代收集法,新生代采用Copying算法,老年代则可能使用Mark-Compact算法。强引用可能导致内存泄漏,而软引用和弱引用提供了内存回收的灵活性。Serial收集器以其简单高效在单线程场景中常见,ParNew是多线程版本,适合Client模式下的新生代回收,用户可以通过调整线程数优化性能。

Server模式下的废品回收器

在Server模式下,废品回收器如Parallel Scavenge关注程序吞吐量,Serial Old则提供低停顿的单线程清理,而CMS和G1则各有侧重:CMS追求低停顿时间但可能影响CPU,G1则以灵活的堆区域划分和无碎片管理为特色,兼顾低停顿与高吞吐。

内存管理和类加载

内存泄露源于长期引用生命周期短暂的对象,类加载过程包括加载、连接(验证、准备、解析)和初始化。类的初始化仅在首次使用时执行,常量池的管理影响着类的初始化时机。类加载遵循双亲委派机制,保证类型安全,而线程上下文加载器则在特定场景中提供灵活性。

理解线程上下文加载器,例如()(),有助于处理标准库与自定义实现的兼容性问题。

总结

JVM的内存管理与废品回收机制是理解Java性能基石的关键。通过深入了解各个区域的特性和回收策略,以及类加载和上下文加载的原理,开发者可以更有效地优化内存使用,确保应用程序的稳定运行。

如何解决JS高程中的废品回收机制与常见内存泄露的问题

这篇文章主要介绍了关于如何解决JS高程中的废品回收机制与常见内存泄露的问题,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下前言起因是因为想了解闭包的内存泄露机制,然后想起《js高级程序设计》中有关于废品回收机制的解析,之前没有很懂,过一年回头再看就懂了,写给大家分享一下。 如果喜欢的话可以点波赞/关注,支持一下。 内存的生命周期:分配你所需要的内存:由于字符串、对象等没有固定的大小,js程序在每次创建字符串、对象的时候,程序都会分配内存来存储那个实体。 使用分配到的内存做点什么。 不需要时将其释放回归:在不需要字符串、对象的时候,需要释放其所占用的内存,否则将会消耗完系统中所有可用的内存,造成系统崩溃,这就是废品回收机制所存在的意义。 所谓的内存泄漏指的是:由于疏忽或错误造成程序未能释放那些已经不再使用的内存,造成内存的浪费。 废品回收机制:在C和C++之类的语言中,需要手动来管理内存的,这也是造成许多不必要问题的根源。 幸运的是,在编写js的过程中,内存的分配以及内存的回收完全实现了自动管理,我们不用操心这种事情。 废品收集机制的原理:废品收集器会按照固定的时间间隔,周期性的找出不再继续使用的变量,然后释放其占用的内存。 什么叫不再继续使用的变量?不再使用的变量也就是生命周期结束的变量,是局部变量,局部变量只在函数的执行过程中存在,当函数运行结束,没有其他引用(闭包),那么该变量会被标记回收。 全局变量的生命周期直至浏览器卸载页面才会结束,也就是说全局变量不会被当成废品回收。 标记清除:当前采用的废品收集策略工作原理:当变量进入环境时(例如在函数中声明一个变量),将这个变量标记为“进入环境”,当变量离开环境时,则将其标记为“离开环境”。 标记“离开环境”的就回收内存。 工作流程:废品收集器会在运行的时候会给存储在内存中的所有变量都加上标记。 去掉环境中的变量以及被环境中的变量引用的变量的标记。 那些还存在标记的变量被视为准备删除的变量。 最后废品收集器会执行最后一步内存清除的工作,销毁那些带标记的值并回收它们所占用的内存空间。 到2008年为止,IE、Chorme、Fireofx、Safari、Opera 都使用标记清除式的废品收集策略,只不过废品收集的时间间隔互有不同。 引用计数略:被废弃的废品收集策略循环引用:跟踪记录每个值被引用的技术在老版本的浏览器中(对,又是IE),IE9以下BOM和DOM对象就是使用C++以COM对象的形式实现的。 COM的废品收集机制采用的就是引用计数策略,这种机制在出现循环引用的时候永远都释放不掉内存。 var element = (something); var myObject = new Object(); = element; // element属性指向dom = myObject; // someThing回指myObject 出现循环引用(两个对象一直互相包含 一直存在计数)。 解决方式是,当我们不使用它们的时候,手动切断链接: = null; = null;淘汰:IE9把BOM和DOM对象转为了真正的js对象,避免了使用这种废品收集策略,消除了IE9以下常见的内存泄漏的主要原因。 IE7以下有一个声明狼藉的性能问题,大家了解一下:256个变量,4096个对象(或数组)字面或者64KB的字符串,达到任何一个临界值会触发废品收集器运行。 如果一个js脚本的生命周期一直保有那么多变量,废品收集器会一直频繁的运行,引发严重的性能问题。 IE7已修复这个问题。 哪些情况会引起内存泄漏?虽然有废品回收机制,但我们在编写代码的时候,有些情况还是会造成内存泄漏,了解这些情况,并在编写程序的时候,注意避免,我们的程序会更具健壮性。 意外的全局变量:上文我们提到了全局变量不会被当成废品回收,我们在编码中有时会出现下面这种情况: function foo() { 2 = 默认绑定this指向全局 // 全局变量=> 2 bar = 全局变量; // 没有声明变量 实际上是全局变量=> } foo();当我们使用默认绑定,this会指向全局,也会创建一个全局变量,这一点可能很多人没有注意到。 解决方法:在函数内使用严格模式or细心一点 function foo() { use strict; 2 = 严格模式下this指向undefined; bar = 报错; } foo();当然我们也可以手动释放全局变量的内存: = undefined delete 2被遗忘的定时器和回调函数当不需要setInterval或者setTimeout时,定时器没有被clear,定时器的回调函数以及内部依赖的变量都不能被回收,造成内存泄漏。 var someResource = getData();setInterval(function() { var node = (Node); if(node) { = (someResource)); // 定时器也没有清除 } // node、someResource 存储了大量数据 无法回收}, 1000);解决方法: 在定时器完成工作的时候,手动清除定时器。 闭包:闭包可以维持函数内局部变量,使其得不到释放,造成内存泄漏。 function bindEvent() { var obj = (XXX); var unused = function () { (obj,闭包内引用obj obj不会被释放); }; // obj = null; }解决方法:手动解除引用,obj = null。 循环引用问题就是IE9以下的循环引用问题,上文讲过了。 没有清理DOM元素引用: var refA = (refA); (refA); // dom删除了 (refA, refA); // 但是还存在引用 能console出整个p 没有被回收不信的话,可以看下这个dom。 解决办法:refA = null;console保存大量数据在内存中。 过多的console,比如定时器的console会导致浏览器卡死。 解决:合理利用console,线上项目尽量少的使用console,当然如果你要发招聘除外。 如何避免内存泄漏:记住一个原则:不用的东西,及时归还,毕竟你是借的嘛。 减少不必要的全局变量,使用严格模式避免意外创建全局变量。 在你使用完数据后,及时解除引用(闭包中的变量,dom引用,定时器清除)。 组织好你的逻辑,避免死循环等造成浏览器卡顿,崩溃的问题。 关于内存泄漏:即使是1byte的内存,也叫内存泄漏,并不一定是导致浏览器崩溃、卡顿才能叫做内存泄漏。 一般是堆区内存泄漏,栈区不会泄漏。 基本类型的值存在内存中,被保存在栈内存中,引用类型的值是对象,保存在堆内存中。 所以对象、数组之类的,才会发生内存泄漏。 使用chorme监控内存泄漏,可以看一下这篇文章结语了解了内存泄漏的原因以及出现的情况,那么我们在编码过程中只要多加注意,就不会发生非常严重的内存泄漏问题。

若对本页面资源感兴趣,请点击下方或右方图片,注册登录后

搜索本页相关的【资源名】【软件名】【功能词】或有关的关键词,即可找到您想要的资源

如有其他疑问,请咨询右下角【在线客服】,谢谢支持!

深入了解 JavaScript 废品回收:揭示内存管理的奥秘 (深入了解javascript特性读后感) 第2张

发表评论

评论列表

  • 这篇文章还没有收到评论,赶紧来抢沙发吧~
欢迎你第一次访问网站!