HEAP: Free Heap block XXXX modified at XXXX after it was freed

*本文旨在解決在調試過程中遇到如下問題時的解決辦法:

HEAP: Free Heap block XXXXA modified at XXXXB after it was freed

意思是:已經釋放的內存地址A,在B地址處的值被改變(A和B都處於被釋放的內存段內),即很可能出現了野指針,而很多情況下你會說,我的每個new和delete都是成對的,在delete後將指針賦值爲NULL。但是我想說,野指針不單單會出現在變量中,它有可能會隱藏到你的類函數中,尤其是靜態函數中。

先說說我遇到的問題。由於需要和下位機通過串口通信,就封裝了一個串口操作的類CSerialPort作爲基類,一個和下位機進行協議交互的類CSmartIOS2作爲子類,父類在打開串口後會啓動一個讀寫串口的線程,線程函數爲基類的靜態函數。在程序中調用此類進行通信,通信過程中沒有任何問題,當應用程序結束時,我會關掉串口,釋放掉CSmartIOS2的實例對象,問題來了,會報以上錯誤。一下子蒙逼了,在經歷過把所有new和delete相關的內容都篩選後,仍然沒找到可能出問題的地方。經過一番折騰,終於讓我找到了一點規律,

HEAP: Free Heap block XXXXA modified at XXXXB after it was freed

XXXXA地址和XXXXB地址還有CSmartIOS2類實例的地址有如下關係:A地址始終比實例地址小,而且B地址和實例地址的差值始終等於64,這讓我更加肯定了自己的猜想,改變的地址B是CSmartIOS2或其基類中的成員變量。

經過斷點監測類實例地址,通過以上規律算出了類成員的地址,然後下內存斷點,當該地址的值被改變時機會觸發斷點。如此終於找到了讓此變量改變的地方,位於CSerialPort類的靜態函數中,也是用於讀寫串口的線程,而這個地址就是類中的一個成員的地址。一切真像大白後,仍有一點不明,那就是明明我在delete 類實例前,先結束掉了串口讀寫線程,爲何還會在delete後被該線程改變了內存呢?

我在關閉端口時,先通過

pBoard->ClosePort()

來結束掉IO線程,關閉端口。緊接着就做delete pBoard;動作,而判斷線程結束的標識是位於線程函數返回前的一個布爾變量,當變量爲真時,線程馬上就結束了,但是是馬上而不是已經結束,此時調用關閉端口的線程在判斷IO線程變量爲真後,立刻做delete pBoard,但是IO線程中的最後一句return 0有可能還沒執行,當delete完成後,IO線程函數執行完畢,釋放線程資源,此時又對類中的一個用於重疊結構的變量

OVERLAPPED m_ov;
的值被改變,問題就這樣發生了,最簡單的解決辦法就是在結束掉線程後,適當等待一段時間給IO線程去釋放資源。大概的代碼片如下:

HANDLE yk_s2_InitBoard(UINT nPort)
{
	MyLock lock(list_lock);

	CSmartIOS2* pBoard = new CSmartIOS2();
	if(pBoard->Init(nPort))
	{
		int ipList[16] = {0};
		int nIP = 0;
		int result = pBoard->EnumIP(ipList,nIP);
		if(result == RET_NUMBER)
		{
			if(nIP > 0)
			{
				theBoardS2List.push_back(pBoard);
				return pBoard;
			}
		}
		else if(result == RET_INVALID_CMD)
		{
			theBoardS2List.push_back(pBoard);
			return pBoard;
		}
		pBoard->ClosePort();
	}
	delete pBoard;
	return NULL;
}


//關閉串口,釋放內存
BOOL yk_s2_CloseBoard(HANDLE hBoard)
{
	if(hBoard == NULL)
		return FALSE;
	if(!isValidatePtr(hBoard))
		return FALSE;

	MyLock lock(list_lock);

	CSmartIOS2* pBoard = (CSmartIOS2*)hBoard;
	if(pBoard->IsOpen())
	{
		if(pBoard->ClosePort())
		{
			Sleep(500);//此處是整個問題的關鍵點
			std::list<CSmartIOS2*>::iterator iter;
			for(iter = theBoardS2List.begin();iter != theBoardS2List.end();)
			{
				if((*iter) == pBoard)
				{
					delete pBoard;
					iter = theBoardS2List.erase(iter);
					return TRUE;
				}
				else
					++iter;
			}
		}
		else
		{
			return FALSE;
		}
	}
	return TRUE;
}

總結如下:

遇到此問題後,首先應該大體定位出問題的內存地址大概是屬於那個對象的,最好可以推出具體的某個變量。然後通過內存斷點去找改變此地址值的代碼段。然後定位問題,分析引起原因。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章