任何程序都有可能发生内存“泄漏”(即申请了系统内存并且在工作完成后没有释放),并且对于使用非托管语言(unmanaged languages)(如C语言)的开发者来说,内存的分配和释放是一个主要的关注点。JavaScript是一种内存托管(memory-managed)的语言,垃圾回收过程能够帮助程序员自动地处理内存的分配和释放。该机制解决了大部分困扰的非托管代码的问题,但是,认为内存托管语言不会产生内存泄漏却是错误的。
垃圾回收进程尝试推断何时可以安全地回收不再使用的变量,通常是通过判定程序是否能够通过变量之间形成的引用网络到达该变量。当确信变量是不可达的,就在它上面标上可以回收的记号,并且在回收器的下一次清理中(可能在未来的任意时刻)释放相关的内存。在托管语言中产生内存泄漏非常简单:只需使用完变量而忘记解除引用。
我们来考虑一个简单的例子,其中定义了一个描述家庭宠物及其主人的对象模型。首先看看主人,以Person对象描述:
function Person(name){
this.name=name;
this.pets=new Array();
}
一个主人可以养一只或者多只宠物。当主人得到了一只宠物,他告诉宠物现在自己是它的主人:
Person.prototype.addPet=function(pet){
this.pets[pet.name]=pet;
if (pet.assignOwner){
pet.assignOwner(this);
}
}
类似的,当主人从他的宠物列表中删除了一只宠物,他告诉宠物自己不再是它的主人:
this.removePet(petName)=function{
var orphan=this.pets[petName];
this.pets[petName]=null;
if (orphan.unassignOwner){
orphan.unassignOwner(this);
}
}
主人在任何时刻都知道谁是他的宠物并且能够通过提供的addPet()
和removePet()
方法来管理宠物列表。主人在领养或不再领养宠物时都会通知该宠物,这基于一个假设,即每个宠物都会遵守这个契约(在JavaScript中,这个契约是隐含的,可以在运行时检查是否遵守了契约)。
宠物多种多样,在这里定义了两种:猫和狗。它们的区别在于对待被领养的态度上,猫并不在意被谁所领养,而狗一生都会伴随领养它的主人(我为这个普遍的观点向动物世界道歉!)。
因此宠物猫的定义看起来像是这样:
function Cat(name){
this.name=name;
}
Cat.prototype.assignOwner=function(person){}
Cat.prototype.unassignOwner=function(person){}
猫对于是否被领养并不感兴趣,因此仅仅提供了契约方法的空实现。
另一方面,我们可以将狗定义为忠实地记得它的主人是谁,即使被遗弃了仍然保持对主人的“引用”(一些狗确实如此
function Dog(name){
this.name=name;
}
Dog.prototype.assignOwner=function(person){
this.owner=person;
}
Dog.prototype.unassignOwner=function(person){
this.owner=person;
}
Cat和Dog对象都是Pet的行为恶劣的实现。作为宠物,它们严格依照契约的文字来实现,但是却没有遵循契约的灵魂。在Java或C#的实现中,我们可以明确地定义Pet接口,但是那样仍然不能阻止实现违背契约的灵魂。在现实编程世界中,对象建模者花费了大量的时间防止出现接口的行为恶劣的实现,尽力封堵所有可能被利用的漏洞。
我们来将这个对象模型具体化。在下面的脚本中,我们创建了三个对象:
- jim,人(Person)
- whiskers,猫(Cat)
- fido,狗(Dog)
首先,我们实例化一个人(Person)(第1步):
var jim=new Person("jim");
我们也给了jim一只宠物狗(第3步)。作为一个全局变量来声明,fido比whiskers稍微多一点优势:
var fido=new Dog("fido");
jim.addPet(fido);
有一天,jim送掉了他的猫(第4步):
jim.removePet("whiskers");
后来,他又送掉了他的狗(第5步)。也许他移民了?
jim.removePet("fido");
我们对jim失去了兴趣并且释放了对他的引用(第6步):
jim=null;
最后,我们又释放了对fido的引用(第7步):
fido=null;
在第6步和第7步之间,我们可能相信已经通过设置jim为null
摆脱了他。事实上,他仍然被fido引用并且仍然可以通过代码fido.owner
到达。垃圾回收器无法将他释放,只能留下他潜伏在JavaScript引擎的堆空间里,占用着宝贵的内存。直到第7步,当fido声明为null
时,jim才变成不可达的,随后内存才能被释放。
在简单的脚本中,这是一个很小的、临时性的问题,但是这个例子展示了表面上很随意的决定对于垃圾回收过程所产生的影响。fido可能在删除jim后没有被直接删除,并且,如果它拥有记住多于一个前任主人的能力,在销毁之前可能会将大批Person对象密封在堆空间中,使其过着暗无天日的生活。如果我们选择以内嵌方式声明fido并且将那只猫声明为全局变量,将不存在任何这类的问题。为了评估fido行为的严重性,我们需要问自己以下的问题:
- 当它引用其他已删除的对象时,将会消耗多少内存?我们知道头脑简单的fido一次只能记住一个Person,但是尽管如此,Person可能还包含500个其他仅能通过他自身才能到达的对宠物猫的引用,因此额外的内存消耗可能无法估量。
- 额外的内存消耗将会保持多长时间?在这个简单的脚本中,答案是“不是很久”,但是我们可能稍后会在删除jim和删除fido之间添加额外的步骤。而且,JavaScript开发者总是以事件驱动的方式编程,因此,如果删除jim和删除fido发生在分离的事件处理函数中,我们将很难预言一个确定的答案。如果不去做某种类型的用例分析,甚至都无法给出一个概率性的答案。
任何一个问题都不像它们看起来那么容易回答。我们能够做的,就是在编写和修改代码时,对这类问题保持关注,并且执行测试以验证我们的假设是否正确。当编写代码的时候,我们就应该考虑应用的使用模式,而不只是在事后追悔莫及。
以上内容覆盖了内存管理的通用原则。
以上内容来自于 Ajax in action, 如有冒犯作者版权,请通知我立即删除. 同时觉得这篇文章不错,收藏一下,也与感兴趣的朋友门共享一下,哈
分享到:
相关推荐
JavaScript 避免内存泄露及内存管理技巧 在JavaScript开发中,内存泄露是一个常见的问题,它会导致页面崩溃、性能下降和用户体验不良。因此,了解如何避免内存泄露和有效地管理内存是非常重要的。本文将详细讲解...
本文实例讲述了JavaScript避免内存泄露及内存管理技巧,非常实用。分享给大家供大家参考之用。具体方法如下: 本文内容源自谷歌WebPerf(伦敦WebPerf集团),2014年8月26日。 一般来说,高效的JavaScript Web应用...
JavaScript内存泄露问题的解析 JavaScript内存泄露问题是一种常见的bug,它会导致系统崩溃和性能下降。内存泄露是指系统不能正确地管理内存分配的情况,这可能会导致程序调用失败、执行减慢等问题。 在JavaScript...
### JavaScript内存泄露详解 #### 一、什么是JavaScript内存泄露? 在JavaScript编程中,内存泄露指的是在浏览器中不再使用的变量或对象占用的内存没有被及时回收,导致可用内存逐渐减少的现象。这种现象通常发生...
本文将深入探讨JavaScript的内存机制,特别是如何避免内存泄露。 首先,我们来了解一下JavaScript的垃圾回收机制。在JavaScript中,程序员不需要手动释放内存,因为引擎会自动进行垃圾回收。当一个对象不再有任何...
在探讨JavaScript内存泄漏的处理方式前,有必要了解内存泄漏的含义。内存泄漏通常指的是程序不再使用的内存未能被释放,导致内存消耗不断上升。即使在高级语言如JavaScript中,这一问题亦可能出现,尽管它拥有垃圾...
JavaScript内存泄漏是一个重要的编程问题,尤其对于Web应用来说,它可能导致性能下降,用户界面响应变慢,甚至在极端情况下导致应用程序崩溃。理解内存泄漏的原因、如何检测和解决它们是每个JavaScript开发者必备的...
Iframe 内存泄露分析是指在使用 Iframe 时,由于互相引用、闭包、跨页面泄漏、伪泄漏等原因,导致浏览器内存泄漏的问题。这种问题在 Ajax 盛行以前并不是什么大问题,因为都是通过页面跳转和刷新来进行与服务端的...
sIEve是一款专门针对JavaScript内存泄漏检测的工具,版本为0.0.8。这款工具的主要目标是帮助开发者识别和定位JavaScript应用中的内存泄漏问题,以优化性能并提高用户体验。sIEve通过深入分析JavaScript运行时的内存...
5. 内存泄漏检测的重要性:内存泄漏检测对于JavaScript应用程序的性能和可靠性至关重要,可以避免应用程序的崩溃和性能下降。 6. JavaScript 的垃圾回收机制:JavaScript 语言具有垃圾回收机制,可以自动管理内存,...
为了避免JavaScript内存泄露,开发者应遵循以下最佳实践: - 尽量避免全局变量,必要时使用后将其设置为null或重新定义。 - 使用`setInterval`或`setTimeout`时,务必记得在不再需要时清除定时器。 - 理解并正确...
要避免JavaScript内存泄漏,开发者应该遵循以下原则: - 避免使用全局变量,尽可能使用局部变量。 - 及时解除DOM元素、事件监听器和其他对象的引用。 - 使用`window.onunload`或`window.addEventListener('...
本文将详细探讨JavaScript内存泄漏的原因,并展示一些常见的内存泄漏模式,以及如何解决这些问题。 首先,JavaScript 是一种垃圾收集语言,意味着对象创建时会分配内存,在没有更多引用时由浏览器回收这些内存。...
2. **内存泄漏类型**:常见的JavaScript内存泄露包括全局变量、闭包引用、DOM元素引用、事件监听器等。了解这些类型有助于识别潜在问题。 3. **工具使用**:利用如IE Developer Tools(F12工具)、Chrome DevTools...
【JavaScript源代码】一篇文章弄懂javascript内存泄漏 在JavaScript中,内存管理对于程序性能至关重要,因为内存泄漏会导致程序效率下降,甚至可能导致应用崩溃。本文旨在深入解析JavaScript中的内存泄漏及其解决...
在JavaScript中,由于其自动垃圾回收(Garbage Collection, GC)机制,程序员通常不会直接管理内存,但不恰当的编程习惯可能导致内存泄露。例如,全局变量、循环引用和闭包都可能导致内存无法被正确回收。 闭包是...
JavaScript内存泄漏是一个重要的主题,尤其是在现代Web开发中,因为页面长时间保持活跃且动态更新内容的情况越来越普遍。了解和处理内存泄漏对于优化Web应用性能至关重要。本文将深入探讨JavaScript中的几种常见内存...
在使用Electron结合Vue进行桌面应用开发时,内存泄漏是一个常见且需要重点关注的问题。内存泄漏问题的出现会逐渐消耗系统资源,最终可能导致应用崩溃或运行缓慢。在Electron中,内存泄漏主要跟主进程和渲染进程的...
6. **避免JavaScript与Android的长时间交互**:如果JavaScript长时间持有对Android对象的引用,也会导致内存泄漏。应使用`WebChromeClient`或`WebViewClient`的适当方法处理JavaScript与Java之间的交互,并确保及时...