OpenCV 3.0 + Visual Studio 内存泄漏检测器 "false" 肯定

OpenCV 3.0 + Visual Studio Memory Leak Detector "false" positive

我想创建启用了 Visual Studio 内存泄漏检测器 (Memory Leak Detector)

的项目

它总是运行良好,我可以通过 运行对我的应用程序进行一系列测试然后检查报告来轻松找到内存泄漏。

但是在将 OpenCV 3.0 静态链接到我的项目后,我得到了一些误报。

例如,最令人沮丧的错误来自 StereoBMImpl::compute 方法和调用:ocl::useOpenCL()

调试后我找到了"leak"的来源:

TLSData<CoreTLSData>& getCoreTlsData()
{
    static TLSData<CoreTLSData> *value = new TLSData<CoreTLSData>();
    return *value;
}

分析这段代码后,我们知道静态对象只分配了一次,一切都应该没问题。但是现在我有一堆误报的内存泄漏报告,例如:

{1370349} normal block at 0x0E74D560, 24 bytes long.
 Data: <                > FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 
{1370348} normal block at 0x0E74D4E0, 64 bytes long.
 Data: <` t             > 60 D5 74 0E CD CD CD CD CD CD CD CD CD CD CD CD 

现在很难在我的应用程序中找到一些真正的内存泄漏,因为 OpenCV 存在一组误报。我也不能 运行 自动内存泄漏测试,因为输出总是包含一些泄漏。

有什么方法可以消除这些 "pseudo" 错误(如果可能的话,无需更改 OpenCV 源代码)?很烦人。

我想其他内存泄漏检测器也会报告一些类似的伪泄漏,因为 new 运算符在没有 delete 的情况下执行(对象由 OS 自动清理)。

我用一种很肮脏的方式解决了这个问题,但我没有找到更好的方法。该解决方案需要修改一个 OpenCV 文件 (system.cpp)。如果您找到更好的修复方法,请发表评论。我想更多的人可能有类似的问题。

首先,我尝试使用@JamesMcNellis 解决方案(来自上面的评论)来解决问题,方法是将块显式标记为 _IGNORE_BLOCK: new (_IGNORE_BLOCK, __FILE__, __LINE__)。解决这个问题真的是一个很好的开始。不幸的是,泄漏 class 包含成员,例如std::vector,因此跟踪来自该向量的分配没有被暂停。

我开始阅读 MSDN 文档以了解 crtdbg.h 中的函数,我找到了一种暂停内存泄漏检查一段时间的方法。可以通过使用函数 _CrtSetDbgFlag 清除标志 '_CRTDBG_ALLOC_MEM_DF' 来实现。通过 MSDN 上的示例查看详细信息:_CrtSetDbgFlag documentation。 这个解决方案可能有一个缺点(我知道的一个),它会暂停所有线程的内存泄漏检查。

最后使用 RAII 和一些宏定义我创建了简单的 class 来管理此功能。

我对官方 3.0 源代码应用的所有更改。

来自 OpenCV 的 system.cpp 文件顶部的某处(在 precomp.hpp 包含之后)我添加了简单的机器:

#if defined(_MSC_VER) && defined(_DEBUG)
    #include <crtdbg.h>
    class MEMORY_LEAKS_CHECKING_SUSPENDER
    {
    public:
        MEMORY_LEAKS_CHECKING_SUSPENDER()
        {
            value = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
            int new_flag = value & (~_CRTDBG_ALLOC_MEM_DF);
            _CrtSetDbgFlag(new_flag);
        }

        ~MEMORY_LEAKS_CHECKING_SUSPENDER()
        {
            _CrtSetDbgFlag(value);
        }

    private:
        int value;
    };

    #define SUSPEND_MEMORY_LEAKS_CHECKING MEMORY_LEAKS_CHECKING_SUSPENDER suspend_memory_leaks_checking
#else
    #define SUSPEND_MEMORY_LEAKS_CHECKING
#endif

每次我想暂停内存泄漏检查我必须添加:PAUSE_MEMORY_LEAKS_CHECKING;它只在Visual Studio调试编译中启用。离开范围后会自动启用内存泄漏跟踪(MEMORY_LEAKS_CHECKING_SUSPENDER class 的析构函数)。

目前为了暂停我的 OpenCV 内存泄漏,我在函数中添加了暂停分配:

  • getTLSContainerStorage()
  • void* TLSDataContainer::getData() const
  • TLSData<CoreTLSData>& getCoreTlsData()
  • inline TLSStorage* TLSStorage::get()

(TLSStorage 中的最后一次暂停可能已修复在主 OpenCV 存储库中 - 我简要检查了存储库)

每次修改都非常简单(例如第一次泄漏):

修改前:

static TLSContainerStorage& getTLSContainerStorage()
{
    static TLSContainerStorage *tlsContainerStorage = new TLSContainerStorage();
    return *tlsContainerStorage;
}

修改后:

static TLSContainerStorage& getTLSContainerStorage()
{
    SUSPEND_MEMORY_LEAKS_CHECKING;
    static TLSContainerStorage *tlsContainerStorage = new TLSContainerStorage();
    return *tlsContainerStorage;
}

如果您修改了所有这些语句并且仍然观察到内存泄漏并且您使用 OpenCV 作为单独加载的 dll,那么请确保您使用 FreeLibrary 函数正确卸载了这个 dll。由于检查 OpenCV system.cpp 文件中的 DLLMain 函数和 cv::__termination 变量使用的原因。

好的,我有一个替代的解决方法:

做一个特殊的函数,比如说,prefetchOpenCvMemoryLeaks(),creates/destroys一个小矩阵,creates/destroys一个小window,等等,这样OpenCV 泄漏饱和。

在外面的main()函数中,用_CrtMemCheckpoint()保存堆状态,调用整个项目,最后再用_CrtMemDifference()保存堆状态和旧的比较.

这样您就可以检查自己的内存泄漏,而不管 OpenCV 个内存泄漏。

我在我的项目中遇到了同样的问题:静态构建 - MFC 和 OpenCV。这些解决方案对我没有帮助。我用 OpenCV 版本测试了它们:3.4.3 和 4.0.1。当所有 opencv 函数都放入它们自己的 dll 中时,问题就出现了。所以我的项目配置可以是这样的:MFC build - static, OpenCV - dynamic