释放记忆意味着什么?这是否意味着将所有位设置为零?

时间:2013-07-06 00:55:11

标签: memory-management

我直接从托管语言开始,几乎没有任何C ++经验,因此这个问题可能太基础了。

在像.net这样的托管语言中,GC释放内存。从我读到的,在C ++中,这是通过调用delete来完成的。但它如何释放记忆呢?它是否将内存位置的所有位设置为零?或者以其他方式告诉操作系统内存是否可以重用?

更新: 我之前已经过this而且我知道GC做了什么。但这不是我的问题。我不想问GC是如何工作的。我想要了解的是,你怎么说一些记忆是免费的?

6 个答案:

答案 0 :(得分:4)

delete做了三件不同的事情:

  1. 运行对象的析构函数(或delete[]的情况下运行数组中的所有对象。)

  2. 将对象先前使用的内存块标记为空闲。

  3. 如果可能,请通知操作系统其他程序可以使用一块内存。

  4. 你的问题是关于#2和#3,但它们是非常不同的东西。要理解#2是如何工作的,请记住操作系统提供的(通常)单个“堆”被分段为不同大小的较小块。当您使用new分配一块内存时,您将获得指向堆的先前空闲部分的指针,并且运行时执行必要的簿记,将该区域标记为不可用于进一步分配。 delete执行相反的操作:它执行簿记,将区域再次标记为可用,可选地将其与相邻的自由区域合并以减少碎片。对new的后续调用将在寻找可用内存返回时考虑该区域。

    换句话说,在解除分配时询问内存会发生什么是错误的。真正的魔力发生在簿记区域!要了解通用分配器的实现,请访问google以实现malloc。

    对于#3,它是可选步骤,并且在许多情况下不可能执行。只能“回馈”释放的内存,这些内存恰好位于分配堆的最末端。位于大区域之后的单一分配将消除回馈的可能性。

答案 1 :(得分:2)

在C ++中,如果使用“new”分配内存,操作系统会将该部分内存分配给该特定进程,直到您释放该内存或该进程退出为止。

如果为进程分配的某些内存意味着操作系统不允许其他进程使用该部分内存,直到该进程释放该内存。在C ++中,您必须使用“删除”来释放内存。

通过释放内存部分,进程只是通知操作系统它不再使用此部分,以便OS可以在其他进程请求内存时分配该内存部分。在那种情况下,内存部分的内容不会改变。

答案 2 :(得分:1)

垃圾收集只是自动内存管理(所以你永远不需要delete任何东西,系统会为你处理它)。我不是百分之百关于它是否将内存位置设置为0,但我认为它没有,因为当你在c ++中delete时,那不是发生的事情,它只是允许空间用于存储。在所有内容上写零是效率低得多而且没有必要。这里有一些链接可能有助于更彻底地解释这一点:

How does garbage collection and scoping work in C#?

http://en.wikipedia.org/wiki/Garbage_collection_(computer_science)

答案 3 :(得分:1)

不,它不会将位设置为零。在一个非常简单的解释中,

首先,垃圾收集器必须确定哪些对象不再可访问(“不可访问”),但哪些仍然可访问或可访问。它只需列出所有对象 roots 即可。根是一个内存位置,包含指向引用对象(堆上的对象)的指针。然后,递归地,它将根目录引用的每个对象标记为“可达”,或者由已标记为可到达的对象的字段或属性引用。 根有四种类型。

  1. 包含参考对象的静态变量
  2. 在堆栈上为任何当前活动的线程引用对象。
  3. 方法参数中的参考类型
  4. CPU寄存器指向的引用对象。
  5. 在确定应用程序域中的任何代码仍可访问(可访问)哪些引用对象之后,它将获取所有仍可访问的对象,如果它们之间的物理内存中存在任何间隙,则会对它们进行“碎片整理”。移动它们中的一些以使它们都是连续的,然后它设置指针,该指针代表“结束”od“used”内存到这个新的压缩碎片整理列表的末尾。然后,对于新实例化的对象,所有新的内存分配将在该指针位置之后立即从内存中分配。

    如果可到达对象使用的内存中没有间隙,则只会将指针重置为列表中最后一个可到达对象的末尾。

答案 4 :(得分:1)

在每个应用程序中,动态内存由“堆”管理。当您的代码要求一块内存时,它会要求堆管理器分配一块内存,当您的应用程序释放该内存块时,它会将其返回给堆管理器。在传统应用程序中,必须显式返回分配的每个内存。否则你最终会耗尽内存。

在C#或Java等语言中,运行时提供垃圾收集器。垃圾收集器自动识别“无法访问”的内存块并释放它们。不可靠的内存块是一个不再被任何变量引用的块。例如,如果您有一个指向内存块的全局变量p1,因为p1是全局的,因此它对代码中的任何位置都是可见的,然后它总是可以访问的。因此垃圾收集器永远不会释放它。另一方面,如果你的一个函数Foo中有局部变量p2,则在Foo返回后不再可以访问可变p2。垃圾收集器能够识别这些变量并释放它们指向的任何内存块。

当应用程序/垃圾收集器与堆交互时,堆可能会决定从操作系统请求更多内存或将其返回给操作系统。操作系统管理来自不同进程的所有这些内存请求,然后决定如何将实际物理内存分配给不同的进程。

答案 5 :(得分:1)

不,删除指针不会将字节设置为零。 它当然不符合标准,但它会带来性能开销,严重的实现也不会费心去做,当内存用于复杂对象(浮点数,对象,字符串等)时它甚至没有意义

你可以随时试试。 声明一个指向int的指针,写一个整数,删除指针。 然后再次读取已删除指针的内容。 它有相同的内容吗?

int *ptr = new int;
*ptr = 13;
cout << "Before delete: " << *ptr << endl;
delete ptr;
cout << "After delete: " << *ptr << endl;

是的,可能它会,但是ptr只是你那里的一个悬空指针,内存已经返回到系统并且当你再次分配内存时它将可用,很可能当你分配另一个int *它将是指着ptr指向的地方。