65.9K
CodeProject 正在变化。 阅读更多。
Home

C/C++ 中的内存分配:如何避免 Intel® Inspector XE for Visual Studio 2015 检测到的内存分配/释放不匹配问题

starIconstarIconstarIconstarIconstarIcon

5.00/5 (10投票s)

2017 年 4 月 30 日

CPOL

3分钟阅读

viewsIcon

18550

downloadIcon

106

本技巧/窍门介绍了如何避免 Intel® Inspector XE for Visual Studio 2015 检测到的内存分配/释放不匹配问题的基本思路。

引言

在使用 Visual Studio 2015 中的Intel® Inspector XE 检测 C/C++ 代码中的内存泄漏和堆栈操作问题时,您可能会遇到这种情况:即使您在每次分配后都正确地释放了每个缓冲区,Intel® Inspector XE 仍然检测到分配/释放不匹配的问题。在本文中,我们将讨论如何正确释放分配的内存缓冲区,以规避 Intel® Inspector XE 检测到的内存分配/释放不匹配问题。

内存分配/释放不匹配问题

假设您的代码执行以下操作:分配一块内存来存储 N 个特定类型 DATA 的对象的数组,并将内存缓冲区的地址分配给指针变量 data

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>

typedef struct tagData
{
 char* string;
} DATA, NEAR *PDATA, FAR* LPDATA;

const size_t N = 10;

int main()
{
 int count = 10;
 while (--count >= 0)
 {
  LPDATA data = (LPDATA)malloc(sizeof(DATA) * N);
  memset((void*)&data[0], 0x00, sizeof(DATA) * N);

  const size_t length = 1024;
  for (size_t index = 0; index < N; index++)
  {
   data[index].string = (char*)malloc(length);
   sprintf(data[index].string, "string: %zu", index);
  }

  // Do some work at here

  for (size_t index = 0; index < N; index++)
   free(data[index].string);

  free(data);
 }

    return 0;
}

这通常通过调用 C 内存分配函数(如 callocmallocrealloc)以及 C++ 中的 new[]delete[] 运算符来完成。然后,我们遍历对象数组 data,并为每个对象,我们使用这些内存分配函数分配进程内存来存储大小为 length 的字符缓冲区字符串。在这种情况下,我们正在将分配的缓冲区地址迭代地分配给每个特定对象 data[index] 的指针变量 string

由于这些内存缓冲区已被分配,我们可以使用这些分配的缓冲区执行一些工作。最后,根据最佳编程实践,为了避免内存泄漏,我们需要在代码执行结束时明智地释放特定的缓冲区。为此,我们遍历对象数组,并为每个特定对象,通过使用 C 函数 free(data[index].string) 或 C++ 中的 delete[] data[index].string 运算符来释放用于存储字符串缓冲区的内存。之后,我们使用相同的 free(data) 函数释放用于存储对象数组 data 的缓冲区。

通常,通过执行上面列出的代码,我们会看到它显然可以成功执行并提供正确的结果,并且在其执行结束时没有内存泄漏或其他问题。然而,通过使用Intel® Inspector XE 来检测和分析代码执行期间可能存在的内存泄漏或其他问题,我们可能会遇到以下代码突然出现一系列分配/释放不匹配的问题。我们特意重复内存分配/释放过程以在 CPU 上创建工作负载,以便Intel® Inspector XE 可以轻松检测到以下问题。这些问题主要发生在 Windows 内核下运行以下代码时。

导致此代码可能遇到这些问题并因此被Intel® Inspector XE 检测到的主要原因是,当在 Windows 内核下运行以下代码时,调用 free(data[index].string) 函数或 free(data) 实际上并没有释放缓冲区。调用这些函数相当于将特定指针变量(如 data = nullptr)设置为 null 指针。反过来,在代码执行开始时预先分配的内存由 Windows 内核自动重新分配给其他正在运行的进程。这实际上是Intel® Inspector 突然检测到本文讨论的这些问题的原因。

解决方法

作为讨论问题的变通方法,我们必须使用 ::VirtualAllocEx(...)::VirtualFreeEx(...) WinAPI 函数来正确分配/释放内存缓冲区。另外,为了确保我们实际释放了之前分配的内存,我们必须检查分配给指针变量的地址值是否不等于 nullptr。如果是,则执行释放,否则不做任何操作。内存缓冲区的分配/释放通常如下面的代码示例所示。

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <memory.h>

#include <Windows.h>

typedef struct tagData
{
 char* string;
} DATA, NEAR *PDATA, FAR* LPDATA;

const size_t N = 10;

int main()
{
 int count = 10;
 while (--count >= 0)
 {
  HANDLE hProcHandle = ::GetCurrentProcess();

  LPDATA data = (LPDATA)::VirtualAllocEx(hProcHandle, NULL, \
   sizeof(DATA) * N, MEM_COMMIT, PAGE_READWRITE);
  ::ZeroMemory((void*)&data[0], sizeof(DATA) * N);

  const size_t length = 1024;
  for (size_t index = 0; index < N; index++)
  {
   data[index].string = (char*)::VirtualAllocEx(hProcHandle, NULL, \
    length, MEM_COMMIT, PAGE_READWRITE);
   sprintf(data[index].string, "string: %zu", index);
  }

  // Do some work here

  for (size_t index = 0; index < N; index++)
   if (data[index].string != NULL)
    ::VirtualFreeEx(hProcHandle, data[index].string, length, MEM_RELEASE);

  if (data != NULL)
   ::VirtualFreeEx(hProcHandle, data, sizeof(DATA) * N, MEM_RELEASE);
 }

    return 0;
}

在这种情况下,与使用 malloc(…)free(…) 函数不同,::VirtualAllocEx(...)::VirtualFreeEx(…) WinAPI 函数在 Windows 内核虚拟内存地址空间中分配/释放特定缓冲区。因此,Intel® Inspector XE 不再检测到任何问题,如内存分配/释放不匹配。有关执行虚拟内存管理的这些函数的详细描述,请在此处 查找

历史

  • 2017 年 5 月 1 日- 本技巧/窍门的第一个修订版已发布。
© . All rights reserved.