几个内存泄漏的例子
- 格式:doc
- 大小:61.50 KB
- 文档页数:7
常见的内存泄漏以及解决⽅案⼀、什么是内存泄漏? 系统进程不再⽤到的内存,没有及时的释放,就叫做内存泄漏。
⼆、JS引起内存泄漏的原因?1、意外的全局变量 由于js对没有进⾏申明的变量会默认是在全局变量上定义的,⽽系统的全局变量是 window,只有关闭窗⼝和刷新页⾯,全局变量才会被释放,如果在⼀个没有声明的变量上保存了⼤量的数据,这些数据就会保存在全局变量上,当这些数据没有及时的被回收,就会发⽣内存泄漏。
没有声明的变量function fn(){a='hello'}fn();使⽤this申明的变量function fn(){// 这⾥的this指向它的调⽤者,他的调⽤者是windowthis.a='hello';}fn() 解决⽅法: 避免使⽤没有声明的变量; 使⽤严格模式,在js⽂件头部或者是函数⾸⾏使⽤严格模式2、闭包引⽤的内存泄漏 由于闭包可以访问函数内部的变量,让这些变量⼀直保存在内存中,如果没有及时的清理掉这些变量,就会发⽣内存泄漏。
function fn(){var a='i am a';return function(){console.log(a);}} 解决⽅法:将事件处理程序定义在函数的外部// badfor(var k=0;k<10;k++){var t=function(a){console.log(a)}t(k)}// goodfunction t(a){console.log(a)}for(var k=0;k<10;k++){t(k)}t=null3、Dom元素的引⽤没有被释放 虽然在别的地⽅Dom别删除了,但是对象对这个Dom元素的引⽤并没有被删除var element={btn:document.getElementById('btn')}function doSomeThing(){element.btn.cilck()}function removeClick(){// 虽然移除了dom中的btn元素,但是对象中对btn的引⽤还是没有被删除document.body.removeChild(document.getElementById( 'btn' )) 解决⽅法:将element.btn=null4、被遗忘的定时器或者回调函数 定时器中有dom的引⽤,即使dom删除了,但是定时器还在,所以内存中还是会有这个dom。
内存泄露和内存溢出的原因
内存泄露和内存溢出是常见的程序运行问题,导致程序的性能下降或崩溃。
其原因可能来自以下几个方面:
1. 代码错误:程序员编写的代码中可能存在逻辑错误或者疏忽导致内存泄露或者溢出。
2. 无限循环:程序中的无限循环会不断占用内存,当内存达到极限时,程序就会崩溃或者出现内存溢出的问题。
3. 大量数据处理:当程序需要处理大量的数据时,如果没有合理地管理内存,就会导致内存溢出。
4. 内存管理不当:内存管理不当也会导致内存泄露或者溢出。
比如,如果程序没有及时释放不再使用的内存,就会导致内存泄露;如果程序请求的内存超过了系统所能提供的内存,就会导致内存溢出。
5. 外部因素:除了程序内部的原因,外部因素也可能导致内存泄露或者溢出。
比如,系统资源不足、硬件故障、病毒攻击等都会影响程序的内存使用情况。
综上所述,内存泄露和内存溢出的原因可能来自多个方面,程序员在编写程序时需要格外注意,合理管理内存,避免出现这些问题。
- 1 -。
内存溢出和内存泄漏的区别,产⽣原因以及解决⽅案1.1内存溢出:(Out Of Memory---OOM)系统已经不能再分配出你所需要的空间,⽐如你需要100M的空间,系统只剩90M了,这就叫内存溢出例⼦:⼀个盘⼦⽤尽各种⽅法只能装4个果⼦,你装了5个,结果掉倒地上不能吃了。
这就是溢出。
⽐⽅说栈,栈满时再做进栈必定产⽣空间溢出,叫上溢,栈空时再做退栈也产⽣空间溢出,称为下溢。
就是分配的内存不⾜以放下数据项序列,称为内存溢出。
说⽩了就是我承受不了那么多,那我就报错,1.2内存泄漏: (Memory Leak)----》强引⽤所指向的对象不会被回收,可能导致内存泄漏,虚拟机宁愿抛出OOM也不会去回收他指向的对象意思就是你⽤资源的时候为他开辟了⼀段空间,当你⽤完时忘记释放资源了,这时内存还被占⽤着,⼀次没关系,但是内存泄漏次数多了就会导致内存溢出例⼦:你向系统申请分配内存进⾏使⽤(new),可是使⽤完了以后却不归还(delete),结果你申请到的那块内存你⾃⼰也不能再访问(也许你把它的地址给弄丢了),⽽系统也不能再次将它分配给需要的程序。
就相当于你租了个带钥匙的柜⼦,你存完东西之后把柜⼦锁上之后,把钥匙丢了或者没有将钥匙还回去,那么结果就是这个柜⼦将⽆法供给任何⼈使⽤,也⽆法被垃圾回收器回收,因为找不到他的任何信息。
⼀般我们所说的内存泄漏指的是堆内存的泄露,堆内存是指程序从堆中分配的,⼤⼩随机的⽤完后必须显⽰释放的内存,C++/C中有free函数可以释放内存,java中有垃圾回收机制不⽤程序员⾃⼰⼿动调⽤释放如果这块内存不释放,就不能再⽤了,这就叫这块内存泄漏了--------------------------------------------------------------------------------------------------------------------------------------------------------------------2.以发⽣的⽅式来分类,内存泄漏可以分为4类:1. 常发性内存泄漏。
c语言中造成内存泄漏几种方式
在C语言中,内存泄漏是一种常见的问题,通常是由于程序员
未正确管理动态分配的内存而导致的。
以下是导致内存泄漏的几种
常见方式:
1. 未释放动态分配的内存,在C语言中,使用malloc、
calloc或realloc等函数动态分配内存后,需要使用free函数来
释放这些内存。
如果程序员忘记释放这些内存,就会导致内存泄漏。
2. 丢失对动态分配内存的指针,当程序员在动态分配内存后,
将指向该内存的指针重新赋值或者丢失该指针,而没有释放内存,
就会导致内存泄漏。
3. 在循环中重复动态分配内存而未释放,如果在循环中重复动
态分配内存,但未在每次循环结束时释放内存,就会导致内存泄漏。
4. 函数内部动态分配内存未释放,在函数内部动态分配内存后,如果忘记在函数返回前释放内存,就会导致内存泄漏。
5. 不正确使用内存分配和释放函数,在C语言中,使用错误的
内存分配和释放函数也可能导致内存泄漏。
例如,使用malloc分配内存后,却使用free函数来释放内存。
综上所述,内存泄漏在C语言中可能由多种原因引起,程序员需要仔细管理动态分配的内存,确保在不再需要时及时释放内存,以避免内存泄漏问题的发生。
案例实战(⼋)⼀个数据同步系统频繁OOM内存溢出的排查实践案例背景⾸先说⼀下案例背景,线上有⼀个数据同步系统,是专门负责从另外⼀个系统去同步数据的,简单来说,另外⼀个系统会不停的发布⾃⼰的数据到Kafka中去,然后我们有⼀个数据同步系统就专门从Kafka⾥消费数据,接着保存到⾃⼰的数据库中去,⼤概就是这样的⼀个流程。
我们看下图,就是这个系统运⾏的⼀个流程。
结果就这么⼀个⾮常简单的系统,居然时不时就报⼀个内存溢出的错误,然后就得重启系统,过了⼀段时间⼜会再次内存溢出⼀下。
⽽且这个系统处理的数据量是越来越⼤,因此我们发现他内存溢出的频率越来越⾼,到这个情况,就必须要处理⼀下了。
经验丰富的⼯程师:从现象看到本质⼀般遇到这种现象,只要是经验丰富的⼯程师,应该已经可以具备从现象看到本质的能⼒了。
我们可以来分析和思考⼀下,既然每次重启过后都会在⼀段时间以后出现内存溢出的问题,说明肯定是每次重启过后,内存都会不断的上涨。
⽽且⼀般要⾼到 JVM 出现内存溢出,通常就是两种情况,要不然是并发太⾼,瞬间⼤量并发创建过多的对象,导致系统直接崩溃了。
要不就是有内存泄漏之类的问题,就是很多对象都赖在内存⾥,⽆论你如何GC就是回收不掉。
那么这个场景是怎么回事呢?我们当时分析了⼀下,这个系统的负载并不是很⾼,虽然数据量不少,但并不是那种瞬时⾼并发的场景。
这么看来,很可能就是随着时间推移,有某种对象越来越多,赖在内存⾥了。
然后不断的触发gc,结果每次gc都回收不掉这些对象。
⼀直到最后,内存实在不⾜了,就会内存溢出我们看看下⾯的图,在下图⾥就画出了这个问题。
通过 jstat 来确认我们的推断接着直接在⼀次重启系统之后,⽤jstat观察了⼀下JVM运⾏的情况:我们发现,⽼年代的对象⼀直在增长,不停的在增长。
每次Young GC过后,⽼年代的对象就会增长不少。
⽽且当⽼年代的使⽤率达到 100% 之后,我们发现会正常触发 Full GC,但是 Full GC 根本回收不掉任何对象,导致⽼年代使⽤率还是100%!然后⽼年代使⽤率维持100%⼀段时间过后,就会报内存溢出的问题,因为再有新的对象进⼊⽼年代,实在没有空间放他了!所以这就基本确认了我们的判断,每次系统启动,不知道什么对象会⼀直进⼊堆内存,⽽且随着Young GC执⾏,对象会⼀直进⼊⽼年代,最后触发Full GC都⽆法回收⽼年代的对象,最终就是内存溢出。
什么是内存泄漏?⽆⽤的对象占据着内存空间,使得实际可使⽤内存变⼩,形象地说法就是内存泄漏了。
不再⽤到的内存,没有及时释放,就叫做内存泄漏(memory leak)。
- 内存溢出(Out Of Memory):“你内存⼀共就剩1MB,⾮要存个1GB的数据,存⼩点不⾏吗?要不再加点内存空间好不好,还存,还存溢出了昂,⼀库⼀库~”- 内存泄漏(Memory Leak):“你声明了⼀个⼜⼀个局部引⽤变量,都⽤完了还不让垃圾回收,空间都被占⽤光了晓得不啦,快点把这块⽤不到的内存给⽼⼦释放了!”全局变量在页⾯关闭之前是不会被浏览器所回收的。
它们就成了占⽤内存的冗余代码。
var a=new Object;var b=new Object;a.r=b;b.r=a;- 第⼆种:循环引⽤⾃⼰var a=new Object;a.r=a;2.闭包在IE浏览器中会形成内存泄漏。
为早期IE是使⽤C/C++引擎,他们的垃圾回收机制是通过引⽤计数这种⽅式。
所以闭包中的引⽤⼀直不清零就会形成泄漏。
3.全局变量在页⾯关闭之前都不会被释放,存在内存泄漏,但使⽤严格模式可以避免。
4.没有清理的DOM元素引⽤。
⽐如将DOM节点引⽤存储起来,再删掉DOM节点,但是引⽤未清理。
它依然存在于内存中。
**/** DOM 节点绑定了事件, 但是在移除的时候没有解除事件绑定,那么仅仅移除 DOM 节点也是没⽤的var element = {shotCat: document.getElementById('shotCat')};document.body.removeChild(document.getElementById('shotCat'));// 如果element没有被回收,这⾥移除了 shotCat 节点也是没⽤的,shotCat 节点依然留存在内存中.//解决⽅法:清除绑定的事件,即可从内存中移除<div id="container"></div>$('#container').bind('click', function(){console.log('click');});$('#container').off('click').remove();//移除绑定的事件5.被遗忘的定时器**setInterval**以及其中的引⽤。
内存溢出和内存泄漏的区别(内存泄漏原因)内存溢出out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
内存泄露memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
memory leak会最终会导致out of memory!内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。
内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。
一个盘子用尽各种方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。
这就是溢出!比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。
就是分配的内存不足以放下数据项序列,称为内存溢出.以发生的方式来分类,内存泄漏可以分为4类:1. 常发性内存泄漏。
发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
2. 偶发性内存泄漏。
发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。
常发性和偶发性是相对的。
对于特定的环境,偶发性的也许就变成了常发性的。
所以测试环境和测试方法对检测内存泄漏至关重要。
3. 一次性内存泄漏。
发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。
比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。
4. 隐式内存泄漏。
程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。
严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。
内存泄漏排查流程过程和方法一、内存泄漏的初步判断1.1 观察系统症状当怀疑有内存泄漏时,首先得看看系统的一些表现。
如果系统变得越来越慢,就像蜗牛爬一样,那很可能是内存泄漏捣的鬼。
还有啊,程序运行的时间越长,可用内存就越少,这也是个很明显的信号。
就好比一个水桶有个小漏洞,水一直流出去,桶里的水就越来越少啦。
1.2 查看资源占用情况我们可以查看系统的资源监视器之类的工具。
看看内存的使用量是不是一直往上涨,就像气球不断被吹气一样。
如果内存使用量只增不减,那内存泄漏的可能性就很大了。
二、定位内存泄漏的源头2.1 代码审查这时候就得卷起袖子好好审查代码啦。
看看有没有一些地方在不断地创建对象,但是却没有及时释放。
比如说,有些新手写代码,就像一个马虎的厨师做菜,只知道往锅里加料,却忘记把用过的锅刷干净。
像在循环里不断创建新的对象,却没有在合适的地方销毁,这就是典型的内存泄漏隐患。
2.2 借助工具检测有不少好用的工具能帮我们大忙呢。
像Valgrind这个工具就像是一个侦探,能够嗅出内存泄漏的蛛丝马迹。
它可以详细地告诉我们是哪段代码在搞鬼,就像给我们指出小偷藏在哪里一样。
还有一些编程语言自带的内存分析工具,也非常实用。
2.3 分析内存分配模式我们要仔细分析内存是怎么分配的。
如果发现有一些内存块被分配后,很长时间都没有被再次使用,就像被遗忘在角落里的宝藏一样,那这里就很可能存在内存泄漏。
而且如果大量的小内存块不断被分配,却没有被回收,这也可能是内存泄漏的一种表现形式。
三、解决内存泄漏问题3.1 修复代码逻辑一旦确定了内存泄漏的源头,就要赶紧修复代码逻辑。
如果是对象没有及时释放,那就得在合适的地方加上释放的代码。
这就好比收拾房间,用过的东西要放回原位或者扔掉,不能让它们一直在房间里占地方。
3.2 进行测试验证修复完代码可不能就这么算了,还得进行测试验证。
要确保内存泄漏的问题真的被解决了。
可以长时间运行程序,看看内存使用情况是不是稳定了。
valgrind内存泄漏分析概述valgrind 是 Linux 业界主流且⾮常强⼤的内存泄漏检查⼯具。
在其官⽹介绍中,内存检查(memcheck)只是其其中⼀个功能。
由于只⽤过其内存泄漏的检查,就不拓展分享 valgrind 其他功能了。
valgrind 这个⼯具不能⽤于调试正在运⾏的程序,因为待分析的程序必须在它特定的环境中运⾏,它才能分析内存。
内存泄漏分类valgrind 将内存泄漏分为 4 类。
明确泄漏(definitely lost):内存还没释放,但已经没有指针指向内存,内存已经不可访问间接泄漏(indirectly lost):泄漏的内存指针保存在明确泄漏的内存中,随着明确泄漏的内存不可访问,导致间接泄漏的内存也不可访问可能泄漏(possibly lost):指针并不指向内存头地址,⽽是指向内存内部的位置仍可访达(still reachable):指针⼀直存在且指向内存头部,直⾄程序退出时内存还没释放。
明确泄漏官⽅⽤户⼿册描述如下:This means that no pointer to the block can be found. The block is classified as "lost",because the programmer could not possibly have freed it at program exit, since no pointer to it exists.This is likely a symptom of having lost the pointer at some earlier point in theprogram. Such cases should be fixed by the programmer.其实简单来说,就是内存没释放,但已经没有任何指针指向这⽚内存,内存地址已经丢失。
定义⽐较好理解,就不举例了。
几个内存泄漏的例子✧new和delete要成对使用✧new和delete要匹配经常看到一些C++方面的书籍中这样提及到内存泄漏问题,这样的说法的意思是比较明白,但对于初学C++程序员还是很难掌握,所以下面举几个反面的例子,希望对大家有帮助。
例一:错误处理流程中的return导致的内存泄漏bool MyFun(){CMyObject* pObj = NULL;pObj = new CMyObject();…if (…)return false;…if(…)return false;…if (pObj != NULL)delete pObj;return true;}注意:红色字体部分的return之前没有释放pObj,导致内存泄漏。
例二:exception改变了程序的正常流程,导致内存泄漏情况1:HRESULT MyFun(){HRESULT hr = S_OK;try{CMyObject* pObj = NULL;pObj = new CMyObject();…if (…){hr = E_FAIL;throw hr;}…if(…){hr = E_FAIL;throw hr;}…if (pObj != NULL)delete pObj;}catch (HRESULT& eHr){}return hr;}情况2:void OtherFun() // 可能是自己写的其他函数;// 也可能是其他人写的函数;// 也可能是系统的API;{…if(…)throw exception;…}bool MyFun(){CMyObject* pObj = NULL;pObj = new CMyObject();…OtherFun();…if (pObj != NULL)delete pObj;return true;}注意:上面的两种情况中的throw行为将导致程序的正常流程,一旦有throw的动作发生,pObj对象将不会被正确释放(delete)。
例三:忘记释放系统API创建的资源,导致内存泄露bool CMyClass::MyFun(){HANDLE hHandle = CreateEvent(NULL,FALSE,TRUE,NULL);…if (…)return false;…return true;}注意:系统API CreateEvent创建的HANDLE对象,需要自己释放,否则会导致内存泄漏。
还有其他一些系统的API也是如此,如:CreateFile、CreateFileMapping 等等,所以我们在使用不熟悉的系统API时,一定要仔细阅读MSDN。
例四:PostMessage可能导致的内存泄漏// 自定义的消息结构体typedef struct tagMSG{int i;float f;}MSG;// 发送消息的函数void MyFun(){MSG* pMsg = new MSG;…PostMessage(m_hWnd, WM_MYEVENT, (WPARAM)pMsg, 0);}// 接收消息的函数afx_msg void OnMessage(WPARAM wParam, LPARAM lParam){MSG* pMsg = (MSG*)wParam;m_i = pMsg->i;m_f = pMsg->f;}注意:OnMessage函数中忘记释放pMsg,导致内存泄漏。
例五:函数返回new的对象而导致的内存泄漏char* MyFun(){char* p = new char[10];…return p;}注意:调用MyFun程序的人可能不会注意到MyFun函数内部new出的对象,往往会忽略对象p的释放。
例六:不好的类结构也会导致内存泄漏// MyClass.h文件class MyClass{public:MyClass();virtual ~MyClass();BOOL Init();BOOL UnInit();private:char* m_pStr;}// MyClass.cpp文件MyClass::MyClass(): m_pStr(NULL){}MyClass::~MyClass(){}BOOL MyClass::Init(){m_pStr = new char[100];…if (…){delete m_pStr;m_pStr = NULL;return FALSE;}return TRUE;}BOOL MyClass::UnInit(){if (m_pStr != NULL){delete m_pStr;m_pStr = NULL;}return TRUE;}注意:这个类在Init()函数中new出类资源,需要调用相应的UnInit()函数来释放资源,但有些时候这个类需要给其他人使用,别人在使用时可能会忘记调用UnInit()函数来释放资源,这样就造成了内存泄漏,为了防止这个问题发生,最好在MyClass::~MyClass()函数中也进行资源释放。
如下写法:MyClass::~MyClass(){UnInit();}例七:容易忽视的深层次结构对象的内存泄漏typedef struct MyStruct{int i;BSTR bstr;}void MyFun(OLECHAR * sz){MyStruct* pStru = new MyStruct;…pStru->bstr = SysAllocString(sz);…delete pStru;}注意:pStru是个深层次结构的对象,在这个对象内部还有指针数据类型bstr,在释放pStr时,如果忘记了释放其内部的指针对象bstr,也会导致内存泄漏。
当然这个例子比较简单,在我们实际编程时,深层次结构的对象要比这个复杂的多,所以处理这些对象时需要格外小心。
例八:虚基类的析构函数可能导致的问题///////////////////////////// Base.hclass Base{public:Base();~Base();virtual void TestFun();}// Base.cppBase::Base(){}Base::~Base(){if (m_pStr != NULL){delete m_pStr;}}void Base::TestFun(){}////////////////////////////// // MyClass.hclass MyClass : public Base {public:MyClass();~MyClass();virtual void TestFun(); protected:char* m_pStr;}// MyClass.cppMyClass::MyClass(): m_pStr(NULL){}MyClass::~MyClass(){if (m_pStr != NULL)delete m_pStr;}void MyClass::TestFun(){m_pStr = new char[100];}/////////////////////////////// Test.cppint _tmain(int argc, _TCHAR* argv[]){Base* pClass = new CMyClass();pClass->TestFun();delete pClass;return 0;}注意:由于Base类的析构函数不是virtual类型,在_tmain程序的用法中,将会导致CMyClass的析构不会被调用,引起内存泄漏。
总结:1、C++编程中,内存泄漏是个比较烦人的问题,创建(new)对象的“入口”一般只有一处,但需要释放(delete)对象的“出口”却很多,而且由于创建(new)出来的对象的生存周期不固定,所以要完全避免内存泄漏是比较困难,需要我们在编程时格外小心;2、养成良好的代码书写习惯,尽量在编写代码时解决内存泄漏问题;3、一旦程序中发生内存泄漏时,可以使用一些内存泄漏检查工具(如:Bound Check等)来查找,这些工具可以帮助我们找出部分的内存泄漏的代码;4、有些内存泄漏的问题还需要我们手工查找,检查代码的逻辑也是查找内存泄漏的一种有效手段。