如果调试运行正常,但发布版崩溃了,应该怎么办?

8

我有一个应用程序,在调试版本中运行得很好,但当我在发布版本中启动它时,会出现

unhandled Exception at 0x0043b134 in myapp.exe: 0xC0000005:
Access violation while reading at position 0x004bd96c

如果我点击“中断”,它会告诉我没有加载符号,无法显示源代码。在这种情况下,我该如何追踪问题?

这种情况有时是由于内存问题引起的。通常,发布版本在这个领域不太容易出错。你的程序可能存在内存泄漏、缓冲区溢出等问题吗? - Cam
1
好像你已经删除了一些东西,但请尝试使用它。 你能贴出一些代码吗?如果不告诉我们你在做什么,我们无法做任何事情。 - GManNickG
11
当这种情况发生在我身上时,我通常会尽早前往酒吧... - James McNellis
5
通常出现这种情况是由于调试构建会初始化您未明确初始化的变量。当您在发布模式下构建时,这些变量现在包含随机值,而不是调试模式为您设置的漂亮整洁的0(NULL)值。要解决此问题,请将编译器警告级别设置为更高,并修复所有警告(特别是提到未初始化变量的警告)。附言:警告通常是问题所在,因此始终解决它们并始终将编译器警告级别设置为最高。 - Martin York
是的,我刚试过了。在VS上旧版本的最大值为4,因此我的项目模板都设置为4,以便新项目自动继承此级别(原始模板的级别为3)。多年来我第一次看到这个设置,现在有一个更高的级别。错误数量太多了,很多似乎来自标准库,这真的很痛苦。 - Martin York
显示剩余6条评论
10个回答

12

这种问题通常是由于未初始化的变量引起的。我建议从这里开始查找问题。

调试模式更加宽容,因为经常配置为初始化没有明确初始化的变量。

也许你正在删除一个未初始化的指针。在调试模式下可以工作,因为指针被清空并且对 NULL 进行 delete ptr 是可以的。但在发布模式下则是垃圾值,然后 delete ptr 就会导致问题。


这听起来很合理!我能告诉Visual Studio在调试版本中不初始化变量吗? - Mat
我在我的调试构建中激活了/ RTCu(未初始化的变量)-如果有任何未初始化的变量,这不应该会出现提示吗? - Mat
在大多数情况下是这样的,但并非所有情况都是如此。如果指针可以通过其他方式进行初始化,则将忽略此检查。实际上,您可能已经使用了地址运算符&并通过指针初始化了变量。因此,它仍然可能缺少初始化并且不会产生警告。请参见http://msdn.microsoft.com/en-us/library/8wtf2dfz.aspx。 - Montdidier

4
可能是以下两种情况之一:
  • 你的一个或多个断言除了检查本身之外还有必要的工作
  • 其他原因
为了排除第一种情况,在调试版本中将assert重新定义为空操作。如果某些断言缺失导致崩溃,你将看到它。否则,就是其他原因。
此外,我假设你有版本控制。这是最近才开始发生的吗?你可以分析上周的代码更改。
最后,即使在调试模式下没有崩溃,运行内存检查工具也可能有助于发现不正确的内存访问。

除了这个项目之外,我在所有的项目中都使用版本控制,因为这台正在开发的机器没有连接到互联网 :/ 真是太傻了... 我重新定义了 assert 什么也不做,并且它在调试构建时不会崩溃。我要谷歌一下“内存检查工具”——你能推荐一个吗? - Mat
@Mat:还要考虑@Grozz的建议,使用日志输出。这肯定会让事情变得清晰明了。 - Daniel Daranas
@Mat: 至于内存检查,请参阅 https://dev59.com/ZXNA5IYBdhLWcg3wVcFx,https://dev59.com/SHVC5IYBdhLWcg3w1E_w,https://dev59.com/33RB5IYBdhLWcg3wZWbi 以及类似的问题。 - Daniel Daranas

3
你确定两个版本都使用相同的.dll吗?我曾经花了一个小时想弄清楚为什么我的程序在调试模式下编译成功,但在发布模式下却失败了,结果只是因为我忘记更新发布文件夹中的dll文件。

3

两个步骤:

a) 使用调试符号构建发布版本(至少使用VS可以实现)

b) 构建没有优化的发布版本

如果问题仍然出现,那么很好解决,并且几乎就像在调试版本中出现了问题。

如果问题发生在启用优化设置时,则真的很难处理,必须根据具体情况进行处理。


如果我使用调试符号构建发布版本,它就不会崩溃。 - Mat
@Mat:这非常奇怪,因为二进制文件在所有方面上应该是相同的。主要区别在于“调试符号”选项会创建一个单独的.PDB文件,描述内存地址和函数/变量之间的关系。也就是说,它应该告诉您地址0x0043b134或地址0x004bd96c处的内容是什么。 - MSalters
@MSalters:我认为原因是发布二进制文件时总是带有一些优化(例如/O2)。我怀疑这与优化标志有关。 - Chubsdad
1
这里的说法是,“正常”的发布版本会崩溃,但带符号的发布版本不会。这很奇怪,因为两个版本应该具有相同的优化标志等。然而,我刚刚注意到/DEBUG意味着/OPT:NOREF, /OPT:NOICF;文档说明您需要显式设置它们以获取带调试信息的发布版本。现在这两个标志将成为我的主要嫌疑对象。 - MSalters

1
我曾经遇到过这个问题,在Visual Studio内部,release/debug都可以正常工作,但是stand alone情况下debug可以正常工作,而release则会崩溃。对于我来说,调试并不特别准确,我的解决方法是:注释掉大部分代码,然后< strong>构建、测试、取消注释、重复,直到找到导致崩溃的部分。在我的情况下,是将一个指向太小数组的指针传递给了一个函数。

1

main函数一开始就插入日志输出,以追踪问题。


1

如果不是内存问题,那么你需要在发布版本中启用断言。对于内存问题,希望你有好的单元测试。你可以使用valgrind轻松捕获此类问题。

顺便问一下,为什么人们要在发布版本中禁用断言?在99%的情况下,它们不会导致性能问题,并且可以很好地检测错误。


1
使用Microsoft debugdiag在Windows上进行崩溃转储(它是免费的),然后使用相同的工具分析转储文件。它会给出函数崩溃时的详细调用堆栈。不过,如果程序在各个地方都崩溃,可能是堆损坏的问题。这时你需要与debugdiag一起使用全局标志(或者gflags,它是微软调试工具套件的一部分,也是免费的)。Gflags可以告诉你堆实际上被破坏的位置。希望对你有所帮助。

0

不看代码很难确定问题出在哪里。以上所有建议都是好的和有用的,但我发现最有帮助的方式是将程序的某些部分分块运行来修复这种问题。也就是说,注释掉大量的代码/功能,然后运行程序并查看是否崩溃。如果没有,那么取消注释一些功能,再次运行,如此反复。通过这种方式,您将能够将问题缩小到导致其发生的确切代码。

在大多数情况下,这是由于一些缓冲区溢出引起的,而调试构建可以预防。


0

对我来说,问题在于构造函数以错误的顺序初始化了两个成员变量。即它们声明的顺序不同。
我很惊讶初始化顺序实际上会有任何影响。


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