【问题标题】:OpenCV 3.0 + Visual Studio Memory Leak Detector "false" positiveOpenCV 3.0 + Visual Studio 内存泄漏检测器“假”阳性
【发布时间】:2015-12-27 00:37:16
【问题描述】:

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

它一直运行良好,我可以通过在我的应用程序上运行大量测试然后检查报告来轻松找到内存泄漏。

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

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

调试后我找到了“泄漏”的来源:

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 存在一组误报。我也不能运行自动内存泄漏测试,因为输出总是包含一些泄漏。

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

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

【问题讨论】:

  • 这不是误报,这是实际的内存泄漏。
  • @JamesMcNellis 我同意,但不幸的是它不会在 OpenCv 中得到修复。我报告了这个问题(github.com/Itseez/opencv/issues/5452),但它被标记为 wontfix(它是“静态初始化/销毁顺序失败”的解决方法)。任何想法如何处理这个问题?
  • 如果修改 OpenCv 是一个选项,那么您可以更改 getCoreTlsData() 函数以将块类型显式传递给调试运算符 new,例如new (_IGNORE_BLOCK, __FILE__, __LINE__) TlsData&lt;CoreTLSData&gt;()。或者(但更丑陋),您可以通过delete &amp;getCoreTlsData(); 在执行泄漏检查之前自己释放块。但这需要谨慎的时间安排:您必须确保 OpenCv 使用它之后执行此操作。
  • @JamesMcNellis 非常感谢,它不像您建议的那么简单,但它向我展示了一种暂停内存泄漏检查的方法。我在下面描述了我的解决方案。

标签: c++ visual-c++ memory-leaks opencv3.0


【解决方案1】:

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

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

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

最后使用 RAII 和几个宏定义,我创建了一个简单的类来管理这个功能。

我对官方 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 Debug 编译中启用。离开作用域后会自动启用内存泄漏跟踪(MEMORY_LEAKS_CHECKING_SUSPENDER 类的析构函数)。

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

  • getTLSContainerStorage()
  • void* TLSDataContainer::getData() const
  • TLSData&lt;CoreTLSData&gt;&amp; 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 变量使用情况。

【讨论】:

  • 我遇到了完全相同的问题,您的解决方案对我有用。我认为你可以接受你自己的答案。
  • AdamF,你用 OpenCV 3.2 试过了吗?我将它用作共享库 (DLL),即使我的项目中对 OpenCV 的唯一其他引用是创建 cv::Mat,也会出现内存泄漏。我尝试应用您的解决方案,但泄漏仍在继续,我不确定如何追踪它们。此外,没有 getTLSContainerStorage(),所以我将你的吊带添加到静态 TlsStorage &getTlsStorage() 中,我猜它取代了它。也没有内联 TLSStorage* TLSStorage::get(),所以我将吊杆放置在 TlsStorage 的 void* getData(size_t slotIdx) const 成员中。但是,还是会漏。 3.3 相同。
  • @DisplayName 我试图为 OpenCV 3.3 提供快速修复,但不幸的是,最近版本中此类泄漏的数量要大得多。您可以尝试修改库,但这不是正确的方法。在我看来,OpenCV 开发人员应该修复这些泄漏,或者至少增加在他们的代码中禁用 MSVC 泄漏检测器的可能性。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    制作一个特殊的函数,比如prefetchOpenCvMemoryLeaks(),它会创建/销毁一个小矩阵,创建/销毁一个小窗口,等等,这样OpenCV 的泄漏就会饱和。

    在外部main()函数中,用_CrtMemCheckpoint()保存堆状态,调用整个项目,最后再次保存堆状态并用_CrtMemDifference()与旧的比较。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-18
      • 2020-04-11
      • 2011-03-27
      • 2012-07-16
      • 2019-09-09
      • 1970-01-01
      • 2014-07-10
      相关资源
      最近更新 更多