应用程序崩溃,提示:访问位置时发生访问冲突

4
我的应用程序运行约18小时后崩溃。我无法调试代码中实际崩溃的点。我检查了调用堆栈-它没有提供任何信息。调用堆栈中的最后几个调用都是灰色的,意味着我无法看到该部分的代码,它们都属于MFC库。
但是,当它崩溃时,我会得到这个“MicroSoft Visual Studio”弹出窗口,其中显示:
在NIMCAsst.exe中未处理的异常0x7c809e8a:0xC0000005:访问位置0x154c6000时违规。
上述信息是否有助于了解崩溃发生的地方?是否有任何软件可以告诉我特定的内存地址由代码中的哪个变量持有。

1
它只是在某个随机点崩溃了。它进入了MFC dll并在那里崩溃,调用堆栈没有显示哪个代码点控制了那里。 - Rakesh Agarwal
如果您已经连接了调试器,您应该能够清楚地看到代码的哪一行调用了MFC。如果没有,那么可能是启用了优化或者.pdb文件与可执行文件不同步。 - sharptooth
你好@RakeshAgarwal,我在VS 2005中的c++项目中遇到了同样的错误。我只是想知道你是如何解决这个错误的。如果你能和我分享你的方法,那将非常有帮助。 - Monu Chaudhary
8个回答

5

如果有时您无法捕获异常,那么您只能逐行检查代码,这非常不愉快,但我敢打赌,这是您的代码而不是MFC(我的错误总是如此)。请特别注意您如何使用内存以及将什么传递给MFC函数。


2
可能的崩溃原因是缓冲区溢出或其他类型的内存损坏。这已经覆盖了一些保存返回地址的堆栈部分,使得调试器无法正确重构堆栈跟踪。或者,导致崩溃的代码没有正确的符号(如果堆栈跟踪显示模块名称,则会出现这种情况)。
我的第一个猜测是检查调用崩溃代码的代码是否存在可能导致崩溃的问题。在崩溃之前是否有任何其他异常或错误条件?也许你忽略了一个错误返回?你尝试过使用Debug Heap吗?adplus呢?Application verifier来打开堆检查?
其他可能性包括运行像pclint这样的工具来检查内存使用的明显问题。你正在使用线程吗?也许存在竞争条件。这个列表可以无限延伸。

错误可能会出现,但崩溃发生的时间不一致-它在运行6小时后的任何时间都会崩溃。因此,我无法在如此长的时间内观察返回的错误。 - Rakesh Agarwal

1
以上信息仅告诉您哪个内存被非法访问。
您可以使用异常处理来缩小问题发生的位置,但是您至少需要知道在哪个角落寻找。
您说您看到调用堆栈,这表明您正在使用调试器。MFC的源代码可用(但可能不包含所有的vc++版本),因此原则上可以通过它进行跟踪。您使用的是哪个VC++版本?
错误需要很长时间才会出现,这表明它是内存损坏。其他一些函数写入了它没有掌握的位置。这段时间内它可以正常工作,但最终函数修改了一个MCF所需的指针,过了一段时间后,MFC访问该指针并通知您。
有时,“位置”可以被识别为数据,这样您就有了提示。例如,如果错误消息表示:
读取位置0x31323334时访问违规
您将其识别为ASCII字符串“1234”的一部分,这可能会引导您找到罪犯。

我正在使用Visual Studio 2005,并已将应用程序与解决方案附加以进行调试。 - Rakesh Agarwal

1
正如Patrick所说,很可能是你的代码给MFC提供了无效的值。一个猜测是你传入了错误的长度,因此库读取得太远了。但实际上有很多可能的原因。

用户编写的代码不仅可能会提供一些不正确结构的数据,而且例如可以调用一个在完成时调用回调函数并给它一个指向有效对象的userdata指针,但在回调到达时该对象已被删除。这里的可能性是无穷无尽的。 - sharptooth

1

这个崩溃是否可以清晰地再现?

如果是的话,使用日志文件!您应该使用日志文件并添加一些语句,仅记录传递的源文件/行号。从入口点(主事件处理程序)和最常见的执行路径开始添加几个语句。在崩溃后检查日志文件中的最后一个条目。然后沿着必须通过的路径/路径添加新条目等。通常,在进行几次迭代后,您将找到故障点。在长时间等待的情况下,日志文件可能会变得非常庞大,每次迭代都需要另外18小时。您可能需要添加一些旋转日志文件等技术。但是,使用此技术,我能够找到一些可比较的错误。

还有一些问题:

您的应用程序是否是多线程的?

它是否使用任何不由stl或可比容器管理的数组(例如,它是否使用C-Strings、C/C++-Arrays等)?


是的,该应用程序是多线程的。没有任何未由STL管理的数组。记录日志是一个好主意。 - Rakesh Agarwal
此外,当向日志文件追加内容时,我建议每次打开、写入、刷新和关闭日志文件,以便在发生崩溃时,日志文件能够正确关闭并包含最后一次打印的文本。 - KPexEA

0

这是可能导致内存泄漏的原因,有各种博客可以教你如何检查应用程序中的内存泄漏,你只需从Windows任务管理器中观察进程的物理内存,就可以找到某个阶段内存不断增加并耗尽内存。你也可以尝试使用windbg工具来识别代码中的内存泄漏。我没有使用过这个工具,只是提供一些提示。


0

尝试将调试器附加到进程上,并让调试器在访问违规时中断。

如果无法实现,则使用名为“用户模式进程转储程序”的工具,在发生访问违规的点创建进程的内存转储。您可以在此处找到该工具进行下载:

http://www.microsoft.com/downloads/details.aspx?FamilyID=E089CA41-6A87-40C8-BF69-28AC08570B7E&displaylang=en

如何运作:您可以在每个进程(或可选的系统范围)上配置规则,当它检测到一系列异常中的任何一个时,工具就会创建一个迷你转储或完整转储。其中之一是访问冲突。在完成转储后,应用程序会像往常一样继续运行(因此,如果访问违规未经处理,则会看到此对话框)。
请注意,您进程中的所有访问违例都会被捕获 - 即使稍后已处理了这些违例,也可以创建完整转储,但根据应用程序使用的内存量,创建完整转储可能需要一段时间(对于消耗100-200 MB私有内存的进程而言,需要10-20秒)。出于这个原因,可能将其启用到系统范围并不是一个好主意。
然后,您应该能够使用像WinDbg(http://www.microsoft.com/whdc/devtools/debugging/default.mspx)这样的工具来分析转储,以找出发生了什么 - 在大多数情况下,您只需要迷你转储,而不是完整转储(但是,如果您的应用程序不使用太多内存,那么拥有完整转储实际上没有太多缺点,除了转储大小和创建转储所需的时间)。

最后,需要警告的是,使用WinDbg调试访问冲突可能是一个相当复杂和繁琐的过程 - 如果您可以通过其他方式获取堆栈跟踪,则建议您首先尝试该方法。


0
这个问题很久以前就有了,我也遇到过同样的问题,但我很快解决了它——关键在于线程: 首先,注意只能在主线程更新GUI。
我的问题是我试图从工作线程(而不是主线程)处理GUI,然后出现同样的错误:0xC0000005。我通过发布消息(在主线程中执行)来解决它,并且问题得到了解决。
typedef enum {
  WM_UPDATE_GUI
}WM_MY_MSG

// register function callback to a message
BEGIN_MESSAGE_MAP(CMyDlg, CDlgBase)
  ON_MESSAGE(WM_UPDATE_GUI, OnUpdateGui)
END_MESSAGE_MAP()

// For this example - function that is not invoked in the Main Thread:
void CMyDlg::OnTimer() 
{ 
  CString str_to_GUI("send me to gui"); // send string to gui
  // Update_GUI(str_to_GUI); // crashed
  ::PostMessage(hWnd, MyMsg::WM_UPDATE_GUI, (WPARAM)&str_to_GUI, 0);
}

HRESULT CMyDlg::OnUpdateGui(WPARAM wParam, LPARAM lParam)
{
  CString str = *(CString*)wParam; // get the string from the posted message
  Update_GUI(str);
  return S_OK;
}

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