为什么我的堆在删除后没有清除?

时间:2019-11-17 06:05:26

标签: c++

我正在编写一个程序,该程序需要一个包含指向其他包含框架数据的堆的指针的堆。但是我注意到,当我在不再需要堆之后清理堆时,它们仍然很忙。还附有一张内存消耗的图片。

这是RAM使用率上升的图像。

10分钟后MB。

30分钟后达到GB。

此后由于内存访问错误而导致崩溃。

    void Trap(vlByte **lpImageData, vlUInt uiCount)
    {
        if (lpImageData != 0)
        {
            for (vlUInt i = 0; i < uiCount; i++)
            {
                if (lpImageData[i] != 0)
                {
                    delete[]lpImageData[i];
                }
            }
            delete[]lpImageData;
        }
    }

清除函数,在失败和完成时调用。

代码本身

    vlByte** lpImageDataRGBA8888 = 0;

    lpImageDataRGBA8888 = new vlByte*[uiCount];
    memset(lpImageDataRGBA8888, 0, uiCount * sizeof(vlByte *));

    vlUInt uiImageSize = vlImageComputeImageSize(uiWidth, uiHeight, 1, 1, IMAGE_FORMAT_RGBA8888);
    for (vlUInt i = 0; i < uiCount; i++)
    {
        lpImageDataRGBA8888[i] = new vlByte[uiImageSize];

        if (!vlImageConvertToRGBA8888(vlImageGetData(iType == 1 ? i : 0, iType == 2 ? i : 0, iType == 3 ? i : 0, 0), lpImageDataRGBA8888[i], uiWidth, uiHeight, vlImageGetFormat()))
        {
            Print("Unable to convert %i frame/face/slice\n", 1 + i);
            Trap(lpImageDataRGBA8888, uiCount);
            return;
        }
    }

    ...

    if (!vlImageCreateMultiple(uiWidth, uiHeight, uiFrames, uiFaces, uiSlices, lpImageDataRGBA8888, &CreateOptions))
    {
        Print("Error Creation Multiple Image\n");
        Trap(lpImageDataRGBA8888, uiCount);
        return;
    }

    Trap(lpImageDataRGBA8888, uiCount);

可重复性最低

    #include <iostream>

    void DLLvlImageCreateMultiple(unsigned int uiCount, unsigned char** lpImageDataRGBA8888)// The dll code
    {
        unsigned char** lpNewImageDataRGBA8888 = 0;

        unsigned int uiImageSize = 256 * 256 * 4; // width * height * byte per pixel

        lpNewImageDataRGBA8888 = new unsigned char* [uiCount];
        memset(lpNewImageDataRGBA8888, 0, uiCount * sizeof(unsigned char*));

        for (unsigned int i = 0; i < uiCount; i++)
        {
            lpNewImageDataRGBA8888[i] = new unsigned char[uiImageSize];
        }

        lpImageDataRGBA8888 = lpNewImageDataRGBA8888;
    }

    void trap(unsigned char** lpImageData, unsigned int uiCount)
    {
        if (lpImageData != 0)
        {
            for (unsigned int i = 0; i < uiCount; i++)
            {
                if (lpImageData[i] != 0)
                {
                    delete[]lpImageData[i];
                }
            }
            delete[]lpImageData;
        }
    }


    void process()
    {
        unsigned char** lpImageDataRGBA8888 = 0;
        unsigned int uiCount = 1; // frames
        unsigned int uiImageSize = 1024 * 1024 * 4; // width * height * byte per pixel

        lpImageDataRGBA8888 = new unsigned char* [uiCount];
        memset(lpImageDataRGBA8888, 0, uiCount * sizeof(unsigned char*));
        for (unsigned int i = 0; i < uiCount; i++)
        {
            lpImageDataRGBA8888[i] = new unsigned char[uiImageSize];
        }

        DLLvlImageCreateMultiple(uiCount, lpImageDataRGBA8888);

        trap(lpImageDataRGBA8888, uiCount);
    }

    int main()
    {
        for (unsigned int i = 0; i < 10240; i++) {
            std::cout << i << "\n";
            process();
        }

        std::cin;
    }

2 个答案:

答案 0 :(得分:0)

因此,基本上,堆的工作方式是根据需要分配大的内存块,并为新对象提供请求的空间。通常,PAGE_SIZE为0x1000字节,这是堆管理器可以分配的最小最小块大小。例如,许多结构可能只使用该堆页面的0x40字节,因此,这将浪费大量资源,并导致其他主要问题,无论何时从堆中删除某些内容,都将释放分配的整个页面。当您从堆中释放某些内容时,堆管理器通常会将其标记为此类,以便其他分配可以使用该空间。取决于您的操作系统,释放时可能会选择零内存,但是您也可以自己执行。

包含一个可复制的小样本后,我在评论中提供了其他解释。

  

您要在DLLvlImageCreateMultiple中重新分配内存,并期望将lpImageDataRGBA8888设置为新分配。来自进程的原始分配被释放,但是第二个则没有。这意味着如果循环成功完成,则发生(256 * 256 * 4 * 10240) = 2,684,354,560 bytes的内存泄漏。

答案 1 :(得分:0)

DLLvlImageCreateMultiple中分配的所有内容都是内存泄漏。这就解释了为什么内存使用量一直在单调上升。

lpImageDataRGBA8888 = lpNewImageDataRGBA8888;

不会更改调用函数中变量的值。它只是更改函数局部变量的值。

您必须:

  1. 返回分配为函数返回值的内存

    unsigned char** DLLvlImageCreateMultiple(unsigned int uiCount)
    {
       ...
    
       return lpNewImageDataRGBA8888;
    }
    

  2. 通过将参数作为参考返回在输出参数中分配的内存。

    void DLLvlImageCreateMultiple(unsigned int uiCount,
                                  unsigned char**& lpImageDataRGBA8888)
    {
       ...
    }
    

无论选择哪个路径,都必须确保在更改指针的值之前释放在process中分配的内存。

void process()
{
   unsigned char** lpImageDataRGBA8888 = 0;
   unsigned int uiCount = 1; // frames
   unsigned int uiImageSize = 1024 * 1024 * 4; // width * height * byte per pixel

   lpImageDataRGBA8888 = new unsigned char* [uiCount];
   memset(lpImageDataRGBA8888, 0, uiCount * sizeof(unsigned char*));
   for (unsigned int i = 0; i < uiCount; i++)
   {
      lpImageDataRGBA8888[i] = new unsigned char[uiImageSize];
   }

   trap(lpImageDataRGBA8888, uiCount);

   DLLvlImageCreateMultiple(uiCount, lpImageDataRGBA8888);

   trap(lpImageDataRGBA8888, uiCount);
}

否则,您将泄漏process中分配的内存。