jquery.data为什么可以避免怎么避免js的内存泄露露?怎么避免js的内存泄露露是怎么产生的?

最近用了一段时间的JQUERY,发现用JQUERY中的.append()方法动态生成数据时,内存不会回收,我不知道是不是我使用有问题,
还是JQUERY本身就带有这问题.
以下是我写的一个HTML,
最近用了一段时间的JQUERY,发现用JQUERY中的.append()方法动态生成数据时,内存不会回收,我不知道是不是我使用有问题,
还是JQUERY本身就带有这问题.
以下是我写的一个HTML, &html&
&table cellpadding="0" cellspacing="0"&
&tbody id="songList"&
&a href="#" onclick="getList()"&点击装载数据
&script type='text/javascript' src='jquery.js'&&/script&
&script type="text/javascript"&
$(document).ready(function(){
getList();
function getList() {
$("#songList").empty();
for(var i=0; i & 10; i++) {
var str='&tr&'
+'&td class="input"&'+ i +'&/td&'
+'&td class="gm"&&span&歌名歌名歌名歌名歌名歌名歌名歌名歌名歌名歌名歌名歌名歌名歌名歌名&/span&&/td&'
+'&td class="gx"&&span&&a href="#" onclick="getlist();"&歌星歌星歌星歌星歌星歌星&/a&&/span&&/td&'
+'&td class="mp3"&&span&&a href=""&mp3&/a&&/span&&/td&'
+'&td class="mv"&&span&&a href=""&mv&/a&&/span&&/td&'
+'&td class="ls"&&span&&a href=""&ls&/a&&/span&&/td&'
$("#songList").append(str);
每次"点击装载数据"链接,能在任务管理器看到此IE进程的内存加了500K,请高手看看,看看怎么解决
描述: jquery.js文件
下载次数: 61
论坛回复 /
(23 / 15924)
jQuery.fn.removeNode = function(){&&
&&&&&&&&&&&&&
&&&&&&&&&&& return function(){&&
&&&&&&&&&&&&&&& if(this[0] && this[0].tagName != 'BODY'){&&
&&&&&&&&&&&&&&&&&&& d = d || document.createElement('div');&&
&&&&&&&&&&&&&&&&&&& d.appendChild(this[0]);&&
&&&&&&&&&&&&&&&&&&& d.innerHTML = '';&&
&&&&&&&&&&&&&&& }&&
&&&&&&&&&&& }&&
&&&&&&& }();
先注册removeNode
之后再通过$('div').removeNode();就可以了。
不过没有清除与之相关的事件和expando中加入的data.
还是不完善。
Ext中的也是很有问题。
============================================
是的,我目前的也是清理的不是很干净
XXX.innerHTML=""
大家思路都是一样的
ayeah 写道搞javascript这么高层的东西还要研究内存回收?
当将IE嵌入其他客户端的时候就要关注这方面了,不然用户看到这个客户端的内存会越来越多
这个确实,我们也是将ie放到一个dll里给另一个厂商调用,ie访问我们一个web应用,里面用ajax生成树,xml来传送数据,我明显感觉到了速度太慢~。。。。
还好我们是做成dll,他们也看不出来到底是谁的问题
这里为什么会出现内存问题呢?
1. 递归的removeChild貌似可以解决问题?那么也就是说,之所以没有被回收,是因为下面这些node之间存在引用?
我估计是不应该在&table&&tbody&&/tbody&&/table&中间直接嵌套表格啊。这样应该会存有引用。
如有不对的地方,请多多指教。这里仅仅只是技术的交流以。
我试过将&table&&tbody&&/tbody&&/table&改成&div&&/div&,还是存在这个问题,
我又试了不使用jquery.js,将$("#songList").append(str);& 改成&&&&&
document.getElementById("songList").innerHTML=&
发现节点没有增加的,内存从每次增加500K变成了70K左右,这种情况是不是算正常了?这70K的内存新增是IE浏览器本身存在的内存没有回收问题还是JS内存没释放,还请知道的给解答一下,
以下是我用来测试的例子
&div id="songList"&
&a href="#" onclick="getList()"&点击装载数据&/a&
&script type="text/javascript"&
function getList() {
var str=""
for(var i=0; i & 10; i++) {
str+='歌名歌名歌名歌名歌名歌名歌名歌名歌名歌名歌名歌名歌名歌名歌名歌名';
document.getElementById("songList").innerHTML=
这种情况下点"点击装载数据",IE内存也会新增,我用的浏览器是IE6
搞javascript这么高层的东西还要研究内存回收?
当将IE嵌入其他客户端的时候就要关注这方面了,不然用户看到这个客户端的内存会越来越多
按道理不应该出现内存的问题
建议
1、到FF中去试一下,看看是不是也会出现这样的情况?
2、把&tbody&去掉,把id="songList"写在&table&中试一下?
关于上楼说的Ext的做法,只不过它的实现起来更方便,这种做法能不能完全释放内存也是个问题。
jquery是对每一个后代元素都进行了
jQuery.event.remove(this);
jQuery.removeData(this);
this.parentNode.removeChild(this);
不应该会出现内存的问题。
建议你先研究下fins这篇文章:http://fins.iteye.com/blog/172891
再下结论
& 上一页 1
zhanjianhua
浏览: 72144 次
来自: 上海
你好,我也遇到了这个问题,不过我的木有解决。如下:org.ap ...
你为什么要不把str声明成jquery对象,加上${}试下
XXX.outerHTML=&&;
更新
remove : function(selector) {// ...
jljlpch 写道jQuery.fn.removeNode
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'jquery.data为什么可以避免内存泄露?内存泄露是怎么产生的?-
相关资料介绍与图文
你现在的位置:& 》关于的详细解说
jquery.data为什么可以避免内存泄露?内存泄露是怎么产生的?
存泄漏可以定义为一个应用,由于某些原因不再需要的内存没有被操作系统或者空闲内存池回收。编程语言支持多种管理内存的方式。这些方式可能会减少内存泄漏的几率。然而,某一块内存是否没有用到实际上是一个不可判定的问题。换句话说,只有开发者可以弄清一块内存是否可以被操作系统回收。某些编程语言提供了帮助开发者做这个的特性。其他一些语言期望开发者可以完全明确什么时候一块内存是没被使用的。1.意外的全局变量JavaScript的目标是开发一种看起来像Java但足够自由的被初学者使用的语言。JavaScript自由的其中一种方式是它可以处理没有声明的变量:一个未声明的变量的引用在全局对象中创建了一个新变量。在浏览器的环境中,全局对象是window。也就是说:123function foo(arg) {bar = &this is a hidden global variable&;}实际上是:123function foo(arg) {window.bar = &this is an explicit global variable&;}如果bar是仅在foo函数作用域内承载引用,并且你忘记用var来声明的变量,一个意外的全局变量就被创建了。在这个例子中,泄漏一个单一字符串不会有太大害处,但这的确是不好的。另一种意外全局变量被创建的方式是通过this:1234567function foo() {this.variable = &potential accidental global&;}// Foo called on its own, this points to the global object (window)// rather than being undefined.foo();为了阻止这种错误发生,在你的Javascript文件最前面添加''。这开启了解析JavaScript的阻止意外全局的更严格的模式。全局变量的一个注意事项:即使我们谈了不明的全局变量,仍然存在很多代码被显式的全局变量填充的情况。这是通过定义不可收集的情况(除非清零或重新赋值)。特别的,用来临时存储和处理大量信息的全局变量会引起关注。如果必须用全局变量来存储很多数据,在处理完之后,确保对其清零或重新赋值。 一个在与全局连接上增加内存消耗常见的原因是缓存)。 缓存存储重复被使用的数据。为此,为了有效,缓存必须有其大小的上限。飙出限制的缓存可能会因为内容不可被回收,导致高内存消耗。2.被遗忘的计时器或回调在JavaScript中setInterval的使用相当常见。其他库提供观察者和其他工具以回调。这些库中大多数,在引用的实例变成不可访问之后,负责让回调的任何引用也不可访问。在setInterval的情况下,这样的代码很常见:var someResource = getData();setInterval(function() {var node = document.getElementById('Node');if(node) {// Do stuff with node and someResource.node.innerHTML = JSON.stringify(someResource));}}, 1000);这个例子表明了跳动的计时器可能发生什么:计时器使得节点或数据的引用不再被需要了。代表node的对象将来可能被移除,使得整个块在间隔中的处理不必要。然而,处理函数,由于间隔仍然是活跃的,不能被回收(间隔需要被停掉才能回收)。如果间隔处理不能被回收,它的依赖也不能被回收。那意味着可能存储着大量数据的someResource,也不能被回收。观察者情况下,一旦不被需要(或相关的对象快要访问不到)就创建明确移除他们的函数很重要。在过去,这由于特定浏览器(IE6)不能很好的管理循环引用(下面有更多相关信息),曾经尤为重要。现如今,一旦观察对象变成不可访问的,即使收听者没有明确的被移除,多数浏览器可以并会回收观察者处理函数。然而,它保持了在对象被处理前明确的移除这些观察者的好实践。例如:11213var element = document.getElementById('button');function onClick(event) {element.innerHtml = 'text';}element.addEventListener('click', onClick);// Do stuffelement.removeEventListener('click', onClick);element.parentNode.removeChild(element);// Now when element goes out of scope,// both element and onClick will be collected even in old browsers that don't// handle cycles well.一条关于对象观察者及循环引用的笔记观察者和循环引用曾经是JavaScript开发者的祸患。这是由于IE垃圾回收的一个bug(或者设计决议)出现的情况。IE的老版本不能检测到DOM节点和JavaScript代码间的循环引用。 这是一个通常为观察到的保留引用(如同上面的例子)的观察者的典型。 也就是说,每次在IE中对一个节点添加观察者的时候,会导致泄漏。这是开发者在节点或空引用之前开始明确的移除处理函数的原因。 现在,现代浏览器(包括IE和MS Edge)使用可以剪裁这些循环和正确处理的现代垃圾回收算法。换言之,在使一个节点不可访问前,调用removeEventLister不是严格意义上必须的。像Jquery一样的框架和库做了在处置一个节点前(当为其使用特定的API的时候)移除监听者的工作。这被在库内部处理,即使在像老版本IE一样有问题的浏览器里面跑,也会确保没有泄漏产生。3. 超出DOM引用有时存储DOM节点到数据结构中可能有用。假设你想要迅速的更新一个表格几行内容。存储每个DOM行节点的引用到一个字典或数组会起作用。当这发生是,两个对于同个DOM元素的引用被留存:一个在DOM树中,另外一个在字典中。如果在将来的某些点你决定要移除这些行,需要让两个引用都不可用。var elements = {button: document.getElementById('button'),image: document.getElementById('image'),text: document.getElementById('text')};function doStuff() {image.src = 'http://some.url/image';button.click();console.log(text.innerHTML);// Much more logic}function removeButton() {// The button is a direct child of body.document.body.removeChild(document.getElementById('button'));// At this point, we still have a reference to #button in the global// elements dictionary. In other words, the button element is still in// memory and cannot be collected by the GC.}对此的额外考虑,必须处理DOM树内的内部节点或叶子节点。假设你在JavaScript代码中保留了一个对于特定的表格内节点(一个td标签)的引用。在将来的某个点决定从DOM中移除这个表格,但是保留对于那个节点的引用。直观的,会假设GC会回收除那个节点之外的每个节点。在实践中,这不会发生的:这个单节点是那个表格的子节点,子节点保留对父节点引用。换句话说,来自JavaScript代码的表格元素的引用会引起在内存里存整个表格。当保留DOM元素的引用的时候,仔细考虑下。4.闭包一个JavaScript开发的关键点是闭包:从父级作用域捕获变量的匿名函数。很多开发者发现,由于JavaScript runtime的实现细节,有以一种微妙的方式泄漏的可能,这种特殊的情况:var theThing =var replaceThing = function () {var originalThing = theTvar unused = function () {if (originalThing)console.log(&hi&);};theThing = {longStr: new Array(1000000).join('*'),someMethod: function () {console.log(someMessage);}};};setInterval(replaceThing, 1000);这个代码片段做了一件事:每次replaceThing被调用的时候,theThing获取到一个包括一个大数组和新闭包(somMethod)的新对象。同时,变量unused保留了一个有originalThing(theThing从之前的对replaceThing的调用)引用的闭包。已经有点疑惑了,哈?重要的是一旦一个作用域被在同个父作用域下的闭包创建,那个作用域是共享的。这种情况下,为闭包somMethod创建的作用域被unused共享了。unused有一个对originalThing的引用。即使unused从来没被用过,someMethod可以通过theTing被使用。由于someMethod和unused共享了闭包作用域,即使unused从来没被用过,它对originalThing的引用迫使它停留在活跃状态(不能回收)。当这个代码片段重复运行的时候,可以看到内存使用稳步的增长。GC运行的时候,这并不会减轻。本质上,一组关联的闭包被创建(同unused变量在表单中的根节点一起),这些闭包作用域中每个带了大数组一个非直接的引用,导致了大型的泄漏。
存泄漏可以定义为一个应用,由于某些原因不再需要的内存没有被操作系统或者空闲内存池回收。编程语言支持多...
jQuery缓存系统的真正魅力在于其内部应用中,动画、事件等都有用到这个缓存系统。如果给DOM元素添...
并不是所有的闭包都有内存泄露问题,只要避免闭包内的对象和外部有循环引用的情况,就可以了
使用以下代码: $.extend($.ui.dialog.overlay, { instances:...
解决方案 这事做的方式jQuery的AJAX方法之一: 一个。创建循环引用,IE浏览器尤其是坏 乙。...
$.extend($.ui.dialog.overlay, { instances: [], old...
Copyright &排行榜大全pai-hang-bang.cn. Some Rights Reserved.从国家、城市、地区到品牌、产品、行业为你提供最新的排行榜资讯本站内容来自于本站编辑整理和媒体发布,仅提供参考或娱乐作用,并不构成任何投资标准和实际应用建议
联系请发电邮自god-loveme#163.com
【#换成@】Java中关于内存泄漏出现的原因汇总及如何避免内存泄漏(超详细版)
来先看看下面的例子,为什么会发生内存泄漏。下面这个例子中,A对象引用B对象,A对象的生命周期(t1-t4)比B对象的生命周期(t2-t3)长的多。当B对象没有被应用程序使用之后,A对象仍然在引用着B对象。这样,垃圾回收器就没办法将B对象从内存中移除,从而导致内存问题,因为如果A引用更多这样的对象,那将有更多的未被引用对象存在,并消耗内存空间。
B对象也可能会持有许多其他的对象,那这些对象同样也不会被垃圾回收器回收。所有这些没在使用的对象将持续的消耗之前分配的内存空间。
Android 内存泄漏总结
内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏大家都不陌生了,简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实例所持有却不再被使用导致 GC 不能回收。最近自己阅读了大量相关的文档资料,打算做个 总结 沉淀下来跟大家一起分享和学习,也给自己一个警示,以后 coding 时怎么避免这些情况,提高应用的体验和质量。
我会从 java 内存泄漏的基础知识开始,并通过具体例子来说明 Android 引起内存泄漏的各种原因,以及如何利用工具来分析应用内存泄漏,最后再做总结。
Java 内存分配策略
Java 程序运行时的内存分配策略有三种,分别是静态分配,栈式分配,和堆式分配,对应的,三种存储策略使用的内存空间主要分别是静态存储区(也称方法区)、栈区和堆区。
静态存储区(方法区):主要存放静态数据、全局 static 数据和常量。这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在。
栈区 :当方法被执行时,方法体内的局部变量(其中包括基础数据类型、对象的引用)都在栈上创建,并在方法执行结束时这些局部变量所持有的内存将会自动被释放。因为栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
堆区 : 又称动态内存分配,通常就是指在程序运行时直接 new 出来的内存,也就是对象的实例。这部分内存在不使用时将会由 Java 垃圾回收器来负责回收。
栈与堆的区别:
在方法体内定义的(局部变量)一些基本类型的变量和对象的引用变量都是在方法的栈内存中分配的。当在一段方法块中定义一个变量时,Java 就会在栈中为该变量分配内存空间,当超过该变量的作用域后,该变量也就无效了,分配给它的内存空间也将被释放掉,该内存空间可以被重新使用。
堆内存用来存放所有由 new 创建的对象(包括该对象其中的所有成员变量)和数组。在堆中分配的内存,将由 Java 垃圾回收器来自动管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,这个特殊的变量就是我们上面说的引用变量。我们可以通过这个引用变量来访问堆中的对象或者数组。
public class Sample {
int s1 = 0;
Sample mSample1 =
new Sample();
public void method() {
int s2 = 1;
Sample mSample2 =
new Sample();
Sample mSample3 =
new Sample();
Sample 类的局部变量 s2 和引用变量 mSample2 都是存在于栈中,但 mSample2 指向的对象是存在于堆上的。
mSample3 指向的对象实体存放在堆上,包括这个对象的所有成员变量 s1 和 mSample1,而它自己存在于栈中。
局部变量的基本数据类型和引用存储于栈中,引用的对象实体存储于堆中。—— 因为它们属于方法中的变量,生命周期随方法而结束。
成员变量全部存储与堆中(包括基本数据类型,引用和引用的对象实体)—— 因为它们属于类,类对象终究是要被new出来使用的。
了解了 Java 的内存分配之后,我们再来看看 Java 是怎么管理内存的。
Java是如何管理内存
Java的内存管理就是对象的分配和释放问题。在 Java 中,程序员需要通过关键字 new 为每个对象申请内存空间 (基本类型除外),所有的对象都在堆 (Heap)中分配空间。另外,对象的释放是由 GC 决定和执行的。在 Java 中,内存的分配是由程序完成的,而内存的释放是由 GC 完成的,这种收支两条线的方法确实简化了程序员的工作。但同时,它也加重了JVM的工作。这也是 Java 程序运行速度较慢的原因之一。因为,GC 为了能够正确释放对象,GC 必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC
都需要进行监控。
监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。
为了更好理解 GC 的工作原理,我们可以将对象考虑为有向图的顶点,将引用关系考虑为图的有向边,有向边从引用者指向被引对象。另外,每个线程对象可以作为一个图的起始顶点,例如大多程序从 main 进程开始执行,那么该图就是以 main 进程顶点开始的一棵根树。在这个有向图中,根顶点可达的对象都是有效对象,GC将不回收这些对象。如果某个对象 (连通子图)与这个根顶点不可达(注意,该图为有向图),那么我们认为这个(这些)对象不再被引用,可以被 GC 回收。
以下,我们举一个例子说明如何用有向图表示内存管理。对于程序的每一个时刻,我们都有一个有向图表示JVM的内存分配情况。以下右图,就是左边程序运行到第6行的示意图。
Java使用有向图的方式进行内存管理,可以消除引用循环的问题,例如有三个对象,相互引用,只要它们和根进程不可达的,那么GC也是可以回收它们的。这种方式的优点是管理内存的精度很高,但是效率较低。另外一种常用的内存管理技术是使用计数器,例如COM模型采用计数器方式管理构件,它与有向图相比,精度行低(很难处理循环引用的问题),但执行效率很高。
什么是Java中的内存泄露
在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。
在C++中,内存泄漏的范围更大一些。有些对象被分配了内存空间,然后却不可达,由于C++中没有GC,这些内存将永远收不回来。在Java中,这些不可达的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄露。
通过分析,我们得知,对于C++,程序员需要自己管理边和顶点,而对于Java程序员只需要管理边就可以了(不需要管理顶点的释放)。通过这种方式,Java提高了编程的效率。
因此,通过以上分析,我们知道在Java中也有内存泄漏,但范围比C++要小一些。因为Java从语言上保证,任何对象都是可达的,所有的不可达对象都由GC管理。
对于程序员来说,GC基本是透明的,不可见的。虽然,我们只有几个函数可以访问GC,例如运行GC的函数System.gc(),但是根据Java语言规范定义, 该函数不保证JVM的垃圾收集器一定会执行。因为,不同的JVM实现者可能使用不同的算法管理GC。通常,GC的线程的优先级别较低。JVM调用GC的策略也有很多种,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC。但通常来说,我们不需要关心这些。除非在一些特定的场合,GC的执行影响应用程序的性能,例如对于基于Web的实时系统,如网络游戏等,用户不希望GC突然中断应用程序执行而进行垃圾回收,那么我们需要调整GC的参数,让GC能够通过平缓的方式释放内存,例如将垃圾回收分解为一系列的小步骤执行,Sun提供的HotSpot
JVM就支持这一特性。
同样给出一个 Java 内存泄漏的典型例子,
Vector v = new
Vector(10);
(int i = 1; i & 100; i++) {
Object o = new
在这个例子中,我们循环申请Object对象,并将所申请的对象放入一个 Vector 中,如果我们仅仅释放引用本身,那么 Vector 仍然引用该对象,所以这个对象对 GC 来说是不可回收的。因此,如果对象加入到Vector 后,还必须从 Vector 中删除,最简单的方法就是将 Vector 对象设置为 null。
详细Java中的内存泄漏
1.Java内存回收机制
不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址。Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(Heap)中分配的,所有对象的回收都是由Java虚拟机通过垃圾回收机制完成的。GC为了能够正确释放对象,会监控每个对象的运行状况,对他们的申请、引用、被引用、赋值等状况进行监控,Java会使用有向图的方法进行管理内存,实时监控对象是否可以达到,如果不可到达,则就将其回收,这样也可以消除引用循环的问题。在Java语言中,判断一个内存空间是否符合垃圾收集标准有两个:一个是给对象赋予了空值null,以下再没有调用过,另一个是给对象赋予了新值,这样重新分配了内存空间。
2.Java内存泄漏引起的原因
内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费称为内存泄漏。内存泄露有时不严重且不易察觉,这样开发者就不知道存在内存泄露,但有时也会很严重,会提示你Out of memory。
Java内存泄漏的根本原因是什么呢?长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏,尽管短生命周期对象已经不再需要,但是因为长生命周期持有它的引用而导致不能被回收,这就是Java中内存泄漏的发生场景。具体主要有如下几大类:
1、静态集合类引起内存泄漏:
像HashMap、Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,他们所引用的所有的对象Object也不能被释放,因为他们也将一直被Vector等引用着。
Static Vector v =
new Vector(10);
(int i = 1; i&100; i++)
Object o = new
在这个例子中,循环申请Object 对象,并将所申请的对象放入一个Vector 中,如果仅仅释放引用本身(o=null),那么Vector 仍然引用该对象,所以这个对象对GC 来说是不可回收的。因此,如果对象加入到Vector 后,还必须从Vector 中删除,最简单的方法就是将Vector对象设置为null。
2、当集合里面的对象属性被修改后,再调用remove()方法时不起作用。
static void main(String[] args)
Set&Person& set =
new HashSet&Person&();
Person p1 =
new Person("唐僧","pwd1",25);
Person p2 =
new Person("孙悟空","pwd2",26);
Person p3 =
new Person("猪八戒","pwd3",27);
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println("总共有:"+set.size()+" 个元素!");
p3.setAge(2);
set.remove(p3);
set.add(p3);
System.out.println("总共有:"+set.size()+" 个元素!");
(Person person : set)
System.out.println(person);
在java 编程中,我们都需要和监听器打交道,通常一个应用当中会用到很多监听器,我们会调用一个控件的诸如addXXXListener()等方法来增加监听器,但往往在释放对象的时候却没有记住去删除这些监听器,从而增加了内存泄漏的机会。
4、各种连接
比如数据库连接(dataSourse.getConnection()),网络连接(socket)和io连接,除非其显式的调用了其close()方法将其连接关闭,否则是不会自动被GC 回收的。对于Resultset 和Statement 对象可以不进行显式回收,但Connection 一定要显式回收,因为Connection 在任何时候都无法自动回收,而Connection一旦回收,Resultset 和Statement 对象就会立即为NULL。但是如果使用连接池,情况就不一样了,除了要显式地关闭连接,还必须显式地关闭Resultset
Statement 对象(关闭其中一个,另外一个也会关闭),否则就会造成大量的Statement 对象无法释放,从而引起内存泄漏。这种情况下一般都会在try里面去的连接,在finally里面释放连接。
5、内部类和外部模块的引用
内部类的引用是比较容易遗忘的一种,而且一旦没释放可能导致一系列的后继类对象没有释放。此外程序员还要小心外部模块不经意的引用,例如程序员A 负责A 模块,调用了B 模块的一个方法如:
public void registerMsg(Object b);
这种调用就要非常小心了,传入了一个对象,很可能模块B就保持了对该对象的引用,这时候就需要注意模块B 是否提供相应的操作去除引用。
6、单例模式
不正确使用单例模式是引起内存泄漏的一个常见问题,单例对象在初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部的引用,那么这个对象将不能被JVM正常回收,导致内存泄漏,考虑下面的例子:
B.getInstance().setA(this);
static B instance=new
static B getInstance(){
void setA(A a){
显然B采用singleton模式,它持有一个A对象的引用,而这个A类的对象将不能被回收。想象下如果A是个比较复杂的对象或者集合类型会发生什么情况
Android中常见的内存泄漏汇总
集合类泄漏
集合类如果仅仅有添加元素的方法,而没有相应的删除机制,导致内存被占用。如果这个集合类是全局性的变量 (比如类中的静态属性,全局性的 map 等即有静态引用或 final 一直指向它),那么没有相应的删除机制,很可能导致集合所占用的内存只增不减。比如上面的典型例子就是其中一种情况,当然实际上我们在项目中肯定不会写这么 2B 的代码,但稍不注意还是很容易出现这种情况,比如我们都喜欢通过 HashMap 做一些缓存之类的事,这种情况就要多留一些心眼。
单例造成的内存泄漏
由于单例的静态特性使得其生命周期跟应用的生命周期一样长,所以如果使用不恰当的话,很容易造成内存泄漏。比如下面一个典型的例子,
class AppManager {
static AppM
AppManager(Context context) {
this.context =
static AppManager getInstance(Context context) {
(instance == null) {
instance =
new AppManager(context);
这是一个普通的单例模式,当创建这个单例的时候,由于需要传入一个Context,所以这个Context的生命周期的长短至关重要:
1、如果此时传入的是 Application 的 Context,因为 Application 的生命周期就是整个应用的生命周期,所以这将没有任何问题。
2、如果此时传入的是 Activity 的 Context,当这个 Context 所对应的 Activity 退出时,由于该 Context 的引用被单例对象所持有,其生命周期等于整个应用程序的生命周期,所以当前 Activity 退出时它的内存并不会被回收,这就造成泄漏了。
正确的方式应该改为下面这种方式:
class AppManager {
static AppM
AppManager(Context context) {
this.context = context.getApplicationContext();
static AppManager getInstance(Context context) {
(instance == null) {
instance =
new AppManager(context);
或者这样写,连 Context 都不用传进来了:
在你的 Application 中添加一个静态方法,getContext() 返回 Application 的 context,
context = getApplicationContext();
* 获取全局的context
* @return 返回全局context对象
static Context getContext(){
class AppManager {
static AppM
AppManager() {
this.context = MyApplication.getContext();
static AppManager getInstance() {
(instance == null) {
instance =
new AppManager();
匿名内部类/非静态内部类和异步线程
非静态内部类创建静态实例造成的内存泄漏
有的时候我们可能会在启动频繁的Activity中,为了避免重复创建相同的数据资源,可能会出现这种写法:
class MainActivity extends
AppCompatActivity {
static TestResource mResource = null;
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(mManager ==
mManager =
new TestResource();
TestResource {
这样就在Activity内部创建了一个非静态内部类的单例,每次启动Activity时都会使用该单例的数据,这样虽然避免了资源的重复创建,不过这种写法却会造成内存泄漏,因为非静态内部类默认会持有外部类的引用,而该非静态内部类又创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收。正确的做法为:
将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,请按照上面推荐的使用Application 的 Context。当然,Application 的 context 不是万能的,所以也不能随便乱用,对于有些地方则必须使用 Activity 的 Context,对于Application,Service,Activity三者的Context的应用场景如下:
其中: NO1表示 Application 和 Service 可以启动一个 Activity,不过需要创建一个新的 task 任务队列。而对于 Dialog 而言,只有在 Activity 中才能创建
匿名内部类
android开发经常会继承实现Activity/Fragment/View,此时如果你使用了匿名类,并被异步线程持有了,那要小心了,如果没有任何措施这样一定会导致泄露
class MainActivity extends
Activity {
Runnable ref1 =
new MyRunable();
Runnable ref2 =
new Runnable() {
void run() {
ref1和ref2的区别是,ref2使用了匿名内部类。我们来看看运行时这两个引用的内存:
可以看到,ref1没什么特别的。
但ref2这个匿名类的实现对象里面多了一个引用:
this$0这个引用指向MainActivity.this,也就是说当前的MainActivity实例会被ref2持有,如果将这个引用再传入一个异步线程,此线程和此Acitivity生命周期不一致的时候,就造成了Activity的泄露。
Handler 造成的内存泄漏
Handler 的使用造成的内存泄漏问题应该说是最为常见了,很多时候我们为了避免 ANR 而不在主线程进行耗时操作,在处理网络任务或者封装一些请求回调等api都借助Handler来处理,但 Handler 不是万能的,对于 Handler 的使用代码编写一不规范即有可能造成内存泄漏。另外,我们知道 Handler、Message 和 MessageQueue 都是相互关联在一起的,万一 Handler 发送的 Message 尚未被处理,则该 Message 及发送它的 Handler 对象将被线程 MessageQueue
一直持有。
由于 Handler 属于 TLS(Thread Local Storage) 变量, 生命周期和 Activity 是不一致的。因此这种实现方式一般很难保证跟 View 或者 Activity 的生命周期保持一致,故很容易导致无法正确释放。
举个例子:
class SampleActivity extends
Activity {
final Handler mLeakyHandler = new
Handler() {
void handleMessage(Message msg) {
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLeakyHandler.postDelayed(new
Runnable() {
void run() {
在该 SampleActivity 中声明了一个延迟10分钟执行的消息 Message,mLeakyHandler 将其 push 进了消息队列 MessageQueue 里。当该 Activity 被 finish() 掉时,延迟执行任务的 Message 还会继续存在于主线程中,它持有该 Activity 的 Handler 引用,所以此时 finish() 掉的 Activity 就不会被回收了从而造成内存泄漏(因 Handler 为非静态内部类,它会持有外部类的引用,在这里就是指 SampleActivity)。
修复方法:在 Activity 中避免使用非静态内部类,比如上面我们将 Handler 声明为静态的,则其存活期跟 Activity 的生命周期就无关了。同时通过弱引用的方式引入 Activity,避免直接将 Activity 作为 context 传进去,见下面代码:
class SampleActivity extends
Activity {
* Instances of static inner classes do not hold an implicit
* reference to their outer class.
static class MyHandler
extends Handler {
final WeakReference&SampleActivity& mA
MyHandler(SampleActivity activity) {
mActivity =
new WeakReference&SampleActivity&(activity);
void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
(activity != null) {
final MyHandler mHandler =
new MyHandler(this);
* Instances of anonymous classes do not hold an implicit
* reference to their outer class when they are "static".
static final
Runnable sRunnable = new
Runnable() {
void run() {
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler.postDelayed(sRunnable,
综述,即推荐使用静态内部类 + WeakReference 这种方式。每次使用前注意判空。
前面提到了 WeakReference,所以这里就简单的说一下 Java 对象的几种引用类型。
Java对引用的分类有 Strong reference, SoftReference, WeakReference, PhatomReference 四种。
在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。
软/弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。利用这个队列可以得知被回收的软/弱引用的对象列表,从而为缓冲器清除已失效的软/弱引用。
假设我们的应用会用到大量的默认图片,比如应用中有默认的头像,默认游戏图标等等,这些图片很多地方会用到。如果每次都去读取图片,由于读取文件需要硬件操作,速度较慢,会导致性能较低。所以我们考虑将图片缓存起来,需要的时候直接从内存中读取。但是,由于图片占用内存空间比较大,缓存很多图片需要很多的内存,就可能比较容易发生OutOfMemory异常。这时,我们可以考虑使用软/弱引用技术来避免这个问题发生。以下就是高速缓冲器的雏形:
首先定义一个HashMap,保存软引用对象。
Map &String, SoftReference&Bitmap&& imageCache = new
HashMap &String, SoftReference&Bitmap&& ();
再来定义一个方法,保存Bitmap的软引用到HashMap。
使用软引用以后,在OutOfMemory异常发生之前,这些缓存的图片资源的内存空间可以被释放掉的,从而避免内存达到上限,避免Crash发生。
如果只是想避免OutOfMemory异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。
另外可以根据对象是否经常使用来判断选择软引用还是弱引用。如果该对象可能会经常使用的,就尽量用软引用。如果该对象不被使用的可能性更大些,就可以用弱引用。
ok,继续回到主题。前面所说的,创建一个静态Handler内部类,然后对 Handler 持有的对象使用弱引用,这样在回收时也可以回收 Handler 持有的对象,但是这样做虽然避免了 Activity 泄漏,不过 Looper 线程的消息队列中还是可能会有待处理的消息,所以我们在 Activity 的 Destroy 时或者 Stop 时应该移除消息队列 MessageQueue 中的消息。
下面几个方法都可以移除 Message:
final void removeCallbacks(Runnable r);
final void removeCallbacks(Runnable r, Object token);
final void removeCallbacksAndMessages(Object token);
final void removeMessages(int
final void removeMessages(int
what, Object object);
尽量避免使用 static 成员变量
如果成员变量被声明为 static,那我们都知道其生命周期将与整个app进程生命周期一样。
这会导致一系列问题,如果你的app进程设计上是长驻内存的,那即使app切到后台,这部分内存也不会被释放。按照现在手机app内存管理机制,占内存较大的后台进程将优先回收,yi'wei如果此app做过进程互保保活,那会造成app在后台频繁重启。当手机安装了你参与开发的app以后一夜时间手机被消耗空了电量、流量,你的app不得不被用户卸载或者静默。
这里修复的方法是:
不要在类初始时初始化静态成员。可以考虑lazy初始化。
架构设计上要思考是否真的有必要这样做,尽量避免。如果架构需要这么设计,那么此对象的生命周期你有责任管理起来。
避免 override finalize()
1、finalize 方法被执行的时间不确定,不能依赖与它来释放紧缺的资源。时间不确定的原因是:
虚拟机调用GC的时间不确定
Finalize daemon线程被调度到的时间不确定
2、finalize 方法只会被执行一次,即使对象被复活,如果已经执行过了 finalize 方法,再次被 GC 时也不会再执行了,原因是:
含有 finalize 方法的 object 是在 new 的时候由虚拟机生成了一个 finalize reference 在来引用到该Object的,而在 finalize 方法执行的时候,该 object 所对应的 finalize Reference 会被释放掉,即使在这个时候把该 object 复活(即用强引用引用住该 object ),再第二次被 GC 的时候由于没有了 finalize reference 与之对应,所以 finalize 方法不会再执行。
3、含有Finalize方法的object需要至少经过两轮GC才有可能被释放。
资源未关闭造成的内存泄漏
对于使用了BraodcastReceiver,ContentObserver,File,游标 Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
一些不良代码造成的内存压力
有些代码并不造成内存泄露,但是它们,或是对没使用的内存没进行有效及时的释放,或是没有有效的利用已有的对象而是频繁的申请新内存。
Bitmap 没调用 recycle()方法,对于 Bitmap 对象在不使用时,我们应该先调用 recycle() 释放内存,然后才它设置为 null. 因为加载 Bitmap 对象的内存空间,一部分是 java 的,一部分 C 的(因为 Bitmap 分配的底层是通过 JNI 调用的 )。 而这个 recyle() 就是针对 C 部分的内存释放。
构造 Adapter 时,没有使用缓存的 convertView ,每次都在创建新的 converView。这里推荐使用 ViewHolder。
对 Activity 等组件的引用应该控制在 Activity 的生命周期之内; 如果不能就考虑使用 getApplicationContext 或者 getApplication,以避免 Activity 被外部长生命周期的对象引用而泄露。
尽量不要在静态变量或者静态内部类中使用非静态外部成员变量(包括context ),即使要使用,也要考虑适时把外部成员变量置空;也可以在内部类中使用弱引用来引用外部类的变量。
对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变量,可以这样做避免内存泄漏:
将内部类改为静态内部类
静态内部类中使用弱引用来引用外部类的成员变量
Handler 的持有的引用对象最好使用弱引用,资源释放时也可以清空 Handler 里面的消息。比如在 Activity onStop 或者 onDestroy 的时候,取消掉该 Handler 对象的 Message和 Runnable.
在 Java 的实现过程中,也要考虑其对象释放,最好的方法是在不使用某对象时,显式地将此对象赋值为 null,比如使用完Bitmap 后先调用 recycle(),再赋为null,清空对图片等资源有直接引用或者间接引用的数组(使用 array.clear() ; array = null)等,最好遵循谁创建谁释放的原则。
正确关闭资源,对于使用了BraodcastReceiver,ContentObserver,File,游标 Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销。
保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期。
以上所述是小编给大家介绍的Java中关于内存泄漏出现的原因汇总及如何避免内存泄漏(超详细版),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!

我要回帖

更多关于 jquery data 的文章

 

随机推荐