7

可能重复:
有没有办法在 C++ 中强制进程的 WorkingSet 为 1GB?

我们希望提前将 .NET 进程的 WorkingSet 增加到 1GB,以避免出现页面错误。

有没有办法在.NET中做到这一点?

更新

不幸的是,即使我们调用SetProcessWorkingSetSizeEx,垃圾收集仍然会绕过 MinWorkingSet 修剪工作集(参见下图中的“自动 GC.Collect()”)。

在下图中,有没有办法将进程 WorkingSet(绿线)锁定为 1GB,以避免在向进程分配新内存时出现页面错误(红线)的峰值?

这很棒的原因是每次发生页面错误时,它都会阻塞线程 250us,这会严重影响应用程序性能。

在此处输入图像描述

更新

引自:“Windows via C/C++, Fifth Edition, Jeffrey Richter (Wintellect)”

单个进程对 SetProcessWorkingSetSize 的调用将被忽略,除非该进程只是试图清空其工作集。要设置此限制,请在 LimitFlags 成员中指定 JOB_OBJECT_LIMIT_WORKINGSET 标志。

这本书暗示设置工作集的唯一方法是将进程分配给作业对象并设置JOB_OBJECT_LIMIT_WORKINGSET和MinimumWorkingSetSize。

更新

SetProcessWorkingSetSizeEx 与软页面错误完全无关。它仅指硬页面错误,因为它防止当前工作集中的内存被分页到硬盘驱动器。

更新

事实证明,增加 WorkingSet 的唯一方法是使用用 C++ 编写的极其专业的CLR 主机运行 .NET (请参阅下面的答案)。

4

5 回答 5

4

为了实现你想要的,你需要调用/调用SetWorkingSetSizeEx,最小 1 GB(第二个参数)和QUOTA_LIMITS_HARDWS_MIN_ENABLE第四个参数,确保工作集大小不会低于你给出的最小值,即使在系统的“高内存压力”条件下.

系统行为还取决于调用者的权限,取决于您可能需要的操作系统版本等SE_INC_WORKING_SET_NAME和/或SE_INC_BASE_PRIORITY_NAME

在“幕后”使用这些 API 的另一个(更好的)选项是 .NET 包装器,您可以在此处找到。

于 2012-08-31T19:56:05.567 回答
3

如果您的问题是您的进程在内存不足的情况下过于激进地修剪其 WS,您可以通过调用SetProcessWorkingSetSize或仅设置Process.CurrentProcess.MinWorkingSet.

但是,您所展示的是您的工作集被 GC 减少了。这告诉我,真正发生的是 GC 正在重新分配构成您的 WS 的页面。如果是这种情况,则您遇到的是地址空间问题而不是工作集问题,并且您无法进行系统调用来防止它。理想情况下,您可以告诉 GC 不要将其内存返回给操作系统,但 .NET 没有这样的功能。

要解决地址空间问题,您将不得不重用已分配的对象。如果您的问题与大对象堆有关,则很可能是由于集合所致。例如,与其创建一个新的数组/列表/字典,不如调用它的Clear方法并重用它。如果您的问题是字符串,您可能可以使用StringBuilders 避开 LOH。

如果您创建了很多特定类型的对象,请考虑创建一个可回收的对象池。我从来没有做过这样的事情,但是如果我要实现它,我会创建一个带有静态工厂方法的对象,该方法将对象从池中拉出并调用初始化程序而不是公共构造函数,并在其上放置一个终结器将其放回池中并清空其中的任何引用。根据需要,池可能是ConcurrentBag<WeakReference<T>>.

于 2012-08-31T19:45:38.570 回答
2

我认为将流程分配给作业对象和设置JOB_OBJECT_LIMIT_WORKINGSET并且MinimumWorkingSetSize可能会起作用。

于 2012-09-01T07:58:09.590 回答
2

我们可以找到在 .NET 下增加进程的 WorkingSet 以减少软页面错误的唯一方法是在自定义CLR Host下运行整个 .NET 应用程序。这是一个非常重要的练习,需要大约 800 行自定义编写的、相当密集的 C++ 代码。C++ 代码拦截了对 Win32 内存管理方法的 .NET 调用,改变了 .NET 运行时的行为,因此它不会像往常那样积极地释放内存。

这会在应用程序启动时引发所有软页面错误,因此在正常的应用程序执行期间,.NET 应用程序中的软页面错误数量几乎降至零。

这意味着应用程序可能会占用大量内存,但它运行得更快。换句话说,我们正在牺牲内存使用来提高实时性能。

于 2012-09-21T11:46:47.153 回答
1

我认为这里有一个误解:释放未使用内存的 GC 是好的。仅仅为了在 WS 指标中看到更高的数字而保留它是没有用的……您不想WS 中使用未使用的内存。您正在针对错误的指标进行优化。

页面错误的数量意义不大,因为存在软错误和硬错误。从磁盘读取的硬故障。在大多数情况下,软故障毫无意义。它们是操作系统的实现细节。例如,对于您触摸的每个新分配的页面,都会发生软故障。

这里有一些证据表明您的红色图表没有显示硬故障:最后它显示大约 500/秒。您的磁盘无法提供 500 IOPS,因此它不可能是硬故障。

您可能只想测量(并减少)硬故障。只有硬故障才会以有意义的方式影响性能。

此外,设置 WS 限制不会影响 GC 的功能。GC 不是操作系统组件。Is 是一个用户模式库。它与操作系统决定用作工作集的内容无关。因此,您不能通过设置 WS 限制来使 GC 不释放内存。

那么为什么 GC 运行时 WS 会收缩呢?因为 GC删除了一些内存。设置一些 WS 限制不能防止删除内存。

在评论中,有人建议 GC 本身可能会调用一些 API 来缩小工作集。我根本看不出它为什么会这样做。为什么 GC 会强制页面退出进程?记住:WS 和分配的页面是不一样的(根本不一样!)。GC 可能会释放内存。它不会强制 WS 收缩。那会有什么目的?

于 2012-09-02T13:26:46.243 回答