[英]Program crashes only in Release mode outside debugger
我有相当大的程序(> 10k行的C ++代码)。 从Visual Studio中启动时,它在调试模式或发布模式下都能正常工作,但是从命令行手动启动时,释放模式二进制文件通常会崩溃(并不总是!!!)。
删除行会导致崩溃:
bool Save(const short* data, unsigned int width, unsigned int height,
const wstring* implicit_path, const wstring* name = NULL,
bool enable_overlay = false)
{
char* buf = new char[17];
delete [] buf;
}
编辑:根据请求扩展了示例。
在我的测试用例中,“len”长度为16。 没关系,如果我对buf做了一些事情,它会在删除时崩溃。
编辑:应用程序工作正常没有删除[]行,但我想它会泄漏内存(因为块永远不会被分配)。 删除行后从未使用的buf。 它似乎也不会与除char之外的任何其他类型崩溃。 现在我真的很困惑。
崩溃消息非常不明确(典型的Windows“xyz.exe已停止工作”)。 当我单击“调试程序”选项时,它进入VS,其中错误被指定为“访问冲突写入位置xxxxxxxx”。 虽然“没有为任何堆栈帧加载符号”,但无法找到错误的位置。
我想这是一个非常严重的堆损坏案例,但如何调试呢? 我应该寻找什么?
感谢帮助。
你有没有检查其他地方的内存泄漏?
通常奇怪的删除行为是由于堆在某一点上被破坏引起的,然后在很久之后,由于另一个堆的使用而变得明显。
调试和发布之间的区别可能是由Windows在每个上下文中分配堆的方式引起的。 例如,在调试中,堆可能非常稀疏,并且损坏不会立即影响任何内容。
您可能在某处有内存覆盖,而delete []只是第一次导致问题。 但覆盖本身可以位于程序的完全不同的部分。 困难在于找到覆盖。
添加以下功能
#include <malloc.h>
#define CHKHEAP() (check_heap(__FILE__, __LINE__))
void check_heap(char *file, int line)
{
static char *lastOkFile = "here";
static int lastOkLine = 0;
static int heapOK = 1;
if (!heapOK) return;
if (_heapchk() == _HEAPOK)
{
lastOkFile = file;
lastOkLine = line;
return;
}
heapOK = 0;
printf("Heap corruption detected at %s (%d)\n", file, line);
printf("Last OK at %s (%d)\n", lastOkFile, lastOkLine);
}
现在,在整个程序中经常调用CHKHEAP()并再次运行。 它应该显示源文件和堆损坏的行以及最后一次它可以的位置。
崩溃有很多可能的原因。 找到它们总是很困难,特别是当它们从调试模式到发布模式不同时。
另一方面,由于您使用的是C++
,您可以通过使用std::string
而不是手动分配的缓冲区来逃避>>存在RAII
存在的原因;)
我观察到这种症状时遇到的一种问题是,当我在shell中运行时,我遇到了一个多进程程序崩溃,但是当从valgrind
或gdb
调用时,它运行得很完美。 我发现(很多我的尴尬),我在系统中仍然运行了一些相同程序的杂散进程,导致mq_send()
调用返回错误。 问题是这些杂散进程也被内核/系统分配了消息队列句柄,因此我新生成的进程中的mq_send()
)失败,但不确定(根据内核调度情况)。
就像我说的那样,琐碎,但直到你发现它,你才会把你的头发撕掉!
我从这个艰难的课程中学到了,现在我的Makefile
有了所有适当的命令来创建新的构建,并清理旧的环境(包括拆除旧的消息队列和共享内存和信号量等)。 通过这种方式,我不会忘记做一些事情,并且必须对看似困难(但显然是可以解决的)问题感到焦虑。 这是我最新项目的剪切和粘贴:
[Makefile]
all:
...
...
obj:
...
clean:
...
prep:
@echo "\n!! ATTENTION !!!\n\n"
@echo "First: Create and mount mqueues onto /dev/mqueue (Change for non ubuntu)"
rm -rf /run/shm/*Pool /run/shm/sem.*;
rm -rf /dev/mqueue/Test;
rm -rf /dev/mqueue/*Task;
killall multiProcessProject || true;
这两个是它们功能中的前两行。
如果你的意思是我解释它的方式,那么第一行是在一个函数中声明一个局部变量buf,但删除是删除在第二个函数之外声明的一些不同的buf。
也许你应该展示这两个功能。
您是否曾尝试使用相同的构建文件将其隔离,但仅根据您上面的内容编写代码? 就像是:
int main(int argc, char* argv[] )
{
const int len( 16 );
char* buf = new char[len + 1];
delete [] buf;
}
您给出的代码绝对正常,并且在它自己的代码中,应该在调试或优化时运行没有任何问题。 因此,如果问题不在于您的代码的具体细节,那么它必须归结为项目的细节(即编译/链接)
您是否尝试过创建一个全新的项目并将10K + C ++行放入其中? 可能不需要太长时间来证明这一点。 特别是如果现有项目已经导入或严重改变。
听起来你在代码中的某个地方有一个整体变量。
在调试模式下,所有内存都初始化为标准,因此您将获得一致的行为。
在释放模式下,除非您明确地执行某些操作,否则不会初始化内存。
使用最高级别设置的警告运行编译器。
然后确保代码编译没有警告。
我遇到了同样的问题,我发现当我去删除字符串长度为1的[]字符串指针时,我的程序才崩溃。
void DeleteCharArray(char* array){
if(strlen(array)>1){delete [] array;}
else{delete array;}
}
这解决了这个问题,但它仍然容易出错,但可以修改为其他方式。 无论如何,我怀疑这是因为C ++ char * str = new char [1]和char * str = new char; 是同一个东西,这意味着当你试图删除一个只有数组的delete []时 ,结果是意外的,而且往往是致命的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.