С++ повреждение кучи

У меня есть простая синхронизированная очередь

 template <typename T>

    class SynchronisedQueue
    {
    public:

        void Enqueue(const T& data)
        {
            boost::unique_lock<boost::mutex> lock(queueMutex);
            dataQueue.push(data);
            conditionVariable.notify_one();
        } 

        T Dequeue()
        {
            boost::unique_lock<boost::mutex> lock(queueMutex);

            while (dataQueue.size()==0) 
            {
                conditionVariable.wait(lock);
            }

            T result=dataQueue.front(); dataQueue.pop();
            return result;
        } 


    private:

    std::queue<T> dataQueue;                        // Use STL queue to store data
boost::mutex queueMutex;                        // The mutex to synchronise on
boost::condition_variable conditionVariable;    // The condition to wait for
};

Когда я удаляю очередь из очереди, я иногда получаю повреждение кучи....

HEAP: блок Free Heap ccb1080 изменен на ccb13c0 после его освобождения

стек вызовов:

ntdll.dll!76fa5654()    
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll] 
ntdll.dll!76f6a554()    
ntdll.dll!76f35a70()    
ntdll.dll!76fa5eff()    
ntdll.dll!76f6a3ba()    
ntdll.dll!76f35a70()    
msvcr90d.dll!_heap_alloc_base(unsigned int size=1222)  Line 105 + 0x28 bytes    C
msvcr90d.dll!_heap_alloc_dbg_impl(unsigned int nSize=1186, int nBlockUse=1, const char * szFileName=0x00000000, int nLine=0, int * errno_tmp=0x1310ee18)  Line 427 + 0x9 bytes  C++
msvcr90d.dll!_nh_malloc_dbg_impl(unsigned int nSize=1186, int nhFlag=0, int nBlockUse=1, const char * szFileName=0x00000000, int nLine=0, int * errno_tmp=0x1310ee18)  Line 239 + 0x19 bytes    C++
msvcr90d.dll!_nh_malloc_dbg(unsigned int nSize=1186, int nhFlag=0, int nBlockUse=1, const char * szFileName=0x00000000, int nLine=0)  Line 296 + 0x1d bytes C++
msvcr90d.dll!malloc(unsigned int nSize=1186)  Line 56 + 0x15 bytes  C++
msvcr90d.dll!operator new(unsigned int size=1186)  Line 59 + 0x9 bytes  C++
x.ax!std::_Allocate<unsigned char>(unsigned int _Count=1186, unsigned char * __formal=0x00000000)  Line 43 + 0x9 bytes  C++
ax.ax!std::allocator<unsigned char>::allocate(unsigned int _Count=1186)  Line 145 + 0xb bytes   C++
ax.ax!std::vector<unsigned char,std::allocator<unsigned char> >::_Buy(unsigned int _Capacity=1186)  Line 1110 + 0xf bytes   C++
ax.ax!std::vector<unsigned char,std::allocator<unsigned char> >::vector<unsigned char,std::allocator<unsigned char> >(const std::vector<unsigned char,std::allocator<unsigned char> > & _Right=[1186](83 'S',50 '2',54 '6',67 'C',162 '¢',4 '',0,0,108 'l',0,0,0,2 '',64 '@',0,0,6 '',14 '',64 '@',0,35 '#',2 '',147 '“',76 'L',114 'r',53 '5',0,0,54 '6',79 'O',78 'N',4 '',0,0,0,0,54 '6',79 'O',78 'N',4 '',0,0,0,0,255 'ÿ',255 'ÿ',255 'ÿ',255 'ÿ',255 'ÿ',255 'ÿ',255 'ÿ',255 'ÿ',106 'j',4 '',0,0,0,0,0,1 '',65 'A',154 'š',3 '',1 '',176 '°',159 'Ÿ',255 'ÿ',240 'ğ',199 'Ç',...))  Line 501 + 0x11 bytes    C++
ax.ax!SynchronisedQueue<std::vector<unsigned char,std::allocator<unsigned char> > >::Dequeue()  Line 32 + 0xc bytes C++
ax.ax!PPin::FillBuffer(IMediaSample * pSample=0x0cadbea8)  Line 225 + 0x12 bytes    C++
ax.ax!PPin::DoBufferProcessingLoop()  Line 300 + 0x13 bytes C++
ax.ax!CSourceStream::ThreadProc()  + 0x13e bytes    
ax.ax!CAMThread::InitialThreadProc()  + 0x51 bytes  
kernel32.dll!753bed6c()     
ntdll.dll!76f4377b()    
ntdll.dll!76f4374e() 

Что может вызвать это повреждение кучи? Как отлаживать ошибки повреждения кучи? Любые идеи...

ОБНОВЛЕНИЕ: пример использования

// Enqueue
void GetVideoStreams( BYTE *pData)
{
 std::vector<BYTE> vecFrame(pData, pData + nLen/sizeof(pData[0]));
 IncomingFramesQueue.Enqueue(vecFrame);

}

//Dequeue
void ConsumeVideoStreams()
{

 vector<BYTE> data = IncomingFramesQueue.Dequeue();

}

person Novalis    schedule 15.08.2012    source источник
comment
Отладочная куча действительно хороша тем, что работает медленно и, таким образом, маскирует ошибки гонки потоков. Я предлагаю вам перейти в режим Release и просто просмотреть программу в стиле div-2, используя HeapValidate(GetProcessHeap(),0,0), чтобы локализовать проблему синхронизации. Я имею в виду проверку кучи непосредственно перед и сразу после строк или вызовов, в которых возникают ошибки, и перемещение ее до тех пор, пока не станет ясно, когда происходит повреждение кучи. Однако не забывайте использовать /MD при компиляции.   -  person ActiveTrayPrntrTagDataStrDrvr    schedule 15.08.2012
comment
какой тип очереди данных?   -  person Tobias Langner    schedule 15.08.2012
comment
Вы встроены в режим отладки, не так ли? Схема хранения STL различается между ними, поэтому связывание режима без отладки с библиотеками режима отладки будет иметь успех!   -  person marko    schedule 15.08.2012
comment
тип данных: SynchronizedQueue‹std::vector‹BYTE›› MyIncomingFramesQueue; так что в моем случае это вектор Тобиаса   -  person Novalis    schedule 15.08.2012
comment
Марко, ты имеешь в виду, что я должен протестировать его в режиме релиза, поскольку естественно получать эти ошибки в режиме отладки?   -  person Novalis    schedule 15.08.2012
comment
@novalis Я имею в виду, что вы должны убедиться, что ваш код собран с теми же параметрами компилятора, что и библиотека, особенно в отношении того, является ли это сборкой отладки или выпуском.   -  person marko    schedule 15.08.2012
comment
Я не думаю, что это связано, но согласно документации boost (boost.org/doc/libs/1_50_0/doc/html/thread/) мьютекс не должен блокироваться при вызове notify_one()   -  person Asaf    schedule 15.08.2012
comment
@Novalis Нет, я имел в виду внутренний тип вашей синхронизированной очереди, dataQueue. Вы мне показали T - но это не тот тип dataQueue или я ошибаюсь?   -  person Tobias Langner    schedule 15.08.2012
comment
@Tobias Langner Это очередь, которую вы можете вставить в тип vector‹BYTE›… например, SynchronisedQueue‹std::vector‹BYTE›› IncomingFramesQueue;   -  person Novalis    schedule 15.08.2012
comment
@Novalis Скорее всего, повреждение кучи происходит из-за pop(), front() или push(). Но без сематики и кода очереди невозможно ответить на ваш вопрос.   -  person Tobias Langner    schedule 15.08.2012
comment
@Tobias Langner Ну, я обновляю вопрос ... введите его простой код использования   -  person Novalis    schedule 15.08.2012
comment
Примечание. Ваша функция Dequeue не защищена от исключений.   -  person Stephan Dollberg    schedule 15.08.2012
comment
это однопоточный и dataQueue.front(); никогда не звонил, если очередь не пуста... Но спасибо за качественную проблему   -  person Novalis    schedule 15.08.2012
comment
@Novalis, вы все еще не понимаете - что такое код pop(), push(), front(), size(). Или dataQueue — это std::queue? Просто скажите нам, что такое тип dataQueue (он находится в приватном разделе вашего класса SynchronisedQueue.   -  person Tobias Langner    schedule 15.08.2012
comment
@Tobias Langner Они стандартные std::queue‹T› dataQueue pop push front… ничего особенного… Я обновлю эту часть…   -  person Novalis    schedule 15.08.2012
comment
Всегда ли повреждение кучи находится в одном и том же месте кода или это движущаяся цель?   -  person Tobias Langner    schedule 15.08.2012
comment
большую часть времени он показывает одно и то же местоположение ... но иногда очень разные местоположения ... но это произошло после дека ....   -  person Novalis    schedule 15.08.2012
comment
dequeue может вводить в заблуждение. Поскольку dequeue каждый раз выделяет много места (и, вероятно, вызывается очень часто), оттуда вызывается код проверки канареек кучи. Но реальная перезапись в куче могла произойти везде. Поскольку вы копируете все при использовании своей синхронизированной очереди, я не вижу ошибки памяти в опубликованном вами коде.   -  person Tobias Langner    schedule 15.08.2012


Ответы (3)


Как указано выше, ваш код и опубликованная вами трассировка стека показывают, что некоторое выделение памяти продолжается. Вместе с сообщением об ошибке я предполагаю, что повреждение кучи произошло где-то еще и просто появляется каждый раз при удалении из очереди, поскольку для копирования этих векторов используется много памяти.

Используйте ознакомительную версию какого-либо инструмента для профилирования памяти (например, http://www.softwareverify.com/cpp-memory.php), чтобы найти ошибку. На рынке также есть несколько бесплатных инструментов (в первую очередь Valgrind), но я не знаю ни одного хорошего бесплатного инструмента для Visual C++.

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

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


Я только что нашел бесплатный инструмент, который, возможно, стоит попробовать: Dr. Память. Я получил ссылку из stackoverflow, конечно.

person Tobias Langner    schedule 15.08.2012

Да,. скорее всего, вы смешиваете версии Release и Debug библиотеки времени выполнения.

В Windows (только AFAIK) есть 2 разные библиотеки времени выполнения, версия выпуска выполняет выделение памяти, как и следовало ожидать, но отладочная версия добавляет защитные блоки вокруг всех распределений, чтобы она могла отслеживать переполнения буфера и другие ошибки памяти. Проблема с этой системой заключается в том, что если вы смешаете библиотеку Release и Debug в одном и том же приложении, вы выделите, скажем, 10 байтов в Release, а затем освободите 18 байтов (4+10+4) в Debug.

Хитрость заключается в том, чтобы каждый раз создавать и связывать либо все Debug, либо все Release. В качестве альтернативы, если вы должны использовать Release lib в отладочной сборке, вы должны убедиться, что все выделения, сделанные в dll, также освобождаются той же библиотекой — не позволяйте процедурам создания/удаления CRT выполнять использоваться, оберните их в функцию. это гарантирует, что 10 байтов, выделенных в dll A, будут освобождены той же dll, которая, очевидно, удалит только 10 байтов.

РЕДАКТИРОВАТЬ:

хорошо, если это не так, то просмотр стека вызовов показывает ошибку при выделении нового вектора с использованием конструктора копирования (строка 32 Dequeue), поэтому сначала разделите эту строку на 2 оператора, чтобы вы могли видеть, является ли это копией или всплывающее окно, вызывающее проблему (я всегда стараюсь помещать между ними оператор отладки, чтобы убедиться, что вы действительно знаете, в какой строке проблема).

В вашем примере кода показаны 2 разных способа доступа к очереди -> Enqueue и .Dequeue, что предполагает большую сложность самой очереди.

person gbjbaanb    schedule 15.08.2012
comment
Что ж, gbjbaanb, это ошибка копирования и вставки... доступ к ним такой же... извините за опечатку - person Novalis; 15.08.2012

Это чрезвычайно полезные функции для проверки того, что куча не повреждена в отладочной сборке:

_ASSERTE(_CrtCheckMemory());

Вы также можете установить флаг, чтобы при выходе проверять наличие утечек памяти:

Установив (оператор ИЛИ) следующий флаг: _CRTDBG_LEAK_CHECK_DF

Дополнительные сведения см. на следующих страницах:

http://msdn.microsoft.com/en-us/library/e73x0s4b(v=vs.110).aspx http://msdn.microsoft.com/en-us/library/5at7yxcs.aspx

person kashiraja    schedule 19.07.2013