OpenCV 3.0 + детектор утечки памяти Visual Studio ложное срабатывание

Я хочу создать проект с включенным детектором утечки памяти Visual Studio (Детектор утечки памяти)

Он всегда работал нормально, и я мог легко найти утечки памяти, выполнив несколько тестов в своем приложении, а затем проверив отчет.

Но после статической привязки 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 (объект автоматически очищается ОС).


person AdamF    schedule 29.09.2015    source источник
comment
Это не ложное срабатывание, это настоящая утечка памяти.   -  person James McNellis    schedule 01.10.2015
comment
@JamesMcNellis Я согласен, но, к сожалению, это не будет исправлено в OpenCv. Я сообщил об этой проблеме (github.com/Itseez/opencv/issues/5452), но он был помечен как wontfix (это обходной путь для фиаско статического порядка инициализации / удаления). Есть идеи, как решить эту проблему?   -  person AdamF    schedule 05.10.2015
comment
Если можно изменить OpenCv, вы можете изменить функцию getCoreTlsData(), чтобы явно передать тип блока оператору отладки new, например new (_IGNORE_BLOCK, __FILE__, __LINE__) TlsData<CoreTLSData>(). В качестве альтернативы (но еще хуже) вы можете освободить блок самостоятельно, прежде чем выполнять проверку на утечку, через delete &getCoreTlsData();. Но это потребует тщательного выбора времени: вам нужно будет убедиться, что вы сделали это после OpenCv, используя его.   -  person James McNellis    schedule 05.10.2015
comment
@JamesMcNellis большое спасибо, это было не так просто, как вы предлагали, но он показал мне способ приостановить проверку утечек памяти. Свое решение я описал ниже.   -  person AdamF    schedule 06.10.2015


Ответы (3)


Решил проблему довольно грязно, но лучше не нашел. Решение требует изменения одного файла OpenCV (system.cpp). Если вы нашли способ исправить это лучше, оставьте, пожалуйста, комментарий. Я полагаю, что подобные проблемы могут быть у большего количества людей.

Сначала я попытался решить проблему с помощью решения @JamesMcNellis (из комментария выше), явно пометив блок как _IGNORE_BLOCK: new (_IGNORE_BLOCK, __FILE__, __LINE__). Это было действительно хорошее начало для решения этой проблемы. К сожалению, класс утечки содержит такие члены, как, например, std::vector, поэтому отслеживание выделений из этого вектора не было приостановлено.

Я начал читать документацию MSDN к функциям из crtdbg.h и нашел способ на время приостановить проверку утечек памяти. Это возможно, сняв флаг '_CRTDBG_ALLOC_MEM_DF' с помощью функции: _CrtSetDbgFlag. Подробности см. В примере в MSDN: _CrtSetDbgFlag документация. У этого решения, вероятно, есть один недостаток (который я знаю): оно приостанавливает проверку утечек памяти для всех потоков.

Наконец, используя RAII и несколько определений макросов, я создал простой класс для управления этой функциональностью.

Все изменения я применил к официальным исходным кодам 3.0.

Где-то наверху (после включения precomp.hpp) system.cpp файла из OpenCV я добавил простую технику:

#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<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, убедитесь, что вы правильно выгрузили эту dll с помощью функции FreeLibrary. По этой причине проверьте функцию DLLMain в system.cpp файле OpenCV и использование cv::__termination переменной.

person AdamF    schedule 06.10.2015
comment
У меня была точно такая же проблема, и ваше решение мне подходит. Думаю, вы могли бы принять свой собственный ответ. - person rold2007; 04.02.2016
comment
AdamF, вы пробовали это с OpenCV 3.2? Я использую его как общую библиотеку (DLL), и у меня возникают утечки памяти, даже если единственной другой ссылкой на OpenCV в моем проекте является создание cv :: Mat. Я попытался применить ваше решение, но утечки продолжаются, и я не знаю, как их отследить. Кроме того, нет getTLSContainerStorage (), поэтому я добавил вашу подвеску в статические TlsStorage и getTlsStorage (), которые, как я предполагаю, заменили ее. Также нет встроенного TLSStorage * TLSStorage :: get (), поэтому я поместил подтяжку в константный член void * getData (size_t slotIdx) TlsStorage. Но все же утечки. То же, что и в 3.3. - person Display Name; 24.07.2017
comment
@DisplayName Я попытался быстро исправить OpenCV 3.3, но, к сожалению, количество таких утечек в последней версии намного больше. Вы можете попробовать изменить библиотеку, но это неправильный способ. На мой взгляд, разработчики OpenCV должны исправить эти утечки или хотя бы добавить возможность отключения детектора утечек MSVC в свой код. - person AdamF; 29.07.2017

У меня была такая же проблема в моем проекте: статическая сборка - MFC и OpenCV. Эти решения мне не помогли. Я тестировал их с версиями OpenCV: 3.4.3 и 4.0.1. Проблема исчезла, когда все функции opencv были в своих собственных dll. Итак, моя конфигурация проекта может быть следующей: сборка MFC - статическая, OpenCV - динамическая.

person victor kulichkin    schedule 08.03.2019

Хорошо, у меня есть альтернативный обходной путь:

Создайте специальную функцию, скажем, prefetchOpenCvMemoryLeaks(), которая создает / уничтожает небольшую матрицу, создает / уничтожает маленькое окно и так далее, чтобы OpenCV утечки были насыщены.

Во внешней функции main() сохраните состояние кучи с помощью _CrtMemCheckpoint(), вызовите весь проект, в конце снова сохраните состояние кучи и сравните его со старым с помощью _CrtMemDifference().

Таким образом, вы можете проверить свои собственные утечки памяти независимо от OpenCV утечек.

person Hexagonal    schedule 12.03.2018