动态内存管理总结(malloc、calloc、realloc的区别),以及内存泄漏常见问题

来源:互联网 发布:经济大数据分析书籍 编辑:程序博客网 时间:2024/04/30 04:57

一、C语言中 malloc、calloc、realloc的区别

(1)C语言跟内存分配方式

<1>从静态存储区域分配

       内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量、static变量。

<2>在栈上创建

       在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内

存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

<3>从堆上分配,亦称动态内存分配.

       程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存.动态内

存的生存期由用户决定,使用非常灵活,但问题也最多。

(2)C语言跟内存申请相关的函数主要有 alloca、calloc、malloc、free、realloc等

    <1>alloca是向栈申请内存,因此无需释放。

    <2>malloc分配的内存是位于堆中的,并且没有初始化内存的内容,因此基本上malloc之后,调用函数memset来初始

化这部分的内存空间。

    <3>calloc则将初始化这部分的内存,设置为0。

    <4>realloc则对malloc申请的内存进行大小的调整。

    <5>申请的内存最终需要通过函数free来释放。

    当程序运行过程中malloc了,但是没有free的话,会造成内存泄漏.一部分的内存没有被使用,但是由于没有free,

因此系统认为这部分内存还在使用,造成不断的向系统申请内存,使得系统可用内存不断减少。但是内存泄漏仅仅指

程序在运行时,程序退出时,OS将回收所有的资源。因此,适当的重起一下程序,有时候还是有点作用。

【attention】

    三个函数的申明分别是:

void *malloc( size_t size );
void *calloc( size_t num, size_t size );

void *realloc( void *memblock, size_t size );

    都在stdlib.h函数库内,它们的返回值都是请求系统分配的地址,如果请求失败就返回NULL。

    (1)函数malloc()

        在内存的动态存储区中分配一块长度为size字节的连续区域,参数size为需要内存空间的长度,返回该区域的首

地址。

    (2)函数calloc()

        与malloc相似,参数sizet为申请地址的单位元素长度,num为元素个数,即在内存中申请num*size字节大小的

连续地址空间。

    (3)函数realloc()

        给一个已经分配了地址的指针重新分配空间,参数memblock为原有的空间地址,size是重新申请的地址长度。

    区别:

    (1)函数malloc不能初始化所分配的内存空间,而函数calloc能。如果由malloc()函数分配的内存空间原来没有被使

用过,则其中的每一位可能都是0;反之, 如果这部分内存曾经被分配过,则其中可能遗留有各种各样的数据.也就是

说,使用malloc()函数的程序开始时(内存空间还没有被重新分配)能正常进行,但经过一段时间(内存空间还已经被重

新分配)可能会出现问题。

    (2)函数calloc() 会将所分配的内存空间中的每一位都初始化为零,也就是说,如果你是为字符类型或整数类型的元

素分配内存,那么这些元素将保证会被初始化为0;如果你是为指针类型的元素分配内存,那么这些元素通常会被初

始化为空指针;如果你为实型数据分配内存,则这些元素会被初始化为浮点型的零。

    (3)函数malloc向系统申请分配指定size个字节的内存空间。返回类型是 void*类型.,oid*表示未确定类型的指

针,C、C++规定,void* 类型可以强制转换为任何其它类型的指针。

    (4)realloc可以对给定的指针所指的空间进行扩大或者缩小,无论是扩张或是缩小,原有内存的中内容将保持不

变。当然,对于缩小,则被缩小的那一部分的内容会丢失。realloc并不保证调整后的内存空间和原来的内存空间保持

同一内存地址。相反,realloc返回的指针很可能指向一个新的地址。

    (5)realloc是从堆上分配内存的.当扩大一块内存空间时,realloc()试图直接从堆上现存的数据后面的那些字节中获

得附加的字节,如果能够满足,自然天下太平;如果数据后面的字节不够,问题就出来了,那么就使用堆上第一个有

足够大小的自由块,现存的数据然后就被拷贝至新的位置,而老块则放回到堆上。这句话传递的一个重要的信息就是

数据可能被移动。


二、内存泄漏

1、内存泄漏简介及后果

wikipedia中这样定义内存泄漏:在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的

内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段

内存之前就失去了对该段内存的控制,从而造成了内存的浪费。

最难捉摸也最难检测到的错误之一是内存泄漏,即未能正确释放以前分配的内存的 bug。 只发生一次的小的内

存泄漏可能不会被注意,但泄漏大量内存的程序或泄漏日益增多的程序可能会表现出各种征兆:从性能不良(并且逐

渐降低)到内存完全用尽。 更糟的是,泄漏的程序可能会用掉太多内存,以致另一个程序失败,而使用户无从查找问

题的真正根源。 此外,即使无害的内存泄漏也可能是其他问题的征兆。

内存泄漏会因为减少可用内存的数量从而降低计算机的性能。最终,在最糟糕的情况下,过多的可用内存被分配

掉导致全部或部分设备停止正常工作,或者应用程序崩溃。内存泄漏可能不严重,甚至能够被常规的手段检测出来。

在现代操作系统中,一个应用程序使用的常规内存在程序终止时被释放。这表示一个短暂运行的应用程序中的内存泄

漏不会导致严重后果。

在以下情況,内存泄漏导致较严重的后果:


  • 程序运行后置之不理,并且随着时间的流失消耗越来越多的内存(比如服务器上的后台任务,尤其是嵌入式系统中的后台任务,这些任务可能被运行后很多年内都置之不理);

  • 新的内存被频繁地分配,比如当显示电脑游戏或动画视频画面时;

  • 程序能够请求未被释放的内存(比如共享内存),甚至是在程序终止的时候;

  • 泄漏在操作系统内部发生;

  • 泄漏在系统关键驱动中发生;

  • 内存非常有限,比如在嵌入式系统或便携设备中;

  • 当运行于一个终止时内存并不自动释放的操作系统(比如AmigaOS)之上,而且一旦丢失只能通过重启来恢复。

内存泄漏的原因

      调用了malloc/new等内存申请的操作,但缺少了对应的free/delete,总之就是,malloc/new比free/delete的数量多。我们在编程时需要注意这点,保证每个malloc都有对应的free,每个new都有对应的deleted!!!平时要养成这样一个好的习惯。

避免内存泄漏可以总结为以下几点:

1)程序员要养成良好习惯,保证malloc/new和free/delete匹配;

2)检测内存泄漏的关键原理就是,检查malloc/new和free/delete是否匹配,一些工具也就是这个原理。要做到这

点,就是利用宏或者钩子,在用户程序与运行库之间加了一层,用于记录内存分配情况。

内存泄漏的检测方法:可以使用相应的软件测试工具对软件进行检测。

1) ccmalloc-Linux和Solaris下对C和C++程序的简单的使用内存泄漏和malloc调试库。

2) Dmalloc-Debug Malloc Library。

3) Electric Fence-Linux分发版中由Bruce Perens编写的malloc()调试库。

4)  Leaky-Linux下检测内存泄漏的程序。5)   LeakTracer-Linux、Solaris和HP-UX下跟踪和分析C++程序中的内存泄漏。6)   MEMWATCH-由Johan Lindh编写,是一个开放源代码C语言内存错误检测工具,主要是通过gcc的precessor
来进行。
7)   Valgrind-Debugging and profiling Linux programs, aiming at programs written in C and C++.
8)   KCachegrind-A visualization tool for the profiling data generated by Cachegrind and Calltree.9)   Leak Monitor-一个Firefox扩展,能找出跟Firefox相关的泄漏类型。
10) IE Leak Detector (Drip/IE Sieve)-Drip和IE Sieve leak detectors帮助网页开发员提升动态网页性能通过报告
可避免的因为IE局限的内存泄漏。11)Windows Leaks Detector-探测任何Win32应用程序中的任何资源泄漏(内存,句柄等),基于Win API调用钩
子。12)SAP Memory Analyzer-是一款开源的JAVA内存分析软件,可用于辅助查找JAVA程序的内存泄漏,能容易找
到大块内存并验证谁在一直占用它,它是基于Eclipse RCP(Rich Client Platform),可以下载RCP的独立版本或者
Eclipse的插件。13)DTrace-即动态跟踪Dynamic Tracing,是一款开源软件,能在Unix类似平台运行,用户能够动态检测操作系
统内核和用户进程,以更精确地掌握系统的资源使用状况,提高系统性能,减少支持成本,并进行有效的调节。
14)IBM Rational PurifyPlus-帮助开发人员查明C/C++、托管.NET、Java和VB6代码中的性能和可靠性错误。
PurifyPlus 将内存错误和泄漏检测、应用程序性能描述、代码覆盖分析等功能组合在一个单一、完整的工具包中。15)Parasoft Insure++-针对C/C++应用的运行时错误自动检测工具,它能够自动监测C/C++程序,发现其中存在
着的内存破坏、内存泄漏、指针错误和I/O等错误。并通过使用一系列独特的技术(SCI技术和变异测试等),彻底的
检查和测试我们的代码,精确定位错误的准确位置并给出详细的诊断信息。能作为Microsoft Visual C++的一个插件
运行。16)Compuware DevPartner for Visual C++ BoundsChecker Suite-为C++开发者设计的运行错误检测和调试
工具软件。作为Microsoft Visual Studio和C++ 6.0的一个插件运行。17)Electric Software GlowCode-包括内存泄漏检查,code profiler,函数调用跟踪等功能。给C++和.Net开发
者提供完整的错误诊断,和运行时性能分析工具包。18)Compuware DevPartner Java Edition-包含Java内存检测,代码覆盖率测试,代码性能测试,线程死锁,分布式应
用等几大功能模块。19)Quest JProbe-分析Java的内存泄漏。20)ej-technologies JProfiler-一个全功能的Java剖析工具,专用于分析J2SE和J2EE应用程序。它把CPU、执行绪
和内存的剖析组合在一个强大的应用中。JProfiler可提供许多IDE整合和应用服务器整合用途。JProfiler直觉式的GUI
让你可以找到效能瓶颈、抓出内存泄漏、并解决执行绪的问题。21)BEA JRockit-用来诊断Java内存泄漏并指出根本原因,专门针对Intel平台并得到优化,能在Intel硬件上获得最
高的性能。22)SciTech Software AB .NET Memory Profiler-找到内存泄漏并优化内存使用针对C#,VB.Net,或其它.Net程
序。23) YourKit .NET & Java Profiler-业界领先的Java和.NET程序性能分析工具。24) AutomatedQA AQTime-AutomatedQA的获奖产品performance profiling和memory debugging工具集的
下一代替换产品,支持Microsoft, Borland, Intel, Compaq 和 GNU编译器。可以为.NET和Windows程序生成全面
细致的报告,从而帮助您轻松隔离并排除代码中含有的性能问题和内存/资源泄露问题。支持.Net 1.0,1.1,2.0,3.0和
Windows 32/64位应用程序。25) JavaScript Memory Leak Detector-微软全球产品开发欧洲团队(Global Product Development- Europe 
team, GPDE) 发布的一款调试工具,用来探测JavaScript代码中的内存泄漏,运行为IE系列的一个插件。

1. 只能在堆(heap)上创建对象/禁止产生栈(stack)对象   

    小的空间, 再在这个空间上创建栈对象时会移动栈顶指针以“挪出”适当大直接调用对应的构造函数以形成一个栈

对象, 而当函数返回时会调用其析构函数释放这个对象, 再调整栈顶指针收回那块栈内存, 在这个过程中是不需要

operator new/delete操作的, 所以将operator new/delete设置为private不能达到禁止产生栈(stack)对象的目

的。

    把析构函数定义为private访问权限, 就可以保证只能在堆(heap)上创建(new)一个新的类对象.析构函数私有化的类

的设计可以保证只能用new命令在堆(heap)中创建对象, 只能动态的去创建对象, 这样可以自由的控制对象的生命周

期, 但这样的类需要提供创建和撤销的公共接口。

2. 只能在栈(stack)上生成对象

   为禁止产生某种类型的堆对象, 可以自己创建一个资源封装类,该类对象只能在栈中产生, 这样就能在异常的情况

下自动释放封装的资源。

    产生堆对象的唯一方法是使用new, 禁止使用new就可禁止产生堆对象. 由于new执行时会调用operator new, 而

operator new是可重载的, 所以将operator new和operator delete重载为private即可创建栈对象不需要调用

new, 因为创建栈对象不需要搜索内存, 而是直接调整堆栈指针将对象压栈, 而operator new的主要任务是搜索合适的

堆内存, 为堆对象分配空间

阅读全文
0 0
原创粉丝点击