C# / .Net Framework 中的堆大小 - 它可以增长吗?如何增长? [复制]

Posted

技术标签:

【中文标题】C# / .Net Framework 中的堆大小 - 它可以增长吗?如何增长? [复制]【英文标题】:Heap size in C# / .Net Framework - Can it grow and how? [duplicate] 【发布时间】:2014-07-25 10:15:41 【问题描述】:

我在一本大学书籍中遇到了一个令人困惑的问题: 那里说“堆不是静态的,可以通过向操作系统请求更多内存来按需增长”。

所以我感到困惑的是:假设我运行我的应用程序,并且对象是在堆上分配的。在某些时候,应用程序内存不足:现在会发生什么?

据我了解,Gc(垃圾收集器)启动并开始它的标记和清除操作。我想知道堆是否有可能以某种方式从 CLR 调用 Gc,而不是从操作系统请求更多内存?

我在 C++ 语言中读到过,有一种方法可以实现,但在 C# .Net Framework 4.5 中是否可行?

【问题讨论】:

如果它需要,CLR 会请求更多内存——它只会尝试首先进行垃圾收集(通常,无论如何)。你到底想达到什么目的? 我只是想理解这句话。堆何时会请求更多内存?堆被分为 Gen 0,1,2。当 Gen 2 中的内存被填满时,是否会发生 Gen 2 中的垃圾回收?或者此时堆可以通过向操作系统请求更多内存来增长? 术语“GC 堆”是一个方便的抽象,但它与您在教科书中找到的那种堆不太相似。它不分配内存,它分配地址空间。 Windows 上的低级操作系统调用是 VirtualAlloc()。只有当垃圾收集没有释放足够的地址空间并且周围没有备用段时才会发生这种情况。又急着释放地址空间。内存不足实际上意味着可用地址空间不足。这通常发生在没有足够大的孔时。 【参考方案1】:

您可以通过将应用程序构建到 64 位架构来分配比约 2 GB 更多的内存,这需要您创建新的构建配置。为您的 .NET 项目使用普通(默认)“任何 CPU”构建选项,您的应用程序将始终在 32 位模式下运行,即使在 64 位 Windows 操作系统上也是如此。因此,在应用程序执行期间,您将无法分配超过 1.5 到 2 GB 的 RAM 内存。要在真正的 64 位模式下运行 .NET 应用程序,您需要进入构建配置管理器并为 x64 体系结构创建一个构建类型,然后使用该构建类型显式地为 x64 重新编译您的程序。可以使用以下步骤为您的 .NET 解决方案创建 x64 构建模式选项:

    在 Visual Studio“解决方案资源管理器”窗格中,右键单击解决方案图标并从弹出菜单中选择“配置管理器”选项。这将打开 .NET 解决方案文件的构建“配置管理器”对话框窗口。 在构建“配置管理器”对话框的右侧顶部,单击向下箭头并选择“&ltnew&gt”选项。这将打开“新解决方案平台”对话框。 在“新建解决方案平台”对话框中,对于“平台”选项,从下拉菜单中选择“x64”。然后单击“确定”按钮,新的 x64 构建选项现在将在“配置管理器”对话框中可用。 然后,在“配置管理器”对话框中,在“Active Solution Platform”下拉菜单中选择“x64”。单击“关闭”按钮。 在 Visual Studio“解决方案资源管理器”窗格中,右键单击 CS 项目图标并从弹出菜单中选择“属性”选项(此菜单底部的最后一个选项)。这将打开 CS 项目属性窗口。 在 CS 项目属性窗口的左侧,单击“构建”选项卡以显示代码项目的构建属性。在此窗口的顶部,请注意“平台”现在应该显示“x64”(而不是默认的“任何 CPU”选项)。如果“平台”下拉菜单未显示“x64”,您现在应该选择它。 然后只需构建您的代码,在“bin”文件夹中,您现在应该有一个 x64 文件夹,其中包含应用程序的新 64 位构建。

在 64 位 Windows 操作系统上使用 64 位构建的应用程序将允许您的程序分配超过 ~2GB 的内存,大概最多 2^64 个地址空间(如果您有 RAM 和磁盘空间可用,这是截至撰写本回复时的真正限制因素)。

如果您的应用程序内存仍然不足,您还可以增加 Windows 内存页面文件的大小。在 Windows 上,页面文件允许操作系统在 RAM 内存空间不足时将内存从 RAM 转移到磁盘。但是将部分 RAM 内存移入和移出磁盘会花费大量时间,因此它可能会真正影响应用程序的性能。无论性能如何,通过增加页面大小,您可以(理论上)使页面文件与 Windows 机器的 C: 驱动器上的可用空间一样大。在这种情况下,您的应用程序将能够在程序执行期间分配,例如,最多 4 TB 的内存(或您的页面文件大小设置的任何内存量)。要更改 Windows 机器的页面文件设置,请执行以下操作:

    右键单击“这台电脑”并在弹出菜单中选择“属性”选项,打开“系统属性”对话框。这也可以在更高版本的 Windows(Windows 10、Win 2012 Server 等)中通过转到“开始”>“控制面板”>“系统和安全”>“系统”来完成。 在“系统”对话框的左侧,单击“高级系统属性”选项。这将显示 Windows 旧版“系统属性”对话框的“高级”选项卡。 在旧版“系统属性”对话框的“高级”选项卡上,单击“性能”框中的“设置”按钮。这将选择“性能选项”对话框。 在“性能选项”对话框中,单击“高级”选项卡以查看 Windows 内存页面文件的当前大小设置。 要增加页面文件大小,请单击“更改”按钮,将打开“虚拟内存”对话框。 在“虚拟内存”对话框中,选择“C:”驱动器,然后在“自定义大小”下,设置“初始”和“最大”大小。您可以使用 C: 驱动器上最大可用空间量的任何大小,但进行此更改将为硬盘驱动器上的页面文件保留该空间。 然后在所有对话框上单击“确定”以提交新设置。然后重新启动计算机以确保所有更改都已正确完成并且新的页面文件设置正在运行。

无论如何,我希望这可以帮助人们理解为什么他们会在 .NET 应用程序中遇到这个 1.5 - 2 GB 内存限制问题,即使在 64 位 Windows 计算机上运行也是如此。这对人们来说可能是一个非常令人困惑的问题,我希望我的解释是有道理的。如果需要,请随时向我发送有关此答案的问题。

【讨论】:

"Using the normal (default) "Any CPU" build option for your .NET project, your application will ALWAYS run under 32-bit mode, even on a 64-bit Windows OS."-- 实际上不是这样。“任何 CPU”都将运行 64 位模式,除非您勾选“首选 32 位”【参考方案2】:

堆增长是因为您的应用程序正在分配和持有对象。每当您的应用程序需要存储指向您的应用程序当前正在使用的对象的指针时,堆就会请求内存。

每当您用完堆时,您都会收到错误提示,提示堆内存不可用或类似情况。为避免此类 GC,提供了此类 GC,如果您实现自己的 CLR 主机,则可以控制此行为。 please Refer

您可以采取一些措施来限制内存使用量。请参阅此问题以获取一些输入:Reducing Memory usage

要了解更多关于堆(ing)和栈(ing)的信息,请参考以下链接:

    Stack and heap Large Object Heap Uncovered Fundamentals of Garbage Collection

【讨论】:

【参考方案3】:

意识到an "out of memory" error doesn't mean you're out of physical memory 很重要。这意味着您的虚拟内存不足。在 32 位应用程序中,该应用程序虚拟地址空间中的项目有 2^32 个可能的地址。

操作系统负责将该虚拟地址空间的部分映射到实际的物理存储介质,无论是 RAM、硬盘驱动器还是任何其他存储设备。由于大多数应用程序并没有使用其绝大多数虚拟地址空间,因此大部分虚拟地址空间都没有映射,并且在它们第一次开始使用之前不对应任何东西。

虽然程序可以随时更改虚拟内存块对应于物理内存的实际块(程序一次使用超过 2^32 个字的内存所必需的)做这种情况并不常见,通常只能由真正需要一次存储大量内存的视频处理应用程序等程序完成。此过程也是手动。如果您不手动交换每个虚拟内存块映射到的内容,则可能会耗尽虚拟内存,即使您仍有能够存储更多信息的存储设备。这是内存不足错误。

堆,在 C# 应用程序中,几乎是没有被堆栈占用的整个虚拟地址空间、静态字段的存储以及任何其他开销,例如程序实际代码的内存.从应用程序的角度来看,堆从一开始就和它一样大。操作系统已经抽象出这样一个事实,即几乎所有 C# 运行时所认为的“它的内存”实际上并没有映射到任何地方,因为它没有被使用;随着您开始使用更多的堆,实际上越来越多的堆被映射到某种形式的物理存储。

如果您有 64 位应用程序,您基本上可以在此答案中将 32 的所有实例替换为 64

【讨论】:

【参考方案4】:

.Net 中的堆是内存的一部分,对于实际用途,对于 32 位进程可以达到最大约 1.5GB,对于 64 位进程是无限的(同样,出于实际目的),它确实会请求内存根据操作系统的需要。但是,由于过多的碎片和使用大分配,它可能会耗尽内存 - GC 不会压缩大对象堆。

当您在 C++ 中执行此操作时,您可以完全控制您希望如何制定内存分配策略,在 .NET 中为您完成,并且您对如何完成几乎没有发言权,即使在大多数情况下它确实如此干得好。

【讨论】:

在 C++ 中,通常不会控制虚拟内存如何映射到物理内存,如何为应用程序分配内存等。这一切都由操作系统和内存管理器处理C++。您在 C++ 中 负责管理内存的生命周期,这意味着当您不再需要它时进行指示。这与控制内存的来源或释放时的去向完全不同。 这取决于您所指的系统。在 Windows 的早期(准确地说是 2.13),我有 C++ 代码,可以从大堆中分配缓冲区,我的代码决定从哪个缓冲区真正分配对象。这使我能够从本质上创建当今 .Net 中的各种堆。我同意我无法控制大堆来自内存的位置。 事实上,您的代码仍然只是访问 virtual 内存,而将虚拟内存映射到物理内存的所有工作都是由操作系统完成的。作为一名程序员,即使在那时,您仍然处于东方的抽象概念,即您有 2^32(或 16 个或其他)连续的内存字,并且内存管理器通常会进一步管理 以便你可以说“我想要 10 个字节的数据”,它会给你一些,或者你可以说“我已经完成了这 10 个字节”,它会考虑把它给其他人。那是堆,您通常不必管理它。【参考方案5】:

从某种意义上说,堆是动态的,当您添加到堆中时,它会增长。它在某种意义上创建指向内存中其他位置的指针。在内存耗尽之前,应用程序可以占用的内存大小总是有限制的。当达到限制时,通常会出现“内存不足”异常。 阅读 wiki 可能会有所帮助:http://en.wikipedia.org/wiki/Heap_%28data_structure%29 http://en.wikipedia.org/wiki/Heap_%28programming%29#Dynamic_memory_allocation

【讨论】:

以上是关于C# / .Net Framework 中的堆大小 - 它可以增长吗?如何增长? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何增加.NET 的堆大小? [复制]

C# 扫描识别图片中的文字(.NET Framework)

C# .NET Framework API(非 Win32 API)中的 CreateProcessAsUser()

.NET Framework 类库——C#命名空间大全

如何使用 Oracle 和 SQL Server 将 .NET 4.5 C# Entity Framework 6 中的列映射为大写?

在哪个 .Net Framework 版本 C# 7.2 可用