`
qiaoxuan9206
  • 浏览: 69971 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

内存泄露,内存溢出和死锁

阅读更多

 

一内存泄露

   内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。内存泄漏与许多其他问题有着相似的症状,并且通常情况下只能由那些可以获得程序源代码的程序员才可以分析出来。然而,有不少人习惯于把任何不需要的内存使用的增加描述为内存泄漏,即使严格意义上来说这是不准确的。
  一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显示释放的内存。应用程序一般使用malloc,realloc,new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块,否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。
  内存泄漏可以分为4类:
  1. 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
  2. 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
  3. 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在一个Singleton类的构造函数中分配内存,在析构函数中却没有释放该内存。而Singleton类只存在一个实例,所以内存泄漏只会发生一次。
  4. 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。                       

   内存泄漏会因为减少可用内存的数量从而降低计算机的性能。最终,在最糟糕的情况下,过多的可用内存被分配掉导致全部或部分设备停止正常工作,或者应用程序崩溃。
  内存泄漏可能不严重,甚至能够被常规的手段检测出来。在现代操作系统中,一个应用程序使用的常规内存在程序终止时被释放。这表示一个短暂运行的应用程序中的内存泄漏不会导致严重后果。
  在以下情况,内存泄漏导致较严重的后果:
  * 程序运行后置之不理,并且随着时间的流失消耗越来越多的内存(比如服务器上的后台任务,尤其是嵌入式系统中的后台任务,这些任务可能被运行后很多年内都置之不理);
  * 新的内存被频繁地分配,比如当显示电脑游戏或动画视频画面时;
  * 程序能够请求未被释放的内存(比如共享内存),甚至是在程序终止的时候;
  * 泄漏在操作系统内部发生;
  * 泄漏在系统关键驱动中发生;
  * 内存非常有限,比如在嵌入式系统或便携设备中;
  * 当运行于一个终止时内存并不自动释放的操作系统(比如AmigaOS)之上,而且一旦丢失只能通过重启来恢复。

2内存溢出

   内存溢出已经是软件开发历史上存在了近40年的“老大难”问题,象在“红色代码”病毒事件中表现的那样,它已经成为黑客攻击企业网络的“罪魁祸首”。 如在一个域中输入的数据超过了它的要求就会引发数据溢出问题,多余的数据就可以作为指令在计算机上运行。据有关安全小组称,操作系统中超过50%的安全漏洞都是由内存溢出引起的,其中大多数与微软的技术有关。
  为了便于理解,我们不妨打个比方。缓冲区溢出好比是将十磅的糖放进一个只能装五磅的容器里。一旦该容器放满了,余下的部分就溢出在柜台和地板上,弄得一团糟。由于计算机程序的编写者写了一些编码,但是这些编码没有对目的区域或缓冲区——五磅的容器——做适当的检查,看它们是否够大,能否完全装入新的内容——十磅的糖,结果可能造成缓冲区溢出的产生。如果打算被放进新地方的数据不适合,溢得到处都是,该数据也会制造很多麻烦。但是,如果缓冲区仅仅溢出,这只是一个问题。到此时为止,它还没有破坏性。当糖溢出时,柜台被盖住。可以把糖擦掉或用吸尘器吸走,还柜台本来面貌。与之相对的是,当缓冲区溢出时,过剩的信息覆盖的是计算机内存中以前的内容。除非这些被覆盖的内容被保存或能够恢复,否则就会永远丢失。
  在丢失的信息里有能够被程序调用的子程序的列表信息,直到缓冲区溢出发生。另外,给那些子程序的信息——参数——也丢失了。这意味着程序不能得到足够的信息从子程序返回,以完成它的任务。就像一个人步行穿过沙漠。如果他依赖于他的足迹走回头路,当沙暴来袭抹去了这些痕迹时,他将迷失在沙漠中。这个问题比程序仅仅迷失方向严重多了。入侵者用精心编写的入侵代码(一种恶意程序)使缓冲区溢出,然后告诉程序依据预设的方法处理缓冲区,并且执行。此时的程序已经完全被入侵者操纵了。
  入侵者经常改编现有的应用程序运行不同的程序。例如,一个入侵者能启动一个新的程序,发送秘密文件(支票本记录,口令文件,或财产清单)给入侵者的电子邮件。这就好像不仅仅是沙暴吹了脚印,而且后来者也会踩出新的脚印,将我们的迷路者领向不同的地方,他自己一无所知的地方。
  缓冲区溢出的处理
  你屋子里的门和窗户越少,入侵者进入的方式就越少……
  由于缓冲区溢出是一个编程问题,所以只能通过修复被破坏的程序的代码而解决问题。如果你没有源代码,从上面“堆栈溢出攻击”的原理可以看出,要防止此类攻击,我们可以:
  1、开放程序时仔细检查溢出情况,不允许数据溢出缓冲区。由于编程和编程语言的原因,这非常困难,而且不适合大量已经在使用的程序;
  2、使用检查堆栈溢出的编译器或者在程序中加入某些记号,以便程序运行时确认禁止黑客有意造成的溢出。问题是无法针对已有程序,对新程序来讲,需要修改编译器;
  3、经常检查你的操作系统和应用程序提供商的站点,一旦发现他们提供的补丁程序,就马上下载并且应用在系统上,这是最好的方法。但是系统管理员总要比攻击者慢一步,如果这个有问题的软件是可选的,甚至是临时的,把它从你的系统中删除。举另外一个例子,你屋子里的门和窗户越少,入侵者进入的方式就越少。

3死锁

因为系统资源不足。
进程运行推进的顺序不合适。
资源分配不当等。
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。

产生死锁的四个必要条件
互斥条件:一个资源每次只能被一个进程使用。
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

对于线程同步问题,有了进一步的理解:详见我写的关于多线程同步的文章(已作修改):http://blog.csdn.net/yjgx007/archive/2004/09/04/94559.aspx,主线程A等待另一个线程B的完成才能继续,在线程B中又要更新主线程A的界面,这里涉及了同步问题以及由此可能产生的死锁问题,同步问题在修改后的文章中讲得比较清楚了,对于线程之间可能产生死锁的浅析如下:

在等待线程B中更新主线程A的界面,如果未能正确处理A,B两线程同步的问题,极有可能导致两线程间的死锁,看下面代码:

 UINT CMsiTestDlg::UpdateDeviceContent(LPVOID pParam)
{
 CMsiTestDlg* pDlg = (CMsiTestDlg*)pParam;
 int i = 0;
 do {
  pDlg->m_progress.SetPos(i); // 更新线程A中的进度条
  Sleep(500);
 } while(i++<10);

 return 0;
}

void CMsiTestDlg::OnButton1()
{
 MSG msg;
 CWinThread* m_pUpdateThread = AfxBeginThread(UpdateDeviceContent, (LPVOID)this/*, THREAD_PRIORITY_BELOW_NORMAL*/);
 if (m_pUpdateThread)
 {
while (::WaitForSingleObject(m_pUpdateThread->m_hThread, INFINITE) != WAIT_OBJECT_0) //开始等待线程B至结束(线程结束时将返回WAIT_OBJECT_0)
   PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE); //获取当前线程消息(可能是A线程也可能是B线程)并将消息从消息队列中移除
   DispatchMessage(&msg); // 重新分发消息
...
 }

 MessageBox("Thread is end!");
}

m_pUpdateThread->m_hThread是等待线程B,这样调用似乎没有什么问题,在VC++中跟踪到线程B的线程函数UpdateDeviceContent中的pDlg->m_progress.SetPos(i); 时,发现程序不能继续执行,表现为在线程B中对主线程A的界面更新发生了阻塞(这里暂不考虑界面线程,权当由主线程处理),为何?原因就在于WaitForSingleObject(m_pUpdateThread->m_hThread, INFINITE)最后一个参数INFINITE - 无限期等待线程B的结束返回,从而产生了不幸:

线程A对线程B无限期等待造成未能重新分发消息(包括界面重绘WM_PAINT, 定时WM_TIMER以及硬件输入和系统消息,就里特指WM_PAINT消息),造成线程B的阻塞,线程B的阻塞又造成线程A的进一步等待造成线程A的阻塞,这就导致了死锁。

解决方法是:设置WaitForSingleObject的等待时间为一定值,如500毫秒,这样,线程A如若等不到线程B的结束,也会返回,并分发消息,使得线程B的执行得以正常继续,从而也就保证了线程A和线程B之间的正常同步!

 

分享到:
评论

相关推荐

    JAVA内存泄漏分析工具

    "JAVA内存泄漏分析工具"正是一款用于解决此类问题的专业工具,它能帮助开发者定位并修复内存相关的问题,如内存泄漏和内存溢出。 内存泄漏是程序在申请内存后,无法释放已申请的内存空间,一次小的内存泄漏可能看似...

    jprofiler的使用及联调内存溢出解决方案交流

    jprofiler 可以帮助开发者快速定位内存溢出的源头,找到程序中的内存泄露点,从而解决内存溢出问题。 在联调内存溢出解决方案中,jprofiler 可以与其他工具结合使用,例如任务管理器等。开发者可以使用 jprofiler ...

    cpu利用率过高,内存溢出分析

    诊断内存溢出通常需要利用内存分析工具,如Valgrind(C/C++)、JProfiler(Java)、VisualVM(Java)等,这些工具可以帮助开发者定位内存泄漏的位置,查看对象生命周期和内存分配情况。 ...

    was内存溢出 javacore分析工具jca 456

    通过分析Javacore,我们可以定位内存溢出的原因,例如是否有无用对象未能正确垃圾回收,是否存在内存泄漏的类或者静态变量等。 JCA(Java Core Analysis)456是一款专门用于解析和分析Javacore文件的工具,它能帮助...

    介绍堆栈,线程 如何解决内存溢出

    本文将深入探讨这两个主题以及如何解决内存溢出问题,以帮助你在笔试和面试中表现出色。 首先,我们来理解堆栈。堆栈是一种特殊的内存区域,遵循“后进先出”(LIFO)原则,主要用于存储程序运行过程中的函数调用...

    java内存溢出原因

    在排查内存溢出问题时,应结合日志分析、业务逻辑审查、代码审查和性能监控工具,以确定内存泄漏的源头,并针对性地优化代码和调整内存配置。对于复杂的情况,可能需要通过模拟复现问题场景,采用排除法逐步缩小问题...

    VC6.0辅助插件-检查内存泄露的工具

    1. **内存泄漏检测**:BoundsChecker能够跟踪内存分配和释放,当程序运行结束时,它会报告那些未被释放的内存块,帮助开发者定位内存泄露的位置。 2. **缓冲区溢出检测**:通过监控数组边界,BoundsChecker可以防止...

    进程的内存监视.rar

    9. **并发与内存**:在多线程环境下,理解内存同步和可见性问题至关重要,避免竞态条件和死锁,确保数据的一致性。 10. **内存安全**:防止缓冲区溢出、指针错误等内存安全问题,是保证程序稳定性和安全性的重要一...

    WINDOWS程序员使用指南(一)----DLL和内存管理

    8. **内存泄漏检测**:在开发过程中,应使用工具如Visual Studio的诊断工具或第三方库(如LeakSanitizer)来检测和修复内存泄漏问题。 9. **内存碎片管理**:长期运行的程序可能面临内存碎片问题,合理规划内存分配...

    sjava面试死锁

    在Java面试中,内存管理和并发编程是两个重要的领域,涉及到的问题包括内存泄露、内存溢出以及线程池的使用。这里我们将深入探讨这些概念及其实际应用。 1. **内存泄露与内存溢出的区别** - **内存泄露**:内存...

    accutrak:可配置的内存调试工具。 它检测一般的内存错误,例如溢出,欠载,重复删除,对已释放内存的无效访问以及内存泄漏

    1. **内存溢出**:当程序分配的内存区域超过其预定边界时,就会发生内存溢出。AccuTrak能够追踪内存分配,检测出这种越界写入,防止对相邻内存区域的意外修改。 2. **内存欠载**:与溢出相反,内存欠载发生在程序...

    jvm分析工具JProfiler,java应用内存溢出堆栈快照分析工具

    当Java应用出现内存溢出问题时,JProfiler可以生成堆栈快照,这有助于分析导致内存溢出的具体原因。通过堆栈快照,开发者可以看到对象的引用链,找出持有大对象的根源,以便进行针对性的优化。 **5. 线程监控** ...

    IBM内存分析工具javaAnalyzer.rar

    在Java应用运行过程中,如果遇到内存溢出或系统异常,操作系统可能会生成一个dump文件,这是一份详细的内存快照,包含了所有线程的状态、对象分配以及堆内存的详细信息。javacore文件则记录了JVM(Java虚拟机)在...

    【JAVA WEB实用技巧与优化方案】如何排查JVM线程和内存相关问题

    在Java应用程序运行过程中,如果遇到内存溢出或性能问题,可以通过触发JVM生成堆内存快照,即内存dump文件,来记录当前所有对象及其占用内存的情况。这个文件包含了JVM内存的所有详细信息,是分析内存问题的重要依据...

    易语。奇迹扩展虚拟内存保护模块。

    - 利用提供的调试工具进行内存泄漏检查和性能分析。 - 在多线程环境中,确保线程安全,避免竞态条件和死锁。 虚拟内存保护是软件开发中不可或缺的一环,它确保了系统的稳定性和安全性。易语的这个扩展模块可能是...

    jvm问题排查

    - 使用MAT(Memory Analyzer Tool)等工具分析heap dump文件,找出内存泄漏的对象。 - 定位代码中的问题并修复。 ##### 2. **频繁GC** - **表现形式**:应用程序频繁发生GC,导致性能下降。 - **解决方案**: -...

    java dump分析工具ha456

    Java Dump分析是Java开发者在遇到性能问题,尤其是内存溢出问题时的重要手段。"ha456"似乎是一个专门用于Java Dump分析的工具,能够帮助我们深入理解内存状况,定位问题源头。本文将详细探讨Java内存分析、dump分析...

    vmmap 观察jvm内存 监控jvm jvm线程

    - 分析线程堆栈,减少不必要的线程创建,优化同步策略,避免死锁和资源争抢。 5. **其他监控工具:** - `jconsole`和`VisualVM`提供图形化的JVM监控,包括内存、线程、类加载等信息。 - `jcmd`是JDK自带的命令行...

Global site tag (gtag.js) - Google Analytics