windows - 我的 32 位应用程序可以做什么来消耗数 GB 的物理 RAM?

标签 windows delphi winapi virtual-memory delphi-5

几个月前,一位同事向我提到,我们的一个内部 Delphi 应用程序似乎占用了 8 GB 的 RAM。我告诉他了:

That's not possible



32 位应用程序只有 32 位虚拟地址空间。即使存在内存泄漏,它最多可以消耗 2 GB 的内存。之后分配将失败(因为虚拟地址空间中没有空白空间)。在内存泄漏的情况下,虚拟页面将被交换到页面文件中,从而释放物理 RAM。

但他指出,Windows 资源监视器表明系统上可用的 RAM 不足 1 GB。虽然我们的应用程序只使用了 220 MB 的虚拟内存:关闭它可以释放 8 GB 的物理 RAM。

所以我测试了它

我让应用程序运行了几个星期,今天我终于决定测试它。

首先,我在关闭应用程序之前查看内存使用情况:
  • 工作集 (RAM) 是 241 MB
  • 使用的总虚拟内存: 409 MB

  • enter image description here

    我使用资源监视器来检查应用程序使用的内存以及正在使用的总 RAM:
  • 应用程序分配的虚拟内存: 252 MB
  • 正在使用的物理内存: 14 GB

  • enter image description here

    然后关闭应用程序后的内存使用情况:
  • 正在使用的物理内存: 6.6 GB (低 7.4 GB)

  • enter image description here

    我还使用 Process Explorer 查看前后物理 RAM 使用情况的分割。唯一的区别是 8 GB 的 RAM 真的未提交,现在免费:
    | Item                          | Before     | After     |
    |-------------------------------|------------|-----------|
    | Commit Charge (K)             | 15,516,388 | 7,264,420 |
    | Physical Memory Available (K) |  1,959,480 | 9,990,012 |
    | Zeroed Paging List  (K)       |    539,212 | 8,556,340 |
    

    enter image description here

    注意:有点有趣的是,Windows 会浪费时间立即将所有内存清零,而不是简单地将其放在备用列表中,并根据需要将其清零(因为需要满足内存请求)。

    这些都不能解释 RAM 是什么 (你坐在那里干什么!你里面装的是什么!?)

    那内存里是什么!

    该 RAM 必须包含 某事有用;它必须有 一些 目的。为此,我求助于 SysInternals 的 RAMMap .它可以分解内存分配。

    RAMMap 提供的唯一线索是 8 GB 的物理内存与名为 的东西相关联。 session 私有(private) .这些 session 私有(private) 分配与任何进程(即不是我的进程)无关:
    | Item                   | Before   | After    |
    |------------------------|----------|----------|
    | Session Private        | 8,031 MB |   276 MB |
    | Unused                 | 1,111 MB | 8,342 MB | 
    

    enter image description here

    我当然不会对 EMS、XMS、AWE 等做任何事情。

    在 32 位非管理员应用程序中可能会发生什么导致 Windows 分配额外的 7 GB RAM?
  • 它不是换出项目的缓存
  • 它不是 SuperFetch 缓存

  • 它就在那里;消耗内存。

    session 私有(private)

    关于“ session 私有(private)”内存的唯一信息来自 a blog post announcing RAMMap :

    Session Private: Memory that is private to a particular logged in session. This will be higher on RDS Session Host servers.



    这是一款什么样的应用

    这是一个 32 位 native Windows 应用程序(即不是 Java,不是 .NET)。因为它是 native Windows 应用程序,所以它当然会大量使用 Windows API。

    应该注意的是,我并不是要求人们调试应用程序;我希望那里的 Windows 开发人员会知道为什么 Windows 可能会保留我从未分配过的内存。话虽如此,最近(在过去的 2 或 3 年中)唯一可能导致这种情况的变化是每 5 分钟截取一次屏幕截图并将其保存到用户的 %LocalAppData% 的功能。文件夹。计时器每五分钟触发一次:
    QueueUserWorkItem(TakeScreenshotThreadProc);
    

    和线程方法的伪代码:

    void TakeScreenshotThreadProc(Pointer data)
    {
       String szFolder = GetFolderPath(CSIDL_LOCAL_APPDTA);
       ForceDirectoryExists(szFolder);
    
       String szFile = szFolder + "\" + FormatDateTime('yyyyMMdd"_"hhnnss', Now()) + ".jpg";
    
       Image destImage = new Image();
       try
       {
          CaptureDesktop(destImage);
    
          JPEGImage jpg = new JPEGImage();
          jpg.CopyFrom(destImage); 
          jpg.CompressionQuality = 13;
          jpg.Compress();
    
          HANDLE hFile = CreateFile(szFile, GENERIC_WRITE, 
                FILE_SHARE_READ | FILE_SHARE_WRITE, null, CREATE_ALWAYS,
                FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_ENCRYPTED, 0);
          //error checking elucidated
          try
          {
              Stream stm = new HandleStream(hFile);
              try
              {
                 jpg.SaveToStream(stm);
              }
              finally
              {
                 stm.Free();
              }
           }
           finally
           {
              CloseHandle(hFile);
           }
        }
        finally
        {
           destImage.Free();
        }
    }
    

    最佳答案

    很可能在您的应用程序中的某个地方,您正在分配系统资源而不是释放它们。任何创建对象并返回句柄的 WinApi 调用都可能是可疑的。例如(在内存有限的系统上运行时要小心——如果你没有 6GB 可用空间,它会导致页面错误):

    Program Project1;
    
    {$APPTYPE CONSOLE}
    uses
      Windows;
    
    var
      b : Array[0..3000000] of byte;
      i : integer;    
    begin
      for i := 1 to 2000 do 
        CreateBitmap(1000, 1000, 3, 8, @b);
      ReadLn;
    end.
    

    由于分配了随后不会释放的位图对象,这会消耗 6GB 的 session 内存。应用程序内存消耗仍然很低,因为对象不是在应用程序的堆上创建的。

    但是,在不了解您的应用程序的更多信息的情况下,很难更具体。以上是展示您正在观察的行为的一种方式。除此之外,我认为您需要调试。

    在这种情况下,分配了大量 GDI 对象 - 然而,这不一定是指示性的,因为通常在应用程序中分配了大量小 GDI 对象而不是大量大对象(Delphi IDE例如,通常会创建 >3000 个 GDI 对象,这不一定是问题)。

    enter image description here

    在@Abelisto 的例子中(在评论中),相比之下:
    Program Project1;
    
    {$APPTYPE CONSOLE}
    uses
      SysUtils;
    
    var
      i : integer;
      sr : TSearchRec;
    begin
      for i := 1 to 1000000 do FindFirst('c:\*', faAnyFile, sr);
      ReadLn;
    end.
    

    这里返回的句柄不是 GDI 对象,而是搜索句柄(属于内核对象的一般类别)。在这里我们可以看到进程使用了​​大量的句柄。同样,进程内存消耗很低,但使用的 session 内存却大幅增加。

    enter image description here

    类似地,对象可能是用户对象——它们是通过调用诸如 CreateWindow 之类的东西创建的。 , CreateCursor ,或通过设置 Hook SetWindowsHookEx .有关创建对象并返回每种类型的句柄的 WinAPI 调用列表,请参阅:

    Handles and Objects : Object Categories -- MSDN

    通过将问题缩小到可能导致问题的调用类型,这可以帮助您开始追踪问题。如果您正在使用任何第三方组件,它也可能位于有问题的第三方组件中。

    像 AQTime 这样的工具可以分析 Windows 分配,但我不确定是否有支持 Delphi5 的版本。可能还有其他分配分析器可以帮助跟踪这一点。

    关于windows - 我的 32 位应用程序可以做什么来消耗数 GB 的物理 RAM?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32357314/

    相关文章:

    windows - Docker native Windows 支持?

    delphi - 过滤Unicode中的不可键入字符

    c++ - 无法将参数 1 从 WCHAR 转换为 const char *

    windows - GetProcAddress 为 RegDeleteKeyEx 返回 NULL

    c++ - 如何在 win32 中获取 HWND?

    java - 在呈现 Java 代码时提供帮助的程序?

    Linux/Windows : User mode vs Privilege mode time spent

    c++ - 在 OpenGL 应用程序中添加菜单栏和附加窗口

    delphi - 无法为TMonthCalendar设置颜色

    delphi - Delphi 控件中的 Ctrl+Backspace