代码之家  ›  专栏  ›  技术社区  ›  Wayne Werner

什么是内存泄漏?

  •  22
  • Wayne Werner  · 技术社区  · 14 年前

    明显地 Wikipedia 关于这个话题有相当多的信息,但我想确保我理解。从我所知道的来看,了解堆栈/堆关系对于真正了解内存泄漏是很重要的?

    这就是我所理解的。欢迎更正!

    当您第一次启动程序时,会分配一个内存块,例如0x000到0xfff。第一部分(例如0x000到0x011)是加载程序代码的代码/文本段。

    +--------------+ 0x011
    | Program Code |
    +--------------+ 0x000
    

    然后有一个栈(比如0x012到0x7FF)来保存局部变量,它们被存储/检索为FIFO。所以如果你有

    char middleLetter(string word){
         int len = word.length();
         return word[len/2];
    }
    
    int main(){
       int cool_number;
       char letter;
       letter = middleLetter("Words");
       ...
    

    然后在堆栈上分配变量,如下所示:

    +-------------+ 0x7ff
    |             |
    |             |
    |             |
    | ...         |
    | len         |
    | letter      |
    | cool_number |
    +-------------+ 0x012
    

    当然,如果您在某处分配内存(使用 malloc new ,但永远不会释放它,那么您的堆可能看起来像这样,现在您有了内存泄漏:

    +-------------+ 0xfff
    |             |
    | malloc(20)  | 0xf64
    | malloc(50)  | 0xf32
    | malloc(50)  | 0xf00
    | ...         |
    |             |
    +-------------+ 0x800
    

    这意味着,虽然您可以通过指针算术直接访问0xf32,但OS/您的程序认为内存位置0xf00-0xf46已被占用,在关闭程序并释放内存之前,不会再次使用这些位置进行存储。但是共享内存呢?维基百科说它永远不会发布(直到你的电脑重新启动?)你怎么知道它是不是共享内存?

    这是一个很好的基本理解吗?我有什么不对劲的地方吗?谢谢你的关注!

    7 回复  |  直到 9 年前
        1
  •  7
  •   user395760    14 年前

    似乎您确实理解它——有一个例外:在您的示例中,len和其他所有变量一样是一个堆栈变量。 new malloc 在堆上创建,其他所有内容(局部变量等)都在堆栈上。主函数的局部变量与其他函数的变量没有区别。

    共享内存是一种非常罕见的情况,您通常不需要它,因此除非您明确要求它,否则您不会拥有它(否则,某些随机的其他进程可能使用您进程使用的非常相同的内存-显然,这会严重破坏事情)。

        2
  •  5
  •   µBio    14 年前

    函数变量通常也在堆栈上,而不是堆上。在大多数系统中,堆用于动态分配。通常的内存泄漏情况是

    1. 调用某个函数f
    2. f分配(new或malloc)一些内存
    3. F返回到调用方(无删除/可用)
    4. 指向动态分配内存的指针超出范围
      • 内存仍在分配中。
      • 您不能再删除/释放它
        3
  •  4
  •   Dutchie432    14 年前

    这样想。当用一种需要编码人员管理内存的语言进行开发时,您需要为程序将要使用的每个对象显式地分配和销毁内存。很容易知道什么时候你没有正确地创建一些东西,因为你的程序将不工作。如果没有正确地销毁对象(这称为内存泄漏),则很难找到并调试这些情况。

    让我们来看一个典型的应用程序,比如说RSS新闻阅读器。在这样的应用程序中,通常有许多循环(循环通过不同的RSS提要、不同的RSS项目、RSS标记等)。如果您有一个创建的对象没有被正确地销毁(或释放)的实例,那么每次运行“泄漏”代码时,您将在内存中得到另一个被放弃的对象。如果循环运行1000次,则会有1000个废弃对象占用空间。您可以看到,这样做如何快速累积,从而消耗宝贵的资源。

        4
  •  4
  •   karlphillip    14 年前

    内存泄漏变得简单 :每当您使用malloc/new分配内存,并且在使用完内存后不使用free/delete释放内存时…会导致内存泄漏!分配的内存将保留在那里,并且该空间将不再被您的程序使用。

    当泄漏发生在一个多次调用的函数上时,这是一个严重的问题,每次调用函数时,泄漏都会越来越大。

        5
  •  3
  •   edgman    14 年前

    通常,函数(子例程)中的自动变量也将存储在堆栈中。只有“malloc”或“new”数据分配来自堆。接下来,可以在程序结束之前释放和重用基于堆的分配(很多次)。分配系统跟踪在用区域和自由区域。最后,内存泄漏是指程序在没有释放内存的情况下丢失了某些已分配内存的跟踪。这可以通过在指针上写入新值或将指针存储在寿命/作用域有限的变量中来实现。

        6
  •  3
  •   Jeremy Pridemore    14 年前

    看起来你在使用C++代码。在C++中,局部变量放在堆栈上(我猜全局变量也是,但我不确定)。所以中间字母函数中的len也将被放在调用堆栈中。我建议阅读本文: http://en.wikipedia.org/wiki/Call_stack

    当您将新的运算符与类型一起使用时,例如 int *x = new int; 例如,找到足够的连续内存来放置一个int。但是用来引用它的指针*x是一个局部变量。如果x超出范围,并且丢失指针,则不会释放堆上的内存。即使你现在没有办法引用它,这个内存仍然被你的程序“使用”。因为您不能引用它,所以您不能取消分配它(或删除它)。

    当你继续这样做的时候,你最终会耗尽你可以分配给堆的空间,你的程序将越来越接近崩溃,因为它没有内存来处理其他问题。

        7
  •  0
  •   supercat    13 年前

    在垃圾收集系统中,术语“内存泄漏”有时看起来有点模糊。我将提供以下定义,这与那些使用显式释放的系统一样适用于垃圾收集系统: 如果存在输入的初始序列s和输入p的重复模式,则程序或子程序p存在内存泄漏,这样:

    1. 向程序或子程序输入s输入p后,程序将保持与p之前相同的“有意义”状态,但
    2. 对于任意数量的内存Q,存在一定数量的n次重复,这样给程序输入s加上n次p的重复将导致程序使用超过数量的内存Q。

    如果所有的输入实际上都对程序的状态起作用,那么一个没有内存泄漏的程序就有可能使用与输入大小无关的内存。例如,一个程序读取一个输入文件并按相反的顺序将其写出来,将需要足够的内存来保存该文件。另一方面,内存泄漏的程序将需要无限量的内存来处理无限的输入流,即使所需的内存量应该是有限的。