VS2012编译器奇怪的内存释放问题 [英] VS2012 compiler Strange memory deallocation issues

查看:381
本文介绍了VS2012编译器奇怪的内存释放问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在VS2012编译器中遇到了一个奇怪的问题,该问题似乎未在GCC中显示.重新分配过程最终需要几分钟而不是几秒钟.有人对此有任何意见吗?分步调试显示对RtlpCollectFreeBlocks()的调用明显挂起.我在调试和发布模式下都遇到此问题.我正在运行32位Windows 7,但在64位7上也遇到同样的问题.

I'm having a strange problem with the VS2012 compiler that doesn't seem to show up in GCC. The deallocation process ends up taking minutes rather than seconds. Does anyone have any input on this? Step debugging shows a noticeable hang at calls to RtlpCollectFreeBlocks(). I have this problem in both debug and release mode. I'm running Windows 7 32-bit, but I have the same problem on 64-bit 7.

#include "stdafx.h"
#include <iostream>
#include <stdint.h>
#include <cstdlib>

#define SIZE 500000

using namespace std;

typedef struct
{
    uint32_t* thing1;
}collection;

/*
 * VS2012 compiler used.
 * Scenarios: 
 *  1) Don't allocate thing1. Program runs poorly.
 *  2) Allocate thing1 but don't delete it. Program runs awesome.
 *  3) Allocate thing1 and delete it. Program runs poorly.
 * 
 * Debug or Release mode does not affect outcome. GCC's compiler is fine.
 */
int _tmain(int argc, _TCHAR* argv[])
{
    collection ** colArray = new collection*[SIZE];

    for(int i=0;i<SIZE;i++)
    {
        collection * mine = new collection;
        mine->thing1 = new uint32_t; // Allocating without freeing runs fine. Either A) don't allocate or B) allocate and delete to make it run slow.
        colArray[i] = mine;
    }

    cout<<"Done with assignment\n";

    for(int i=0;i<SIZE;i++)
    {
        delete(colArray[i]->thing1); // delete makes it run poorly.
        delete(colArray[i]);

        if(i > 0 && i%100000 == 0)
        {
            cout<<"100 thousand deleted\n";
        }
    }
    delete [] colArray;

    cout << "Done!\n";
    int x;
    cin>>x;
}

推荐答案

您看到的性能下降来自Windows调试堆功能,而且即使在发行版中,它也无法实现自身功能.

The performance hit you're seeing is coming from Windows debug heap functionality, and its a little stealthy in how it enables itself, even in release builds.

我自由地构建了一个简单程序的64位调试映像,并发现了这一点:

I took the liberty of build a 64bit debug image of a simpler program and came to discover this:

  • msvcr110d.dll!_CrtIsValidHeapPointer(const void * pUserData = 0x0000000001a8b540)
  • msvcr110d.dll!_free_dbg_nolock(void * pUserData = 0x0000000001a8b540,int nBlockUse = 1)
  • msvcr110d.dll!_free_dbg(void * pUserData = 0x0000000001a8b540,int nBlockUse = 1)
  • msvcr110d.dll!运算符删除(无效* pUserData = 0x0000000001a8b540)

我特别感兴趣的是msvcr110d.dll!_CrtIsValidHeapPointer的主体,事实是:

Of particular interest to me was the body of msvcr110d.dll!_CrtIsValidHeapPointer which it turns out is this:

if (!pUserData)
    return FALSE;

// Note: all this does is checks for null    
if (!_CrtIsValidPointer(pHdr(pUserData), sizeof(_CrtMemBlockHeader), FALSE))
    return FALSE;

// but this is e-x-p-e-n-s-i-v-e
return HeapValidate( _crtheap, 0, pHdr(pUserData) );

HeapValidate() 呼叫是残酷的.

That HeapValidate() call is brutal.

好吧,也许我会在调试版本中期望这一点.但肯定不会释放.事实证明,这种情况会更好,但请看一下调用堆栈:

Ok, maybe I would expect this in a debug build. but certainly not release. As it turns out, that gets better, but look at the call stack:

  • ntdll.dll!RtlDebugFreeHeap()
  • ntdll.dll!string启用堆调试选项\ n"()
  • ntdll.dll!RtlFreeHeap()
  • kernel32.dll!HeapFree()
  • msvcr110.dll!free(无效* pBlock)

这很有趣,因为当我先运行此代码,然后使用IDE(或WinDbg)将其附加到正在运行的进程而又不允许它控制执行启动环境时,此调用堆栈在ntdll.dll!RtlFreeHeap()处停止.换句话说,在IDE RtlDebugFreeHeap外部运行不会被调用.但是为什么?

This is interesting, because when I ran this first, then attach to the running process with the IDE (or WinDbg) without allowing it to control the execution startup environment, this callstack stops at ntdll.dll!RtlFreeHeap(). In other words, running outside the IDE RtlDebugFreeHeap is not invoked. But why??

我心想,不知何故调试器正在翻转开关以启用堆调试.经过一番挖掘之后,我发现开关"就是调试器本身.如果正在运行的进程是由调试器生成的,则Windows使用特殊的调试堆函数(RtlDebugAllocHeapRtlDebugFreeHeap). 在WinDbg上MSDN上的该手册页有关在Windows下进行调试的有趣提示:

I thought to myself, somehow the debugger is flipping a switch to enable heap debugging. After doing some digging I came to find that "switch" is the debugger itself. Windows uses the special debug heap functions (RtlDebugAllocHeap and RtlDebugFreeHeap) if the process being run is spawned by a debugger. This man-page from MSDN on WinDbg eludes to this, along with other interesting tidbits about debugging under Windows:

来自使用WinDbg调试用户模式进程

调试器创建的进程(也称为衍生进程)的行为与调试器未创建的进程稍有不同.

Processes that the debugger creates (also known as spawned processes) behave slightly differently than processes that the debugger does not create.

调试器创建的进程不使用标准的堆API,而是使用特殊的调试堆.您可以通过使用_NO_DEBUG_HEAP环境变量或-hd命令行选项来强制生成的进程使用标准堆而不是调试堆.

Instead of using the standard heap API, processes that the debugger creates use a special debug heap. You can force a spawned process to use the standard heap instead of the debug heap by using the _NO_DEBUG_HEAP environment variable or the -hd command-line option.

现在我们要去某个地方.为了测试这一点,我只用适当的时间就放置了sleep()来连接调试器,而不是使用它生成进程,然后让它以其愉快的方式运行.当然,如前所述,它可以全速前进.

Now we're getting somewhere. To test this out I simply dropped a sleep() with an appropriate amount of time for me to attach the debugger rather than spawn the process with it, then let it run on its merry way. Sure enough, as mentioned previously, it sailed full-speed-ahead.

基于该文章的内容,我自由地更新了我的发布模式版本,以在我的项目文件的执行环境设置中定义_NO_DEBUG_HEAP=1.我显然仍然对调试版本中的细粒度堆活动感兴趣,因此这些配置保持原样.完成此操作后,我在VS2012(和VS2010)下运行的发行版的总体速度快,我也邀请您尝试.

Based on the content of that article, I have taken liberty to update my Release-mode builds to define _NO_DEBUG_HEAP=1 in their execution environment settings of my project files. I'm obviously still interested in granular heap-activity in debug builds, so those configurations stayed as-is. After doing this, the overall speed of my release builds running under VS2012 (and VS2010) were substantially faster, and I invite you to try as well.

这篇关于VS2012编译器奇怪的内存释放问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆