虚拟内存地址空间不足(Borland C++ Builder 6程序)

3
我遇到了一些使用 C++ Builder 6 写的应用程序问题。运行一段时间(一周、一个月)后,应用程序会崩溃并关闭,而没有任何错误消息。在应用程序日志中,在崩溃前不久我得到了许多“内存不足”的异常信息。
当程序抛出内存不足异常时我查看了进程(如下图所示),发现有很多未提交的私有内存空间。这种行为的原因是什么?
几年前我也遇到了这样的问题。原因是链接器选项中的“使用动态库”未选中。当我重新选中它时,问题消失了,反之亦然。我制作的测试应用程序只是调用“new char[1000000]”,然后删除它。每次都释放了内存(在 Windows 任务管理器中没有已提交内存的上升),但是一段时间后我就会遇到内存不足问题,VMMap 显示完全相同的情况。有很多保留的私有内存,但其中大部分未提交。
现在问题又出现了,但我不能用同样的方法解决它。我不知道是否是原因,但我在同一台机器上安装了 Builder 6 和 2010。现在我只有 Builder 6,似乎无法像以前那样使用测试应用程序重现错误。无论如何,似乎存在某种内存管理器错误或其他问题。CodeGuard 没有显示任何内存泄漏。使用“new”创建内存块时,“内存提交大小”会立即显示出来,当删除内存时内存使用量会减少,因此我认为内存泄漏不是问题,任务管理器并没有显示出很多“内存提交大小”。
我是否能做些什么?是否有任何方法可以释放未提交的内存?如何进一步诊断问题?
屏幕截图: http://i.stack.imgur.com/UKuTZ.jpg

你是否调用了一个返回已分配内存但未释放的Windows API,Codeguard可能无法捕获这些问题。Codeguard并不是万无一失的。 - Gregor Brandt
该应用程序有数千行代码,因此可能会出现一些问题。例如,我猜测通过directsound播放波形。但由于项目的规模以及与其连接的各种硬件设备的依赖性(我在这里非常受限),很难定位问题。 - Krzysiek
我关心的是为什么它释放了大部分保留块的空间,但却留下一些已提交KB?这不奇怪吗?有很多1.9MB块,其中只有约50KB被提交(截图中显示)。即使它是API分配,如果它没有被释放,难道不应该被提交吗? - Krzysiek
还有一件事。关于“Codeguard并不是万无一失的”。是的,我知道,我的假设并不仅仅基于它,实际上我几乎不使用它。我看到的主要问题是,随着应用程序的内存使用量不断增加,内存似乎被释放了。正在使用应用程序虚拟内存地址空间。此外,我无论如何都无法模拟这种行为,所以我不知道该寻找什么。 - Krzysiek
相当古老但仍然有趣的问题...我必须添加一些东西,所以请看我的答案。 - Spektre
2个回答

3

我找到了一种模拟这个问题和解决方案的方法。

for(int i=0; i<100; i++)
{
    char * b = new char[100000000];
    new char;
    delete b;
}

Borland内存管理器会保留一个内存块,大小是4kB的倍数。当分配的内存大小不是4kB的倍数时,就会有一些空闲空间,Borland可能会用来分配其他内存块。当第一个内存块被释放后,第二个内存块仍然保持整块内存的保留状态。
乍一看,这段代码应该只会导致100B的内存泄漏,但实际上,在不到16次迭代后,它将导致内存分配异常。
我找到了两种解决方案。一种是FastMM,它可以解决问题,但也会带来一些麻烦。第二种解决方案是使用Embarcadero Rad Studio 2010中的borlndmm.dll替换Borland 6中的版本。虽然我还没有进行彻底的测试,但似乎可以正常工作而没有任何问题。
我应该将整个项目移植到RAD 2010中,但由于某些原因,我被困在Borland 6中。

0

序言

嗯,有趣的行为...我必须加上一些我艰难学到的东西。我试用了几次后就立即放弃了BCB6,因为它有太多的错误,不符合我的口味(与BCB5相比,特别是在处理AnsiString时)。所以我长时间使用BCB5而没有任何问题。我甚至将其用于像CAD/CAM这样的大型项目。

几年过去后,由于我的雇主,我不得不转移到BDS2006,问题开始出现(有些可能与你的类似)。除了较小的IDE和跟踪/断点/代码保护错误外,还有更重要的事情:

  • 内存管理器

    • delete/delete[] 如果对同一指针调用两次,而不抛出任何异常以通知,则会破坏内存管理器...
    • 结构体的错误默认构造函数/析构函数加上delete是我遇到的最大问题之一(与编译器的错误结合使用)
    • 类中错误或缺少的成员函数可能会导致多个delete调用!!!这是由于编译器或C++引擎中的错误而导致的。

    但我很幸运,在这里解决了它:bds 2006 C hidden memory manager conflicts (class new / delete[] vs. AnsiString)

  • 错误的编译

    有时应用程序会被错误地编译,没有抛出任何错误,但是一些代码行在exe文件中丢失和/或与源代码中的顺序不同。我也在BCB 5,6中偶尔看到这种情况。要解决这个问题:

    1. 删除所有临时文件,如~, obj, tds, map, exe,...
    2. 关闭IDE并再次打开,以确保(有时本地变量的视图(大多数是大数组)会破坏IDE内存)
    3. 再次编译
  • 注意断点/跟踪/codeguard行为与原始应用程序不同

    特别是在多线程应用程序中,跟踪和未跟踪的行为不同。此外,codeguard也有很大的区别(我并不是指执行速度减慢会影响敏感的定时)。例如,codeguard有一个讨厌的习惯,有时会无缘无故地抛出内存不足异常,因此必须一遍又一遍地检查一些代码部分,直到它通过为止,即使内存使用量仍然相同,并且远离内存不足。

  • AnsiString运算符

    VCL中有两种类型的AnsiString:常规类型和组件属性类型。因此,考虑到这一点是明智的,因为对于组件属性,AnsiString的操作符是不同的。尝试例如:

    Edit1->Text+="xxx";
    

    还有一些AnsiString运算符错误,例如:

    AnsiString version="aaa"+AnsiString("aaa")+"aaa";       // codeguard: array access violation
    
  • 导入较旧的BCB项目

    如果可能,避免直接导入,因为它经常会创建一些未知的分配和内存泄漏错误。我不确定为什么,但我怀疑导入的窗口类处理方式不同,而内存泄漏与bullet #1有关。更好的方法是创建新应用程序并手动创建/复制组件和代码。我知道这是一个退步,但这是避免问题的唯一安全方法。仍然不知道问题出在哪里,但简单的*.bdsproj替换将不起作用!!!在*.dfm中我没有看


  • 网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接