那些操作会造成内存泄漏

2021-02-24 20:37发布

12条回答
20200921文 - 做更棒的自己!
2楼 · 2021-02-25 09:22

严格意义上的内存泄露的原因只有一种:没有释放向系统申请的内存,因为不申请内存,就谈不上什么泄露,搞清楚内存泄露的原因,应当从汇编语言的角度考虑问题。

当然没有释放内存的原因是多种的:

有可能是你自己代码写的不好,忘记了释放自己代码里申请的内存,

也有可能是你使用了一个写的不好的库,库本身有问题,这里说的库不仅仅是第三方库,甚至于各种语言的运行时库也有可能出现(再高的人都免不了出BUG),还甚至于操作系统的库,因为操作系统的BUG也多的很(当然系统一般情况不会出现这些低级的错误)。

死循环不能说是内存泄露,概念上应称为死锁,死锁的确是有可能会导致内存无限量增长,但其与内存泄露有本质区别,尽管有时候它们导致的结果在内存层面是相同的。


ban_gank
3楼 · 2021-02-25 09:26

1、JS的回收机制

JavaScript垃圾回收的机制很简单:找出不再使用的变量,然后释放掉其占用的内存,但是这个过程不是实时的,因为其开销比较大,所以垃圾回收系统(GC)会按照固定的时间间隔,周期性的执行。

到底哪个变量是没有用的?所以垃圾收集器必须跟踪到底哪个变量没用,对于不再有用的变量打上标记,以备将来收回其内存。用于标记的无用变量的策略可能因实现而有所区别,通常情况下有两种实现方式:标记清除引用计数。引用计数不太常用,标记清除较为常用。


2、标记清除

js中最常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。


function test(){var a=10;//被标记,进入环境var b=20;//被标记,进入环境}test();//执行完毕之后a、b又被标记离开环境,被回收


3、引用此时


引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值(function object array)赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为0的值所占用的内存。


function test(){var a={};//a的引用次数为0var b=a;//a的引用次数加1,为1var c=a;//a的引用次数加1,为2var b={};//a的引用次数减1,为1}


4、哪些操作会造成内存泄露


1)意外的全局变量引起的内存泄露


function leak(){leak="xxx";//leak成为一个全局变量,不会被回收}


2)闭包引起的内存泄露



function bindEvent(){var obj=document.createElement("XXX");obj.οnclick=function(){//Even if it's a empty function}}

闭包可以维持函数内局部变量,使其得不到释放。 上例定义事件回调时,由于是函数内定义函数,并且内部函数--事件回调的引用外暴了,形成了闭包。


解决之道,将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,删除对dom的引用。

//将事件处理函数定义在外部function onclickHandler(){//do something}function bindEvent(){var obj=document.createElement("XXX");obj.οnclick=onclickHandler;}


//在定义事件处理函数的外部函数中,删除对dom的引用function bindEvent(){var obj=document.createElement("XXX");obj.οnclick=function(){//Even if it's a empty function}obj=null;}


3)没有清理的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[removed])}function removeButton(){document.body.removeChild(document.getElementById('button'))}


4)被遗忘的定时器或者回调



var someResouce=getData();setInterval(function(){var node=document.getElementById('Node');if(node){node[removed]=JSON.stringify(someResouce)}},1000)

这样的代码很常见, 如果 id 为 Node 的元素从 DOM 中移除, 该定时器仍会存在, 同时, 因为回调函数中包含对 someResource 的引用, 定时器外面的 someResource 也不会被释放。



5)子元素存在引起的内存泄露



黄色是指直接被 js变量所引用,在内存里,红色是指间接被 js变量所引用,如上图,refB 被 refA 间接引用,导致即使 refB 变量被清空,也是不会被回收的子元素 refB 由于 parentNode 的间接引用,只要它不被删除,它所有的父元素(图中红色部分)都不会被删除。


6)IE7/8引用计数使用循环引用产生的问题


function fn(){var a={};var b={};a.pro=b;b.pro=a;}fn();

fn()执行完毕后,两个对象都已经离开环境,在标记清除方式下是没有问题的,但是在引用计数策略下,因为a和b的引用次数不为0,所以不会被垃圾回收器回收内存,如果fn函数被大量调用,就会造成内存泄漏。在IE7与IE8上,内存直线上升。


IE中有一部分对象并不是原生js对象。例如,其内存泄漏DOM和BOM中的对象就是使用C++以COM对象的形式实现的,而COM对象的垃圾回收机制采用的就是引用计数策略。因此,即使IE的js引擎采用标记清除策略来实现,但js访问的COM对象依然是基于引用计数策略的。换句话说,只要在IE中涉及COM对象,就会存在循环引用的问题。


var element=document.getElementById("some_element");var myObject=new Object();myObject.e=element;element.o=myObject;

上面的例子在一个DOM元素(element)与一个原生js对象(myObject)之间创建了循环引用。其中,变量myObject有一个名为e的属性指向element对象;而变量element也有一个属性名为o回指myObject。由于存在这个循环引用,即使例子中的DOM从页面中移除,它也永远不会被回收。


看上面的例子,有人会觉得太弱了,谁会做这样无聊的事情,但是其实我们经常会这样做


window.οnlοad=function outerFunction(){var obj=document.getElementById("element"):obj.οnclick=function innerFunction(){};};

这段代码看起来没什么问题,但是obj引用了document.getElementById(“element”),而document.getElementById(“element”)的onclick方法会引用外部环境中的变量,自然也包括obj,是不是很隐蔽啊。


最简单的解决方式就是自己手工解除循环引用,比如刚才的函数可以这样

myObject.element=null;element.o=null;window.οnlοad=function outerFunction(){var obj=document.getElementById("element"):obj.οnclick=function innerFunction(){};obj=null;};

将变量设置为null意味着切断变量与它此前引用的值之间的连接。当垃圾回收器下次运行时,就会删除这些值并回收它们占用的内存。 要注意的是,IE9+并不存在循环引用导致Dom内存泄漏问题,可能是微软做了优化,或者Dom的回收方式已经改变


5、如何分析内存的使用情况

Google Chrome浏览器提供了非常强大的JS调试工具,Memory 视图  profiles 视图让你可以对 JavaScript 代码运行时的内存进行快照,并且可以比较这些内存快照。它还让你可以记录一段时间内的内存分配情况。在每一个结果视图中都可以展示不同类型的列表,但是对我们最有用的是 summary 列表和 comparison 列表。  summary 视图提供了不同类型的分配对象以及它们的合计大小:shallow size (一个特定类型的所有对象的总和)和 retained size (shallow size 加上保留此对象的其它对象的大小)。distance 显示了对象到达 GC 根(校者注:最初引用的那块内存,具体内容可自行搜索该术语)的最短距离。 comparison 视图提供了同样的信息但是允许对比不同的快照。这对于找到泄漏很有帮助。


6、怎样避免内存泄露

1)减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收;

2)注意程序逻辑,避免“死循环”之类的 ;

3)避免创建过多的对象  原则:不用了的东西要及时归还。


思禹小姐姐y
4楼 · 2021-02-25 09:47

1)意外的全局变量引起的内存泄露

2)闭包引起的内存泄露

3)没有清理的DOM元素引用

4)被遗忘的定时器或者回调

5)子元素存在引起的内存泄露

6)IE7/8引用计数使用循环引用产生的问题

lemon
5楼 · 2021-02-25 09:55

内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。

垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0(没有其他对象引用过该对象),或对该对象的惟一引用是循环的,那么该对象的内存即可回收。

setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。

闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)


浅浅77
6楼 · 2021-02-25 13:55

意外的全局变量引起的内存泄漏

image.png

你可以通过加上 'use strict' 启用严格模式来避免这类问题, 严格模式会阻止你创建意外的全局变量.

闭包引起的内存泄漏

image.png

闭包可以维持函数内局部变量,使其得不到释放。 上例定义事件回调时,由于是函数内定义函数,并且内部函数--事件回调的引用外暴了,形成了闭包 解决之道,将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,删除对dom的引用

image.png

image.png

没有清理的DOM元素引用

image.png

虽然我们用removeChild移除了button, 但是还在elements对象里保存着#button的引用,换言之, DOM元素还在内存里面

被遗忘的定时器或者回调

image.png

这样的代码很常见, 如果 id 为 Node 的元素从 DOM 中移除, 该定时器仍会存在, 同时, 因为回调函数中包含对 someResource 的引用, 定时器外面的 someResource 也不会被释放.

子元素存在引用引起的内存泄漏

image.png

黄色是指直接被 js变量所引用,在内存里,红色是指间接被 js变量所引用,如上图,refB 被 refA 间接引用,导致即使 refB 变量被清空,也是不会被回收的子元素 refB 由于 parentNode 的间接引用,只要它不被删除,它所有的父元素(图中红色部分)都不会被删除。

我想吃肉
7楼 · 2021-02-25 18:30

首先让我们从另一个角度来看,如何主动发生内存泄漏呢?当然是想办法给他一个一直存在的强引用了。


static

static这个关键字使一个变量变为只和这个类相关的类变量,和实例无关。他的生命周期是很长的,贯穿于app的启动到关闭。因此只要用一个static引用一个大对象,就可以泄漏了!举个例子:


static Activity activity;


这是最简单粗暴的持有一个activity的引用,这样这个activity退出之后对象并没有被销毁。


static View view;


一个View初始化时会用到context,我们在自定义View,重写构造方法时就知道这个了。因此如果一个View也像这样被持有,那个context也不会被释放。


innerClass

内部类有个特性,是他会持有一个外部类的引用。如果内部类的实例一直存活,那么外部类activity的实例也就一直在。比如持有一个static的内部类引用:

图片

或者以前我们用asynctask时喜欢搞一个匿名内部类执行异步任务,那当我们activity退出后这个异步任务还在执行的话,就会泄露了。

图片

还有自己开个匿名线程:

图片

还有在使用handler时,如果用了匿名handler,那么这个handler会带着activity的引用藏到消息队列中。消息没有被处理,就会造成内存泄漏。类似的,还有timertask等。


register

我们平时会用到很多第三方库,比如ButterKnife EventBus RxJava等等,有的时候要获取系统服务,getSystemService。在使用的时候,都有一个先registerd或者bind的操作,而且在创建的时候会把activity的引用传过去。如果在activity结束时没有unregister或者unbind,就会造成内存泄漏。


刘家起 - coding
8楼 · 2021-02-26 09:08

内存泄漏是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。在C++中,因为是手动管理内存,内存泄漏是经常出现的事情。而现在流行的C#和Java等语言采用了自动垃圾回收方法管理内存,正常使用的情况下几乎不会发生内存泄漏。浏览器中也是采用自动垃圾回收方法管理内存,但由于浏览器垃圾回收方法有bug,因此会产生内存泄漏

1、意外的全局变量引起的内存泄漏

2、闭包引起的内存泄漏

3、没有清理的DOM元素引用

4、被遗忘的定时器或者回调

5、子元素存在引用引起的内存泄漏


熊晓燕
9楼 · 2021-02-26 09:47

严格意义上的内存泄露的原因只有一种:没有释放向系统申请的内存,因为不申请内存,就谈不上什么泄露,搞清楚内存泄露的原因,应当从汇编语言的角度考虑问题。

当然没有释放内存的原因是多种的:

有可能是你自己代码写的不好,忘记了释放自己代码里申请的内存,

也有可能是你使用了一个写的不好的库,库本身有问题,这里说的库不仅仅是第三方库,甚至于各种语言的运行时库也有可能出现(再高的人都免不了出BUG),还甚至于操作系统的库,因为操作系统的BUG也多的很(当然系统一般情况不会出现这些低级的错误)。

死循环不能说是内存泄露,概念上应称为死锁,死锁的确是有可能会导致内存无限量增长,但其与内存泄露有本质区别,尽管有时候它们导致的结果在内存层面是相同的。


相关问题推荐

  • 回答 120

    相对前几年来说,要高上不少了,毕竟入行的人也是越来越多了,基础的工作对应想要参与的人群基数越来越大,但是对于高端人才的需求还是很多,人才还是相对稀缺性的。所以,想要学web或者其他技术也一样,别等,别观望。web前端就业方向特别多包括web前端开发...

  • 回答 25

    相对定位和绝对定位是定位的两种表现形式,区别如下:一、主体不同1、相对定位:是设置为相对定位的元素框会偏移某个距离。2、绝对定位:absolute 脱离文档流,通过 top,bottom,left,right 定位。二、特点不同1、相对定位:在使用相对定位时,无论是否进行移...

  • 抓包是什么意思?2020-04-01 17:36
    回答 7
    已采纳

    抓包(packet capture)就是将网络传输发送与接收的数据包进行截获、重发、编辑、转存等操作,也用来检查网络安全。抓包也经常被用来进行数据截取等。抓包可以通过抓包工具来查看网络数据包内容。通过对抓获的数据包进行分析,可以得到有用的信息。目前流行的...

  • 回答 89

    常用的前端框架有Bootstrap框架、React框架、Vue框架、Angular框架、Foundation框架等等

  • 回答 65
    已采纳

    前端是目的就业前景非常不错的一个计算机技术,但是自学的话还是有一定难度的,网络上自学是碎片化的,同时互联网技术跟新换代快,自己的话比较吃力也学习不到最新的技术。

  • SSR 是什么意思?2020-03-20 18:56
    回答 6

    SSR就是一台服务器,可以利用 SSR 在远程的服务器上配置 SSR,使其能够成为 SSR 节点,这样本地电脑或者其它设备利用 SSR 节点实现 VPN 或者远程上网及游戏加速等方面。ShadowsocksR(简称 SSR)是 Shadowsocks 分支,在 Shadowsocks 的基础上增加了一些数据...

  • 回答 11

    1、代码判断xAxis: {type: 'time',splitLine: {show: false},interval: 3600, // 设置x轴时间间隔axisLabel: {formatter: function(value, index) {return liangTools.unix2hm(value)}}},首先要把xAxis 显示类型设置成time,然后设置对应X轴......

  • 回答 52
    已采纳

    计算机培训方向比较多,建议找适合自己的方向选择培训编程类:JAVA、WEB、Python、C/C++、C#等测试类:软件测试运维类:云计算、网络安全设计类:UI设计、3D建模等

  • 回答 8

    HTML5 + CSS + JavaScript 开发 跨平台重用代码 

  • 回答 4

    采用rem单位自动响应,并提供独有栅格化系统快速定义宽高、边距节省css代码量,同时总结各大型移动端网页,提供一套ui颜色搭配规范,尺寸规范,字体规范等。

  • 回答 10

    iView UI、ioni、SUI

  • 回答 6

     jQTouch 

  • 回答 4

    如果只是普通的移动端用vue react 或者dva 如果是要编译成小程序什么的或者混生 就用uni-app(对应vue语法)taro(对应react) 或者纯原生 这个没有限制的,自己怎么舒服怎么来

  • 回答 4

    因为可以运用在网页和小程序的开饭中,而且开源,用着便宜,企业都很喜欢

  • 回答 10

    一、Visual Studio Code下载地址:https://code.visualstudio.com/微软在2015年4月30日Build 开发者大会上正式宣布了 Visual Studio Code 项目:一个运行于 Mac OS X、Windows和 Linux 之上的,针对于编写现代 Web 和云应用的跨平台源代码编辑器。Visual Stud...

  • 回答 9

    jQuery自带淡入淡出效果 https://www.w3school.com.cn/jquery/jquery_fade.asp 看看这个 

没有解决我的问题,去提问