您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > javascript

如何避免JavaScript中的内存泄漏?

时间:2023-10-27 15:49:06  来源:  作者:OSC开源社区

作者 | 葡萄城技术团队

原文链接:https://my.oschina.NET/powertoolsteam/blog/10122640

前言

过去,我们浏览静态网站时无须过多关注内存管理,因为加载新页面时,之前的页面信息会从内存中删除。然而,随着单页 Web 应用(SPA)的兴起,应用程序消耗的内存越来越多,这不仅会降低浏览器性能,甚至会导致浏览器卡死。因此,在编码实践中,开发人员需要更加关注与内存相关的内容。因此,小编今天将为大家介绍 JAVA 内存泄漏的编程模式,并提供一些内存管理的改进方法。

什么是内存泄漏以及如何发现它?

如何避免JavaScript中的内存泄漏?

什么是内存泄漏?

Java 对象被保存在浏览器内存的堆中,并通过引用方式访问。值得一提的是,Java 垃圾回收器则运行于后台,并通过识别无法访问的对象来释放并恢复底层存储空间,从而保证 Java 引擎的良好运行状态。

当内存中的对象在垃圾回收周期中应该被清理时,若它们被另一个仍然存在于内存中的对象通过一个意外的引用所持有,就会引发内存泄漏问题。这种情况下,冗余对象会继续占据内存空间,导致应用程序消耗过多的内存资源,并可能导致性能下降和表现不佳的情况出现。因此,及时清理无用对象并释放内存资源是至关重要的,以确保应用程序的正常运行和良好的性能表现。

如何发现内存泄漏?

那么如何知道代码中是否存在内存泄漏?内存泄漏往往隐蔽且很难检测和定位。即使代码中存在内存泄漏,浏览器在运行时也不会返回任何错误。如果注意到页面的性能逐渐下降,可以使用浏览器内置的工具来确定是否存在内存泄漏以及是哪个对象引起的。

任务管理器(不要与操作系统的任务管理器混淆)提供了浏览器中所有选项卡和进程的概览。Chrome 中,可以通过在 linuxwindows 操作系统上按 Shift+Esc 来打开任务管理器;而在 Firefox 中,通过在地址栏中键入 about:performance 则可以访问内置的管理器,它可以显示每个标签的 Java 内存占用情况。如果网站停留在那里什么都不做,但 Java 内存使用量逐渐增加,那很可能是存在内存泄漏。

如何避免JavaScript中的内存泄漏?

开发者工具提供了一些先进的内存管理方法,例如,使用 Chrome 浏览器的性能记录工具,可以对页面的性能进行可视化分析。在这个过程中,可以通过一些指标来判断是否存在内存泄漏问题,比如堆内存使用量增加的情况,并及时采取措施解决这些问题,以确保应用程序的正常运行和良好的性能表现。

如何避免JavaScript中的内存泄漏?

另外,通过 Chrome 和 Firefox 的开发者工具提供的内存工具,可以进一步探索内存使用情况。队列内存使用快照的比较可以显示在两个快照之间分配了多少内存以及分配的位置,并提供额外信息来帮助识别代码中存在问题的对象。这些工具为开发者提供了便利,能够更好地进行内存管理和性能优化,提高应用程序的质量和性能。

Java 代码中常见的内存泄漏的常见来源:

研究内存泄漏问题就相当于寻找符合垃圾回收机制的编程方式,有效避免对象引用的问题。下面小编就为大家介绍几个常见的容易导致内存泄漏的地方:

1. 全局变量

全局变量始终存储在根目录下,且永远不会被回收。而在 Java 的开发中,一些错误会导致局部变量被转换到了全局,尤其是在非严格的代码模式下。下面是两个常见的局部变量被转化到全局变量的情况:

  1. 为未声明的变量赋值
  2. 使用 this 指向全局对象。
functioncreateGlobalVariables{

leaking1 = 'I leak into the global scope'; // 为未声明的变量赋值

this.leaking2 = 'I also leak into the global scope'; // 使用this指向全局对象

};

createGlobalVariables;

window.leaking1;

window.leaking2;

** 注意:** 严格模式("use strict")将帮助您避免上面示例中的内存泄漏和控制台错误。

2. 闭包

函数中定义的变量会在函数退出调用栈并且在函数外部没有指向它的引用时被清除。而闭包则会保持被引用的变量一直存在,即便函数的执行已经终止。

functionouter{

constpotentiallyHugeArray = [];

returnfunctioninner{

potentiallyHugeArray.push('Hello'); // function inner is closed over the potentiallyHugeArray variable

console.log('Hello');

};

};

constsayHello = outer; // contAIns definition of the function inner

functionrepeat(fn, num) {

for(leti = 0; i < num; i++){

fn;

}

}

repeat(sayHello, 10); // each sayHello call pushes another 'Hello' to the potentiallyHugeArray

// now imagine repeat(sayHello, 100000)

在这个例子中,potentiallyHugeArray 从未被任何函数返回,也无法被访问,但它的大小会随着调用 inner 方法的次数而增长。

3. 定时器

在 Java 中,使用使用 setTimeout 或 setInterval 函数引用对象是防止对象被垃圾回收的最常见方法。当在代码中设置循环定时器(可以使 setTimeout 表现得像 setInterval,即使其递归)时,只要回调可调用,定时器回调对象的引用就会永远保持活动状态。

例如下面的这段代码,只有在移除定时器后,data 对象才会被垃圾回收。在没有移除 setInterval 之前,它永远不会被删除,并且 data.hugeString 会一直保留在内存中,直到应用程序停止。

functionsetCallback{

constdata = {

counter: 0,

hugeString: newArray(100000).join('x')

};

returnfunctioncb{

data.counter++; // data object is now part of the callback's scope

console.log(data.counter);

}

}

setInterval(setCallback, 1000); // how do we stop it?

那么应该如何避免上述这种情况的发生呢?可以从以下两个方法入手:

  1. 注意定时器回调引用的对象。
  2. 必要时取消定时器。

如下方的代码所示:

functionsetCallback{

// 'unpacking' the data object

letcounter = 0;

consthugeString = newArray(100000).join('x'); // gets removed when the setCallback returns

returnfunctioncb{

counter++; // only counter is part of the callback's scope

console.log(counter);

}

}

consttimerId = setInterval(setCallback, 1000); // saving the interval ID

// doing something ...

clearInterval(timerId); // stopping the timer i.e. if button pressed

4. 事件监听

活动的事件监听器会阻止其范围内的所有变量被回收。一旦添加,事件监听器会一直生效,直到下面两种情况的发生:

  1. 通过 removeEventListener 移除。
  2. 相关联的 DOM 元素被移除。

在下面的示例中,使用匿名内联函数作为事件监听器,这意味着它不能与 removeEventListener 一起使用。此外,由于 document 不能被移除,触发方法中的内容会一直驻留内存,即使只使用它触发一次。

consthugeString = newArray(100000).join('x');

document.addEventListener('keyup', function{ // anonymous inline function - can't remove it

doSomething(hugeString); // hugeString is now forever kept in the callback's scope

});

那么如何避免这种情况呢?可以通过 removeEventListener 释放监听器:

functionlistener{

doSomething(hugeString);

}

document.addEventListener('keyup', listener); // named function can be referenced here...

document.removeEventListener('keyup', listener); // ...and here

如果事件监听器只需要运行一次,addEventListener 可以带有第三个参数,一个提供附加选项的对象。只要将 {once: true} 作为第三个参数传递给 addEventListener ,监听器将在事件处理一次后自动删除。

document.addEventListener('keyup', functionlistener{

doSomething(hugeString);

}, {once: true}); // listener will be removed after running once

5. 缓存

如果不断向缓存中添加内容,而未使用的对象也没有移除,也没有限制缓存的大小,那么缓存的大小就会无限增长:

letuser_1 = { name: "Peter", id: 12345};

letuser_2 = { name: "Mark", id: 54321};

constmapCache = newMap;

functioncache(obj){

if(!mapCache.has(obj)){

constvalue = `${obj.name}has an id of ${obj.id}`;

mapCache.set(obj, value);

return[value, 'computed'];

}

return[mapCache.get(obj), 'cached'];

}

cache(user_1); // ['Peter has an id of 12345', 'computed']

cache(user_1); // ['Peter has an id of 12345', 'cached']

cache(user_2); // ['Mark has an id of 54321', 'computed']

console.log(mapCache); // ((…) => "Peter has an id of 12345", (…) => "Mark has an id of 54321")

user_1 = null; // removing the inactive user

// Garbage Collector

console.log(mapCache); // ((…) => "Peter has an id of 12345", (…) => "Mark has an id of 54321") // first entry is still in cache

为了解决这个问题,需要清除不需要的缓存:

一种有效的解决内存泄漏问题的方法是使用 WeakMap。它是一种数据结构,其中键引用被保持为弱引用,并且仅接受对象作为键。如果使用对象作为键,并且它是唯一引用该对象的引用,相关条目将从缓存中移除,并进行垃圾回收。在下面的示例中,当替换 user_1 后,与之关联的条目将在下一次垃圾回收时自动从 WeakMap 中移除。

letuser_1 = { name: "Peter", id: 12345};

letuser_2 = { name: "Mark", id: 54321};

constweakMapCache = newWeakMap;

functioncache(obj){

// ...same as above, but with weakMapCache

return[weakMapCache.get(obj), 'cached'];

}

cache(user_1); // ['Peter has an id of 12345', 'computed']

cache(user_2); // ['Mark has an id of 54321', 'computed']

console.log(weakMapCache); // ((…) => "Peter has an id of 12345", (…) => "Mark has an id of 54321"}

user_1 = null; // removing the inactive user

// Garbage Collector

console.log(weakMapCache); // ((…) => "Mark has an id of 54321") - first entry gets garbage collected

结论

对于复杂的应用程序,检测和修复 Java 内存泄漏问题可能是一项非常艰巨的任务。了解内存泄漏的常见原因以防止它们发生是非常重要的。在涉及内存和性能方面,最重要的是用户体验,这才是最重要的。



Tags:JavaScript   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
17 个你需要知道的 JavaScript 优化技巧
你可能一直在使用JavaScript搞开发,但很多时候你可能对它提供的最新功能并不感冒,尽管这些功能在无需编写额外代码的情况下就可以解决你的问题。作为前端开发人员,我们必须了解...【详细内容】
2024-04-03  Search: JavaScript  点击:(4)  评论:(0)  加入收藏
你不可不知的 15 个 JavaScript 小贴士
在掌握如何编写JavaScript代码之后,那么就进阶到实践&mdash;&mdash;如何真正地解决问题。我们需要更改JS代码使其更简单、更易于阅读,因为这样的程序更易于团队成员之间紧密协...【详细内容】
2024-03-21  Search: JavaScript  点击:(25)  评论:(0)  加入收藏
构建一个通用灵活的JavaScript插件系统?看完你也会!
在软件开发中,插件系统为应用程序提供了巨大的灵活性和可扩展性。它们允许开发者在不修改核心代码的情况下扩展和定制应用程序的功能。本文将详细介绍如何构建一个灵活的Java...【详细内容】
2024-03-20  Search: JavaScript  点击:(20)  评论:(0)  加入收藏
对JavaScript代码压缩有什么好处?
对JavaScript代码进行压缩主要带来以下好处: 减小文件大小:通过移除代码中的空白符、换行符、注释,以及缩短变量名等方式,可以显著减小JavaScript文件的大小。这有助于减少网页...【详细内容】
2024-03-13  Search: JavaScript  点击:(2)  评论:(0)  加入收藏
跨端轻量JavaScript引擎的实现与探索
一、JavaScript 1.JavaScript语言JavaScript是ECMAScript的实现,由ECMA 39(欧洲计算机制造商协会39号技术委员会)负责制定ECMAScript标准。ECMAScript发展史: 2.JavaScript...【详细内容】
2024-03-12  Search: JavaScript  点击:(2)  评论:(0)  加入收藏
面向AI工程的五大JavaScript工具
令许多人惊讶的是,一向在Web开发领域中大放异彩的JavaScript在开发使用大语言模型(LLM)的应用程序方面同样大有价值。我们在本文中将介绍面向AI工程的五大工具,并为希望将LLM...【详细内容】
2024-02-06  Search: JavaScript  点击:(52)  评论:(0)  加入收藏
18个JavaScript技巧:编写简洁高效的代码
本文翻译自 18 JavaScript Tips : You Should Know for Clean and Efficient Code,作者:Shefali, 略有删改。在这篇文章中,我将分享18个JavaScript技巧,以及一些你应该知道的示例...【详细内容】
2024-01-30  Search: JavaScript  点击:(65)  评论:(0)  加入收藏
使用 JavaScript 清理我的 200GB iCloud,有了一个意外发现!
本文作者在综合成本因素之下,决定用 Java 脚本来清理一下自己的 iCloud,结果却有了一个意外发现,即在 iCloud 中上传同一个视频和删除此视频之后,iCloud 的空间并不一致,这到底是...【详细内容】
2024-01-11  Search: JavaScript  点击:(97)  评论:(0)  加入收藏
JavaScript前端框架2024年展望
Angular、Next.js、React和Solid的维护者和创作者们展望2024年,分享了他们计划中的改进。译自2024 Predictions by JavaScript Frontend Framework Maintainers,作者 Loraine...【详细内容】
2024-01-05  Search: JavaScript  点击:(89)  评论:(0)  加入收藏
JavaScript开发者转向Rust的原因?
JavaScript开发者转向Rust的原因可能有很多,这里列出一些可能的原因: 性能: Rust是一种编译型语言,其性能通常优于JavaScript等解释型语言。对于需要处理大量数据或需要高并发的...【详细内容】
2024-01-04  Search: JavaScript  点击:(96)  评论:(0)  加入收藏
▌简易百科推荐
17 个你需要知道的 JavaScript 优化技巧
你可能一直在使用JavaScript搞开发,但很多时候你可能对它提供的最新功能并不感冒,尽管这些功能在无需编写额外代码的情况下就可以解决你的问题。作为前端开发人员,我们必须了解...【详细内容】
2024-04-03  前端新世界  微信公众号  Tags:JavaScript   点击:(4)  评论:(0)  加入收藏
你不可不知的 15 个 JavaScript 小贴士
在掌握如何编写JavaScript代码之后,那么就进阶到实践&mdash;&mdash;如何真正地解决问题。我们需要更改JS代码使其更简单、更易于阅读,因为这样的程序更易于团队成员之间紧密协...【详细内容】
2024-03-21  前端新世界  微信公众号  Tags:JavaScript   点击:(25)  评论:(0)  加入收藏
又出新JS运行时了!JS运行时大盘点
Node.js是基于Google V8引擎的JavaScript运行时,以非阻塞I/O和事件驱动架构为特色,实现全栈开发。它跨平台且拥有丰富的生态系统,但也面临安全性、TypeScript支持和性能等挑战...【详细内容】
2024-03-21  前端充电宝  微信公众号  Tags:JS   点击:(23)  评论:(0)  加入收藏
构建一个通用灵活的JavaScript插件系统?看完你也会!
在软件开发中,插件系统为应用程序提供了巨大的灵活性和可扩展性。它们允许开发者在不修改核心代码的情况下扩展和定制应用程序的功能。本文将详细介绍如何构建一个灵活的Java...【详细内容】
2024-03-20  前端历险记  微信公众号  Tags:JavaScript   点击:(20)  评论:(0)  加入收藏
对JavaScript代码压缩有什么好处?
对JavaScript代码进行压缩主要带来以下好处: 减小文件大小:通过移除代码中的空白符、换行符、注释,以及缩短变量名等方式,可以显著减小JavaScript文件的大小。这有助于减少网页...【详细内容】
2024-03-13  WangLiwen    Tags:JavaScript   点击:(2)  评论:(0)  加入收藏
跨端轻量JavaScript引擎的实现与探索
一、JavaScript 1.JavaScript语言JavaScript是ECMAScript的实现,由ECMA 39(欧洲计算机制造商协会39号技术委员会)负责制定ECMAScript标准。ECMAScript发展史: 2.JavaScript...【详细内容】
2024-03-12  京东云开发者    Tags:JavaScript   点击:(2)  评论:(0)  加入收藏
面向AI工程的五大JavaScript工具
令许多人惊讶的是,一向在Web开发领域中大放异彩的JavaScript在开发使用大语言模型(LLM)的应用程序方面同样大有价值。我们在本文中将介绍面向AI工程的五大工具,并为希望将LLM...【详细内容】
2024-02-06    51CTO  Tags:JavaScript   点击:(52)  评论:(0)  加入收藏
JS小知识,使用这6个小技巧,避免过多的使用 if 语句
最近在重构我的代码时,我注意到早期的代码使用了太多的 if 语句,达到了我以前从未见过的程度。这就是为什么我认为分享这些可以帮助我们避免使用过多 if 语句的简单技巧很重要...【详细内容】
2024-01-30  前端达人  今日头条  Tags:JS   点击:(56)  评论:(0)  加入收藏
18个JavaScript技巧:编写简洁高效的代码
本文翻译自 18 JavaScript Tips : You Should Know for Clean and Efficient Code,作者:Shefali, 略有删改。在这篇文章中,我将分享18个JavaScript技巧,以及一些你应该知道的示例...【详细内容】
2024-01-30  南城大前端  微信公众号  Tags:JavaScript   点击:(65)  评论:(0)  加入收藏
使用 JavaScript 清理我的 200GB iCloud,有了一个意外发现!
本文作者在综合成本因素之下,决定用 Java 脚本来清理一下自己的 iCloud,结果却有了一个意外发现,即在 iCloud 中上传同一个视频和删除此视频之后,iCloud 的空间并不一致,这到底是...【详细内容】
2024-01-11    CSDN  Tags:JavaScript   点击:(97)  评论:(0)  加入收藏
站内最新
站内热门
站内头条