Home

About

Memory Leak

08 Mar 2015

工具: LeakFinderC.exe

用法: LeakFinderC dumpFile leakCandidateFile symbolFile [falseAlarmFile]

常见错误:

inline BSTR MsiString2BSTR(const MBase::String& String)
{
	return SysAllocString(String.c_str());	//copied once
}

class CComBSTR
{
public:
	BSTR m_str;
	CComBSTR()
	{
		m_str = NULL;
	}
	/*explicit*/ CComBSTR(Int32 nSize)
	{
		m_str = ::SysAllocStringLen(NULL, nSize);
	}
	/*explicit*/ CComBSTR(Int32 nSize, LPCOLESTR sz)
	{
		m_str = ::SysAllocStringLen(sz, nSize);
	}
	/*explicit*/ CComBSTR(LPCOLESTR pSrc)
	{
		m_str = ::SysAllocString(pSrc);	//copied twice
	}
	/*explicit*/ CComBSTR(const CComBSTR& src)
	{
		m_str = src.Copy();
	}
	/*explicit*/ CComBSTR(REFGUID src)
	{
		LPOLESTR szGuid;
		StringFromCLSID(src, &szGuid);
		m_str = ::SysAllocString(szGuid);
		CoTaskMemFree(szGuid);
	}
	CComBSTR& operator=(const CComBSTR& src)
	{
		if (m_str != src.m_str)
		{
			if (m_str)
				::SysFreeString(m_str);
			m_str = src.Copy();
		}
		return *this;
	}

	CComBSTR& operator=(LPCOLESTR pSrc)
	{
		::SysFreeString(m_str);
		m_str = ::SysAllocString(pSrc);
		return *this;
	}

	~CComBSTR()
	{
		::SysFreeString(m_str);
	}
	
	operator BSTR() const
	{
		return m_str;
	}
	BSTR* operator&()
	{
		return &m_str;
	}
	
	void Attach(BSTR src)
	{
		if (m_str != src)
		{
			::SysFreeString(m_str);
			m_str = src;
		}
	}
	BSTR Detach()
	{
		BSTR s = m_str;
		m_str = NULL;
		return s;
	}
};

CComBSTR lFileEncodingBSTR = MsiString2BSTR(lFileEncoding);
HRESULT CDSSEvaluationPU::Init()
{
	...
}

STDMETHODIMP CDSSDataSource::Reset()
{
	...
	switch (mMode)	//missing a case here so mpEvalPU.Release() is never invoked 
	{
		case xxx:
			...
			break;
		...
		default:
			return TRACE_LEAVE();
	}
	...
	mpEvalPU.Release();
	return TRACE_LEAVE;
}
//MContractMgr::CreateContractManagerInstance();
//MDb::ContractManagerPtr lContractManagerPtr(new DSSDbContractManager(100*1024*1024, MContractMgr::ContractManagerInstance())); 

MDb::ContractManagerPtr lContractManagerPtr(new DSSDbContractManager(100*1024*1024, MContractMgr::CreateContractManagerInstance())); 	//Memory leak happens in the above two lines!! Comment them out and use this line fixes this leak 


namespace MBase 
{
	class ReferenceCounted
	{
	protected:

		virtual ~ReferenceCounted() throw()
		{
		};

	public:
		virtual void AddRef() const throw()=0;
		virtual void Release() const throw()=0;
	};
}

namespace MContractMgr {

class ContractManager : public MBase::ReferenceCounted
{
public:
	// Reserve returns a "handle" of the memory reserved (to be used in unreserve)
	virtual unsigned Int32 InitiateContract(const _TCHAR* ipcSource)=0;
	virtual void CancelContract(unsigned Int32 inHandle) throw()=0;

	virtual MCMRESULT Reserve(unsigned Int32 inHandle,unsigned Int32 inSize,unsigned Int32 inTimeout)=0;
	//NC- None-continuous version. The reserved memory is not necessarily continuous
	//The size could be > 4GB and in Int64 type
	virtual MCMRESULT ReserveNC(unsigned Int32 inHandle,unsigned Int64 inSize,unsigned Int32 inTimeout,bool ibContinuous = true)=0;
	virtual void UnReserve(unsigned Int32 inHandle,unsigned Int32 inSize) throw()=0;
	//NC- None-continuous version. The size is 64 bit.
	virtual void UnReserveNC(unsigned Int32 inHandle,unsigned Int64 inSize) throw()=0;
	
	virtual void SetMemoryCounters(MemoryStatus::MemoryCounters &irMemoryCounters)=0;
};

typedef MBase::SmartPtrI<ContractManager> ContractManagerPtr;
DLL_CONTRACTMGR_EXIM ContractManager* CreateContractManagerInstance();
DLL_CONTRACTMGR_EXIM ContractManager* ContractManagerInstance();

}

ContractManager* CreateContractManagerInstance()
{
#if defined WIN32
#ifdef SMARTHEAP
	return ContractManagerImplSHNT::CreateInstance();
#else
	return ContractManagerImplNT::CreateInstance();
#endif
#elif defined sun
	return ContractManagerImplSolaris::CreateInstance();
#elif defined _AIX
	return ContractManagerImplAIX::CreateInstance();
#elif defined(linux) || defined(__APPLE__)
	return ContractManagerImplLinux::CreateInstance();
#elif defined __hpux
	return ContractManagerImplHPUX::CreateInstance();
#else
#error	unsupported platform
#endif
}

ContractManagerImplNT* ContractManagerImplNT::CreateInstance()
{
	if(mpContractManager.IsNull())
	{
		mpContractManager = new ContractManagerImplNT();
		_ASSERT(!mpContractManager.IsNull());
		if (!mpContractManager.IsNull()) {
			if (!mpContractManager->InitMCM()) {
				_ASSERT(false);
				mpContractManager.Reset();
			}
		}
	}

	return mpContractManager.Get();
}

ContractManager* ContractManagerInstance()
{
#if defined WIN32
#ifdef SMARTHEAP
	return ContractManagerImplSHNT::Instance();
#else
	return ContractManagerImplNT::Instance();
#endif
#elif defined sun
	return ContractManagerImplSolaris::Instance();
#elif defined _AIX
	return ContractManagerImplAIX::Instance();
#elif defined(linux) || defined(__APPLE__)
	return ContractManagerImplLinux::Instance();
#elif defined __hpux
	return ContractManagerImplHPUX::Instance();
#else
	#error "Unsupported platform"
#endif
}

ContractManagerImplNT* ContractManagerImplNT::Instance()
{
	return mpContractManager.Get();
}

namespace MDb
{
	/**
	 * MDb::ContractManager is the interface that the db classes will use to create memory contracts.
	 * The application using the db classes must provide the MDb::ConnectionManager with an object that
	 * implements this interface in order to enable contract manager in the db classes.
	 */
	class ContractManager: public MBase::ReferenceCounted
	{
	public:
		virtual MBase::ReturnPtrI<MBase::MemoryContract> CreateMemoryContract(wchar_t* ipSource) = 0;

		virtual unsigned int GetMaximumOutOfProcessTableSize() = 0;
	};

	typedef MBase::SmartPtrI<ContractManager> ContractManagerPtr;

} // namespace

class DSSDbContractManager: public MDb::ContractManager
{
public:
	DSSDbContractManager(unsigned int iMaximumOutOfProcessTableSize, 
		MContractMgr::ContractManager* ipContractManager);
	virtual ~DSSDbContractManager() throw();

	MBase::ReturnPtrI<MBase::MemoryContract> CreateMemoryContract(wchar_t* ipSource);

	unsigned int GetMaximumOutOfProcessTableSize();

	REFERENCE_COUNTED_IMPLEMENTATION
private:
	const unsigned int mMaximumOutOfProcessTableSize;
	MContractMgr::ContractManagerPtr const mpContractManager;
};

DSSDbContractManager::DSSDbContractManager(unsigned int iMaximumOutOfProcessTableSize, 
										   MContractMgr::ContractManager* ipContractManager):
	mMaximumOutOfProcessTableSize(iMaximumOutOfProcessTableSize),
	mpContractManager(ipContractManager)
{
	_ASSERTE(ipContractManager);
}

namespace MBase
{
	template<class ReferenceCountedT>
	class SmartPtrI
	{
	public:
		SmartPtrI(ReferenceCountedT* ipData=NULL) throw() :
		  mpData(ipData)
		{
			if(mpData)
			{
				mpData->AddRef();
			}
		}

		// copy constructor allowed on SmartPtr
		// upcast should be implicit
		template<class ReferenceCountedU>
		SmartPtrI(const SmartPtrI<ReferenceCountedU>& irSmartPtrI) throw()
		{
			if (!irSmartPtrI.IsNull())
			{
				mpData = irSmartPtrI.Get();
				mpData->AddRef();
			}
			else
			{
				mpData = NULL;
			}
		}

		SmartPtrI(const SmartPtrI<ReferenceCountedT>& irSmartPtrI) throw()
		{
			if (!irSmartPtrI.IsNull())
			{
				mpData = irSmartPtrI.Get();
				mpData->AddRef();
			}
			else
			{
				mpData = NULL;
			}
		}

		~SmartPtrI() throw()
		{
			Dispose();
		}

		void Reset(ReferenceCountedT* ipData=NULL) throw()
		{
			if(mpData!=ipData)
			{
				Dispose();
				mpData=ipData;
				if(mpData)
				{
					mpData->AddRef();
				}
			}
		}

		// Attach to a raw pointer without AddRef
		void Attach(ReferenceCountedT* ipData=NULL) throw()
		{
			Dispose();
			mpData=ipData;
		}

		// Assignment operator of raw pointer
		// upcast should be implicit
		SmartPtrI& operator=(ReferenceCountedT* ipData) throw()
		{
			Reset(ipData);
			return *this;
		}

		// Assignment operator
		// upcast should be implicit
		template<class ReferenceCountedU>
		SmartPtrI& operator=(const SmartPtrI<ReferenceCountedU>& irSmartPtrI) throw()
		{
			if (!irSmartPtrI.IsNull())
			{
				Reset(irSmartPtrI.Get());
			}
			else
			{
				Reset(NULL);
			}
			return *this;
		}

		SmartPtrI& operator=(const SmartPtrI<ReferenceCountedT>& irSmartPtrI) throw()
		{
			if (!irSmartPtrI.IsNull())
			{
				Reset(irSmartPtrI.Get());
			}
			else
			{
				Reset(NULL);
			}
			return *this;
		}

		// Assignment operator from a return ptr
		// upcast should be implicit
		template<class ReferenceCountedU>
		SmartPtrI& operator=(ReturnPtrI<ReferenceCountedU>& irReturnPtrI) throw()
		{
			// do not add ref again, "steal" the reference from the return ptr
			Reset(NULL);
			mpData=irReturnPtrI.GiveUp();
			return *this;
		}

		ReferenceCountedT* operator->() const throw()
		{
			_ASSERTE(mpData!=NULL);
			return mpData;
		}

		ReferenceCountedT& operator*() const throw()
		{
			_ASSERTE(mpData!=NULL);
			return *mpData;
		}

		ReferenceCountedT* Get() const throw()
		{
			return mpData;
		}

		bool IsNull() const throw()
		{
			return !mpData;
		}

		template<class ReferenceCountedU>
		void DownCastFrom(SmartPtrI<ReferenceCountedU>& irSmartPtrI) throw()
		{
			// Only modify if iData is different from the current member
			if(static_cast<ReferenceCountedU*>(mpData) != irSmartPtrI.Get())
			{
				Reset(reinterpret_cast<ReferenceCountedT*>(irSmartPtrI.Get()));
			}
		}

		bool operator==(const SmartPtrI& iSmartBase) const
		{
			return mpData==iSmartBase.mpData;
		}

		bool operator<(const SmartPtrI& iSmartBase) const
		{
			return mpData<iSmartBase.mpData;
		}

		// GiveUp will relinquish this reference to the object
		// (we will not call release when it goes out of scope)
		ReferenceCountedT* GiveUp() throw()
		{
			ReferenceCountedT* lpTemp=mpData;
			mpData=NULL;
			return lpTemp;
		}

		// implicit cast to return ptr (to allow user to just call "return SmartPtrI")
		operator ReturnPtrI<ReferenceCountedT>() const
		{
			return Return();
		}

		// create a ReturnPtrI from this smart ptr
		// this will increment the reference count by 1 (upon creating the return ptr)
		ReturnPtrI<ReferenceCountedT> Return() const
		{
			return ReturnPtrI<ReferenceCountedT>(mpData);
		}
	private:
		void Dispose() throw()
		{
			//To avoid problems with circular references make a temporary
			//copy first and set the member to the new one before deleting it
			ReferenceCountedT* lpDataTemp = mpData;
			if(lpDataTemp)
			{
				mpData=NULL;
				lpDataTemp->Release();
			}
		}
		ReferenceCountedT* mpData;
	};
}

几点感想:

  1. Smart point is not silver bullet. 其实它并没有解决问题,只是转移了问题。

  2. 用智能指针其实更具迷惑性。因为智能指针本身也要申请内存。这样我们除了要关注智能指针包含的指针的内存管理,还要留意智能指针本身申请的内存什么时候释放

  3. 用了智能指针程序可读性更差