进程终止时是否回收了内存?

Is a processes memory reclaimed when it terminates?

在我的一个应用程序中,我基本上在 C++ 中分配内存并在 C# 中将其排入队列以进行释放。此释放在后台运行并且是不确定的,因此在极少数情况下理论上可能会发生在释放所有非托管内存之前退出应用程序。

如果是这种情况,行为(粗略且非常精简)与我的程序相同

int main()
{
   Foo* = new Foo();
   return 0;
}

我现在的问题是

编辑: 这仅与 Windows 有关,正如一些人提到的那样,这取决于 OS。

编辑 2: 我不是在谈论简单地忽略我的应用程序中的所有内存泄漏,而是我是否需要确保在应用程序退出之前正确释放所有内存。

编辑 3: 这与打开的文件句柄、析构函数和副作用或任何东西无关,这是关于将被非确定性释放的内存以及在终止前未释放内存的极少数情况。

Is all the memory a program allocates, but not deallocates automatically reclaimed when the program exits or is it a memory leak that stays until I reboot?

当进程终止时,操作系统会自动回收内存。

If it is automatically reclaimed, what mechanism is responsible for that?

C++的内存管理器,排在newdeletemalloc等之后,从操作系统中获取比较大块的内存,负责管理更小粒度的内存块。操作系统跟踪已分配给进程的所有内存,并在进程终止时回收它。

除非您是在操作系统下进行低级别开发。该操作系统管理真实内存,为您的应用程序提供映射到真实内存的虚拟内存。

在您的应用程序关闭(它的进程终止)后,操作系统删除虚拟内存映射,释放该进程的所有内存资源。

如果您只关心内存,您可能不需要调用 delete,因为操作系统会破坏您的整个 virtual address space at process termination. Consider reading Operating Systems: Three Easy Pieces(可免费下载)以了解有关操作系统的更多信息。

但您想避免 memory leaks, so you'll better clean up properly. On some operating systems, you have tools like valgrind 以帮助检测此类泄漏(因此您不希望他们发出虚假警告)。

另请参阅 RAII。在现实生活中,Foo(或它使用的间接数据)的构造函数(或其他方法)可能会消耗其他资源(数据库连接、打开的文件、屏幕上的 windows、守护进程、远程连接到外部服务或网络服务器、机械臂等...)并且您希望妥善处理它们。

考虑使用 smart pointers

数据结构的活性是整个程序属性。我建议阅读 GC handbook, at least to understand the concepts and terminology of garbage collection(它可以被视为一种管理资源的方式,而不仅仅是原始内存)。

(在实践中,这取决于很多:如果你正在与数百名其他程序员一起编写一百万行代码程序,你需要比单独编写一个微小的单一源文件程序要更加小心;如果你编写一个神经外科手术机器人,它与桌面应用程序等不一样......所以 YMMV)

答案是 Windows 应该在您之后清理内存,如果没有这样做,则应将其视为操作系统中的错误。 Linux 以及任何其他让您过早终止进程的系统也是如此。

这样的原因,如果我没理解错也是你描述的情况比较类似于下面的情况:

int main()
{
   Foo* foo = new Foo();
   // The process is being brutally kill here. (kill -9 or Windows equivalent)
   delete foo;
   return 0;
}

因此,即使您按照应有的方式做了所有事情,并正确地进行了清理,但由于操作系统选项会过早地终止您的进程,这会导致内存泄漏。

最后,一个重要的澄清,这并不意味着你可以完全忽略这个话题,你应该尽最大努力自己清理,不要指望其他系统在你之后清理。

内存分配有两个不同的级别:

  1. 内存分配从OS到进程。这需要 syscallbrk()mmap()),并且完全由实现定义。 你自己永远不会看到这个。

  2. 从进程到单个数据对象的内存分配。这就是 operator new() 所做的。

new returns 指向数据对象的指针,因此主要与第二层有关。但是,new 本身没有可以分配给对象的内存,除非它先向 OS 请求一些内存。因此,它需要先进行一级分配。

new 本身的实现完全存在于您的进程中。因此,当您的进程以不自然的方式终止(由于信号而终止)时,new 实现没有机会清理任何东西。但这不是必需的,因为 OS 本身会跟踪在第一级分配的内存。它根本不信任一些随机进程来跟踪它们自己分配的内存。因此,不会发生永久内存泄漏。

但是,未能 delete 使用 new 创建的对象确实会产生后果:

  • 您的进程无法重用内存本身。如果您重复分配未能释放的对象,您的 new 实现将需要进行越来越多的第一级内存分配,直到您的 OS 没有剩余内存可分配给您的进程。

  • No delete 表示没有调用相应的析构函数。因此,您的流程可能会错过一些外部清理。例如,未能析构对象可能意味着一些临时文件未从文件系统中删除,因此您的进程泄漏磁盘 space 而不是内存。你也不想要那个,是吗?