发现一个关于CString类的问题,它在Rrelease下释放不掉,因为我在程序里用到了CStringArray。而后我就做了个最简单的SDI应用程序用来测试CString类。在菜单中添加Test项来进行测试,在MytestView.h中定义如下两个数组类:typedef CArray<CString*,CString*> strArray;
typedef CArray<char*,char*> charArray;
定义两个类成员:strArray m_strArray;
charArray m_charArray;
响应函数如下:
CMytestView::OnTest()
{
int i = 0;
for(i = 0;i < 1000000;i++)
{
CString* pstr = new CString;
pstr->operator +=("hello world");
m_strArray.Add(pstr);
}
for(i = 0;i < 1000000;i++)
{
delete m_strArray[i];
}
m_strArray.RemoveAll();
}
在Release版本下运行程序,响应菜单消息,通过任务管理器观察内存情况,发现内存有明显上升,可是没有下降,就停留在一个水平,之后不关闭主程序,再次响应菜单,发现内存又有明显上升,而后就降下来,降到第一次停留的水平,多试几次还是这样,就是说就第一次的内存没有释放掉。
而在Debug下运行程序却一切正常,内存上升后就下降。当用CStringArray的时候,通过Add加元素,最后RemoveAll,也是同样的情况。
之后我对函数做了如下修改:
CMytestView::OnTest()
{
int i = 0;
for(i = 0;i < 1000000;i++)
{
char* pchar = new char[i%10+1];
m_charArray.Add(pchar);
}
for(i = 0;i < 1000000;i++)
{
delete m_charArray[i];
}
m_charArray.RemoveAll();
}
在Release和Debug下同样运行程序,响应消息,观察内存都是先上去后下来,一切正常,我就郁闷了。
难道CString类在Release和Debug下就有这么大不同,是CString类有问题,VC++6.0有问题还是我的用法有问题,望各位大虾赐教啊!
typedef CArray<char*,char*> charArray;
定义两个类成员:strArray m_strArray;
charArray m_charArray;
响应函数如下:
CMytestView::OnTest()
{
int i = 0;
for(i = 0;i < 1000000;i++)
{
CString* pstr = new CString;
pstr->operator +=("hello world");
m_strArray.Add(pstr);
}
for(i = 0;i < 1000000;i++)
{
delete m_strArray[i];
}
m_strArray.RemoveAll();
}
在Release版本下运行程序,响应菜单消息,通过任务管理器观察内存情况,发现内存有明显上升,可是没有下降,就停留在一个水平,之后不关闭主程序,再次响应菜单,发现内存又有明显上升,而后就降下来,降到第一次停留的水平,多试几次还是这样,就是说就第一次的内存没有释放掉。
而在Debug下运行程序却一切正常,内存上升后就下降。当用CStringArray的时候,通过Add加元素,最后RemoveAll,也是同样的情况。
之后我对函数做了如下修改:
CMytestView::OnTest()
{
int i = 0;
for(i = 0;i < 1000000;i++)
{
char* pchar = new char[i%10+1];
m_charArray.Add(pchar);
}
for(i = 0;i < 1000000;i++)
{
delete m_charArray[i];
}
m_charArray.RemoveAll();
}
在Release和Debug下同样运行程序,响应消息,观察内存都是先上去后下来,一切正常,我就郁闷了。
难道CString类在Release和Debug下就有这么大不同,是CString类有问题,VC++6.0有问题还是我的用法有问题,望各位大虾赐教啊!
解决方案 »
- 哪位学过VB6的InputBox函数,我这有个VC2008的对话框问题
- 子对话框 改变大小 onsize
- 有没有在 SYS 驱动里实现 TCP 通讯的代码,给一份,谢谢!
- 如何可以让MessageBox显示更多的字符?
- 哪里有类似msdn中ChatSvr/ChatCli的例子程序,用socket api,而不是CSocket写的??
- 谁有ATL书籍? 100分相送 [email protected]
- 如何锁定 鼠标 在屏幕上任一区域
- if((str.Left(4)=="查看")||(str.Left(4)=="测试"))是什么意思?
- 两个CTime time1,time2,如何计算他们的天数
- 对一个ini文件操作,如何得到下面所有的key?是什么函数 ?谢谢。
- 请问这种窗口技术是怎么实现的?
- 如何制定开发时间?
CString m_strArray[1000000];
在菜单中加入“创建”菜单项。响应消息如下:
CMytestView::OnConstruct()
{
int i = 0;
for(i = 0;i < 1000000;i++)
{
m_strArray[i] = "abcdefghijklmn";
}
}
运行程序,观察任务管理器,在Debug下,当响应菜单后,内存明显上升,关掉子窗口,不退出程序,内存立刻降下去,一切正常;可当在Release下运行程序,然后也关闭子窗口,发现内存上去后就没下来。到底是怎么回事啊,是VC++6.0在对CString类的在两中情况下有什么不同啊,会产生这样的奇怪现象,那到你们没有吗?有可以试一下。
CString* pstr = new CString;不应该这么用吧。
第三段代码中:
CString m_strArray[1000000];用的好像也不对。
CString本来就是个字符串指针,它一般不是在堆上分配内存的,如果你这样
使用的话它将在堆上分配内存,占有很大的Memery。
你的意思是说自己重新建立一个和CString一样的类:CString1;然后在VC5中用吗?VC5中没有CString类吗?
我的用法有什么不对吗?那具体应该怎样用呢。还有占用内存是比较大,可为什么在Debug下能释放,而在Release下就不能释放内,你可以试一下吗?
__________这个判断是错误的,CString是一个类,它有一个字符串指针成员m_pchData,其内存也是在堆上分配的。==========VC5中没有CString类吗?
__________有,不然我也不会有“VC5下CString的源代码的DEBUG和RELEASE版没有太大区别”这个结论。实际上昨天晚上看到你的帖子,在VC6下面试过,发现问题,但是没找到原因。今天试了在VC5下还是有这个问题,而VC5的CString版本比VC6简单,准备从这里开刀。可是没想到重写了一个类,居然就正常了。
class C1String;struct C1StringData
{
long nRefs; // reference count
int nDataLength;
int nAllocLength;
// TCHAR data[nAllocLength] TCHAR* data()
{ return (TCHAR*)(this+1); }
};
extern AFX_DATA TCHAR fxChNil;
const C1String& AFXAPI fxGetEmptyString();
#define fxEmptyString fxGetEmptyString()class C1String
{
public:
// Constructors
C1String();
~C1String();
const C1String& operator+=(LPCTSTR lpsz);
// inlines
C1StringData* GetData() const
{ ASSERT(m_pchData != NULL); return ((C1StringData*)m_pchData)-1; }
operator LPCTSTR() const // as a C string
{ return m_pchData; }
protected:
LPTSTR m_pchData; // pointer to ref counted string data
void Init()
{ m_pchData = fxEmptyString.m_pchData; }
void AllocBuffer(int nLen);
void ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data);
void ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData);
static void PASCAL Release(C1StringData* pData);
static int PASCAL SafeStrlen(LPCTSTR lpsz)
{ return (lpsz == NULL) ? 0 : lstrlen(lpsz); }
};实现代码是直接copy的源代码,唯一的不同仅在于为了不冲突,把类名和宏名替换了。
首先:你声明的数组是typedef CArray<char*,char*> charArray;
参数类型是char*
所以对于你第一个OnTest()中的
CString* pstr = new CString;
pstr->operator +=("hello world");
m_strArray.Add(pstr);在准备开始Add操作并在参数pstr传递前,系统会pstr隐式转化为char*指针,从而你保存的是CString对象内部缓冲区的char*指针而不是对象本身的指针,因此你在最后作delete m_charArray[i];操作时,并没有调用CString对象的析构函数,而直接去删除这片区域,但是你最后没有调用
CString对象的ReleaseBuffer()函数(直接通过指针修改内部数据时最后要调用这个函数),
所以最后会发生内存泄漏
对于第二个OnTest()
你每次new char[i%10+1];操作时,都在堆空间分配一块连续的内存单元,单元类型是基本数据char,而不是CString,所以用m_charArray.Add(pchar);就是在数组中保存了这片区域首指针,
最后delete m_charArray[i];时,就是基本的C++销毁char*型数据块的语法了,而不牵涉到MFC对象
的销毁,自然不会出错
谢谢你的回复,按照你的做法,那它的实现部分也是照旧copy过来的吗?但我还要用到一些其他功能啊,比如比较,去处左右空白,转换类型等,这样不是很麻烦吗?
另外,我在想,既然在Debug下面没有问题,在Release下才有问题,那是不是在这两种情况下不同的编译机制会对CString类产生影响呢,有什么办法解决啊。?
下一步只有派生一个CString的子类来看看了。
______________我也是考虑到这个问题才拿VC5下的CString来下手的。因为这个版本的源码仅有一个地方用到了_DEBUG标示,是用来在Debug版下afxDump用的。也就是说有可能是编译优化方式的不同造成的这个问题。
但是我重写了CString这个类就没有问题了也让我一下子找不到头绪了……
在DEBUG中直接使用 delete new在release 中:
凡是字符长度<=512的,分别调用
FixedAlloced[ 64, 128,256,512 ]CFixedAlloc 类
有两个链表 分别管理Alloc与Free块在Alloc方法中:
如果Free链表中没有数据就 new
有的话就从Free链表中弹出一块使用
并将该块保存在Alloc链表中在Free方法中:
没有真正释放内存,只是将这块内存从Alloc链表中移到Free链表
释放内存只有在它的析构中被调用.因为CFixedlAlloc是作为全局静态对象使用的( 有 64,128,256,512共4个)
所以分配的内存就要等到程序退出才能释放.我们可以计算一下new 10000次的10 char字串的CString 对象所占内存
1:它调用了FixedAlloc64.
单个CString数据是 ( 64+1+ ( 12=(sizeof(CStringData)))) = 77个字节
在CFixedAlloc中因维护单向链表再开销一个4字节的指针 = 81.
2: new 10000次
81*10000 = 810000 = 810K.
3: delete .
内存没有被释放.
如果是 new 10000 次 10字长 char的对象.就是 11*100000 = 110K.
delete 后.内存被释放.这就是MFC为什么比SDK多占内存的原因. PS:根据本人研究MFC代码认为MFC的CString类是印度程序员写的.
理由如下: 以TrimRight函数为例.
有效率的算法是从字符尾部查找到头部或不同字符结束.
而且CString 保存了字串长度节约了strlen的开销.
这样尢其在大字串中很有效. 但是CString 各个版本都是从字符头开始查找.
想想TrimLeft与TrimRight都从Left开始,真是有点左右不分. 这个世界占左右不分的,记忆中是印度人才会这样.
CString::~CString()
// free any attached data
{
if (GetData() != afxDataNil)
{
if (InterlockedDecrement(&GetData()->nRefs) <= 0)
delete[] (BYTE*)GetData();
}
}
与VC6下不同,VC5版本的CString无论是Debug版还是Release版都只有这一个析构,中间也没有对字符串长度的判断,直接delete。但是仍然存在楼主的问题。
所以分配的内存就要等到程序退出才能释放.
______________________________________________________________________
之后我建的MDI程序,CString数组作为子视图的成员,在关闭子窗口也不会被释放吗,也要等到整个程序退出才会是放?
我怎么不到关于Release下调用FixedAlloced的说明啊,能否给些具体说明。
1:你的测试环境单不单纯,因为MFC其它类也许会有类似是操作.
2:delete new 不能简单看成 malloc, free的,因为C++是可以重载操作符的.如果大家有兴趣研究技术,我把我写的类库放到网上,不知大家有没有兴趣
{
ASSERT(nAllocSize >= sizeof(CNode));
ASSERT(nBlockSize > 1); m_nAllocSize = nAllocSize;
m_nBlockSize = nBlockSize;
m_pNodeFree = NULL;
m_pBlocks = NULL;
InitializeCriticalSection(&m_protect);
}CFixedAlloc::~CFixedAlloc()
{
FreeAll();
DeleteCriticalSection(&m_protect);
}void CFixedAlloc::FreeAll()
{
EnterCriticalSection(&m_protect);
m_pBlocks->FreeDataChain();
m_pBlocks = NULL;
m_pNodeFree = NULL;
LeaveCriticalSection(&m_protect);
}void* CFixedAlloc::Alloc()
{
EnterCriticalSection(&m_protect);
if (m_pNodeFree == NULL)
{
CPlex* pNewBlock = NULL;
TRY
{
// add another block
pNewBlock = CPlex::Create(m_pBlocks, m_nBlockSize, m_nAllocSize);
}
CATCH_ALL(e)
{
LeaveCriticalSection(&m_protect);
THROW_LAST();
}
END_CATCH_ALL // chain them into free list
CNode* pNode = (CNode*)pNewBlock->data();
// free in reverse order to make it easier to debug
(BYTE*&)pNode += (m_nAllocSize * m_nBlockSize) - m_nAllocSize;
for (int i = m_nBlockSize-1; i >= 0; i--, (BYTE*&)pNode -= m_nAllocSize)
{
pNode->pNext = m_pNodeFree;
m_pNodeFree = pNode;
}
}
ASSERT(m_pNodeFree != NULL); // we must have something // remove the first available node from the free list
void* pNode = m_pNodeFree;
m_pNodeFree = m_pNodeFree->pNext; LeaveCriticalSection(&m_protect);
return pNode;
}void CFixedAlloc::Free(void* p)
{
if (p != NULL)
{
EnterCriticalSection(&m_protect); // simply return the node to the free list
CNode* pNode = (CNode*)p;
pNode->pNext = m_pNodeFree;
m_pNodeFree = pNode;
LeaveCriticalSection(&m_protect);
}
}
#define __FIXALLOC_H__#include "afxplex_.h"/////////////////////////////////////////////////////////////////////////////
// CFixedAllocclass CFixedAlloc
{
// Constructors
public:
CFixedAlloc(UINT nAllocSize, UINT nBlockSize = 64);// Attributes
UINT GetAllocSize() { return m_nAllocSize; }// Operations
public:
void* Alloc(); // return a chunk of memory of nAllocSize
void Free(void* p); // free chunk of memory returned from Alloc
void FreeAll(); // free everything allocated from this allocator// Implementation
public:
~CFixedAlloc();protected:
struct CNode
{
CNode* pNext; // only valid when in free list
}; UINT m_nAllocSize; // size of each block from Alloc
UINT m_nBlockSize; // number of blocks to get at a time
CPlex* m_pBlocks; // linked list of blocks (is nBlocks*nAllocSize)
CNode* m_pNodeFree; // first free node (NULL if no free nodes)
CRITICAL_SECTION m_protect;
};#ifndef _DEBUG// DECLARE_FIXED_ALLOC -- used in class definition
#define DECLARE_FIXED_ALLOC(class_name) \
public: \
void* operator new(size_t size) \
{ \
ASSERT(size == s_alloc.GetAllocSize()); \
UNUSED(size); \
return s_alloc.Alloc(); \
} \
void* operator new(size_t, void* p) \
{ return p; } \
void operator delete(void* p) { s_alloc.Free(p); } \
void* operator new(size_t size, LPCSTR, int) \
{ \
ASSERT(size == s_alloc.GetAllocSize()); \
UNUSED(size); \
return s_alloc.Alloc(); \
} \
protected: \
static CFixedAlloc s_alloc \// IMPLEMENT_FIXED_ALLOC -- used in class implementation file
#define IMPLEMENT_FIXED_ALLOC(class_name, block_size) \
CFixedAlloc class_name::s_alloc(sizeof(class_name), block_size) \#else //!_DEBUG#define DECLARE_FIXED_ALLOC(class_name) // nothing in debug
#define IMPLEMENT_FIXED_ALLOC(class_name, block_size) // nothing in debug#endif //!_DEBUG#endif
{
int i = 0;
for(i = 0;i < 1000000;i++)
{
CString* pstr = new CString;
//pstr->operator +=("hello world"); 如果是空的CString没有问题
m_strArray.Add(pstr);
}
for(i = 0;i < 1000000;i++)
{
delete m_strArray[i];
}
m_strArray.RemoveAll();
}全部释放
无论你用什么CString的函数让CString不为空就会有你说的Release问题
而这样又不会有问题CMytestView::OnTest()
{
int i = 0;
for(i = 0;i < 1000000;i++)
{
CString* pstr = new CString;
pstr->operator +=("hello world");
m_strArray.Add(pstr);
pstr->Empty();
}
for(i = 0;i < 1000000;i++)
{
delete m_strArray[i];
}
m_strArray.RemoveAll();
}全部释放很见鬼的问题
CMytestView::OnTest()
{
int i = 0;
for(i = 0;i < 1000000;i++)
{
CString* pstr = new CString;
pstr->operator +=("hello world");
m_strArray.Add(pstr);
if (i > 500000)
m_strArray[i-500000]->Empty();
}
for(i = 0;i < 1000000;i++)
{
delete m_strArray[i];
}
m_strArray.RemoveAll();
}内存还有一半没有施放 CMytestView::OnTest()
{
int i = 0;
for(i = 0;i < 1000000;i++)
{
CString* pstr = new CString;
pstr->operator +=("hello world");
m_strArray.Add(pstr);
}
for(i = 0;i < 1000000;i++)
{
pstr->Empty();
delete m_strArray[i];
}
m_strArray.RemoveAll();
}不会释放建议不要保留大量的CString到数据中可以用CHAR*
想要对字符操作的时候用临时的CString操作完成Copy回去
E.g.
CHAR* p = m_chaArray.GetAt(index);
CString str(p);
.......... //str.Find += Left....
strcpy(p, (LPCTSTR)str);//保证p的长度
____________单纯的定义为何?一个MFC向导建立的项目能不能够上你说的单纯的条件?============2:delete new 不能简单看成 malloc, free的,因为C++是可以重载操作符的.
____________有点常识的人都不会这样看,而且正因为没有这样看,所以我才重写了一个C1String类,主要精力花在了析构上。另外,“因为C++是可以重载操作符的”在这里没什么意义。
pstr->Empty();应为m_strArray[i]->Empty();
#pragma init_seg(compiler)#define ROUND(x,y) (((x)+(y-1))&~(y-1))
#define ROUND4(x) ROUND(x, 4)
AFX_STATIC CFixedAlloc _afxAlloc64(ROUND4(65*sizeof(TCHAR)+sizeof(CStringData)));
AFX_STATIC CFixedAlloc _afxAlloc128(ROUND4(129*sizeof(TCHAR)+sizeof(CStringData)));
AFX_STATIC CFixedAlloc _afxAlloc256(ROUND4(257*sizeof(TCHAR)+sizeof(CStringData)));
AFX_STATIC CFixedAlloc _afxAlloc512(ROUND4(513*sizeof(TCHAR)+sizeof(CStringData)));#endif //!_DEBUGvoid CString::AllocBuffer(int nLen)
// always allocate one extra character for '\0' termination
// assumes [optimistically] that data length will equal allocation length
{
ASSERT(nLen >= 0);
ASSERT(nLen <= INT_MAX-1); // max size (enough room for 1 extra) if (nLen == 0)
Init();
else
{
CStringData* pData;
#ifndef _DEBUG
if (nLen <= 64)
{
pData = (CStringData*)_afxAlloc64.Alloc();
pData->nAllocLength = 64;
}
else if (nLen <= 128)
{
pData = (CStringData*)_afxAlloc128.Alloc();
pData->nAllocLength = 128;
}
else if (nLen <= 256)
{
pData = (CStringData*)_afxAlloc256.Alloc();
pData->nAllocLength = 256;
}
else if (nLen <= 512)
{
pData = (CStringData*)_afxAlloc512.Alloc();
pData->nAllocLength = 512;
}
else
#endif
{
pData = (CStringData*)
new BYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)];
pData->nAllocLength = nLen;
}
pData->nRefs = 1;
pData->data()[nLen] = '\0';
pData->nDataLength = nLen;
m_pchData = pData->data();
}
}void FASTCALL CString::FreeData(CStringData* pData)
{
#ifndef _DEBUG
int nLen = pData->nAllocLength;
if (nLen == 64)
_afxAlloc64.Free(pData);
else if (nLen == 128)
_afxAlloc128.Free(pData);
else if (nLen == 256)
_afxAlloc256.Free(pData);
else if (nLen == 512)
_afxAlloc512.Free(pData);
else
{
ASSERT(nLen > 512);
delete[] (BYTE*)pData;
}
#else
delete[] (BYTE*)pData;
#endif
}CString::~CString()
// free any attached data
{
if (GetData() != _afxDataNil)
{
if (InterlockedDecrement(&GetData()->nRefs) <= 0)
FreeData(GetData());
}
}
谢谢!那我能不能在我不用CString的时候调用CFixedAlloc::FreeAll()来释放掉呢。
谢谢,为什么我将STRCORE.CPP中的:
#define ROUND(x,y) (((x)+(y-1))&~(y-1))
#define ROUND4(x) ROUND(x, 4)
AFX_STATIC CFixedAlloc _afxAlloc64(ROUND4(65*sizeof(TCHAR)+sizeof(CStringData)));
AFX_STATIC CFixedAlloc _afxAlloc128(ROUND4(129*sizeof(TCHAR)+sizeof(CStringData)));
AFX_STATIC CFixedAlloc _afxAlloc256(ROUND4(257*sizeof(TCHAR)+sizeof(CStringData)));
AFX_STATIC CFixedAlloc _afxAlloc512(ROUND4(513*sizeof(TCHAR)+sizeof(CStringData)));
拷贝过来,再把FIXALLOC.H和FIXALLOC.CPP有拷贝过来。
然后#include "FIXALLOC.H";
再在程序里手工调用:
#ifndef _DEBUG
_afxAlloc64.FreeAll();
_afxAlloc128.FreeAll();
_afxAlloc256.FreeAll();
_afxAlloc512.FreeAll();
#endif
还是不能释放掉呢。
应该要怎么用啊?
建议如下定义:
*.h
void DoFixedAlloc_FreeAll();//
.CPP#ifndef _DEBUG#pragma warning(disable: 4074)
#pragma init_seg(compiler)AFX_STATIC CFixedAlloc _afxAlloc64(ROUND4(65*sizeof(TCHAR)+sizeof(CStringData)));
AFX_STATIC CFixedAlloc _afxAlloc128(ROUND4(129*sizeof(TCHAR)+sizeof(CStringData)));
AFX_STATIC CFixedAlloc _afxAlloc256(ROUND4(257*sizeof(TCHAR)+sizeof(CStringData)));
AFX_STATIC CFixedAlloc _afxAlloc512(ROUND4(513*sizeof(TCHAR)+sizeof(CStringData)));
void DoFixedAlloc_FreeAll()
{
_afxAlloc64.FreeAll();
_afxAlloc64.FreeAll();
...
}
#else // if debug nop
void DoFixedAlloc_FreeAll()
{
}
#endif //!_DEBUG因为 AFX_STATIC 是在 afxver_.h 定义成 extern 的
所以你可以在外重定义没问题
而且看 CPlex的代码,也根本不能调用FreeAll.它是FREE所有在用的模块.
用STL或者自已写个String类吧.反正高手都是这样干的的:)
我在MytestView.h中定义全局函数void DoFixedAlloc_FreeAll();
在.cpp中include "FIXALLOC.H"
实现全局函数:
#ifndef _DEBUG#pragma warning(disable: 4074)
#pragma init_seg(compiler)#define ROUND(x,y) (((x)+(y-1))&~(y-1))
#define ROUND4(x) ROUND(x, 4)AFX_STATIC CFixedAlloc _afxAlloc64(ROUND4(65*sizeof(TCHAR)+sizeof(CStringData)));
AFX_STATIC CFixedAlloc _afxAlloc128(ROUND4(129*sizeof(TCHAR)+sizeof(CStringData)));
AFX_STATIC CFixedAlloc _afxAlloc256(ROUND4(257*sizeof(TCHAR)+sizeof(CStringData)));
AFX_STATIC CFixedAlloc _afxAlloc512(ROUND4(513*sizeof(TCHAR)+sizeof(CStringData)));
void DoFixedAlloc_FreeAll()
{
_afxAlloc64.FreeAll();
_afxAlloc128.FreeAll();
_afxAlloc256.FreeAll();
_afxAlloc512.FreeAll();
}
#else // if debug nop
void DoFixedAlloc_FreeAll()
{}
#endif //!_DEBUG
通过菜单响应消息:
void CMytestView::OnDestroy()
{
// TODO: Add your command handler code here
for(int i = 0;i < 1000000;i++)
{
m_strArray[i].Empty();
}
DoFixedAlloc_FreeAll();
}
还是不行啊。怎么回事??
万分感谢你们,我要对这个帖子追加给分,怎么追加啊。?
//#include "stdafx.h"
#include "CStringTest.h"#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif/////////////////////////////////////////////////////////////////////////////
// The one and only application object
#include <Afxtempl.h>CWinApp theApp;using namespace std;typedef CArray<CString*,CString*> strArray;
typedef CArray<char*,char*> charArray;strArray m_strArray;
charArray m_charArray;void OnTest()
{
for(int i = 0; i < 1000000; i++)
{
CString* pstr = new CString;
pstr->operator +=("hello world");
m_strArray.Add(pstr);
}
for(i = 0;i < 1000000;i++)
{
delete m_strArray[i];
}
m_strArray.RemoveAll();
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
cerr << _T("Fatal Error: MFC initialization failed") << endl;
nRetCode = 1;
}
else
{
// TODO: code your application's behavior here.
OnTest();
}
return nRetCode;
}
就停留在一个水平,之后不关闭主程序,再次响应菜单,发现内存又有明显上升,而后就降下来,降到第一次停留的水平,多试几次还是这样,就是说就第一次的内存没有释放掉。
__________________我觉得可能是编译器的原因,在release版本下让heap manager做“假释放”,以备以后的使用。第二次以后运行这段代码内存的增加量远比第一次运行的小。
但是始终有点说不通。诚如前面提到的,VC6版本的CString在Release下可能没有真正的释放内存,那么为什么VC5下就是直接delete的,仍然有这个状况?而第二次运行这段代码又貌似正常。不过因为以后使用相同的代码不会造成额外的内存负担,姑且认为从整体上说没有内存泄露吧,应该不会有太大问题。不然就自己写个类吧。
我把VC6 CString内存管理的分析,在blog写了一篇.
不过不写不知道,我的文笔真是差啊-_-!!
我对产生问题的原因总结为是教条主义.
固执于采用C++的new delete操作符操纵内存造成的.
所以VC7中还是改成C的malloc realloc free方式了.地址在这,敬请斧正
http://blog.csdn.net/roscoe/archive/2005/05/22/377949.aspx不怕被人笑话.我是坚持认为CString 是印度阿三写的.不然怎么会这么臭
固执于采用C++的new delete操作符操纵内存造成的.
__________________________________
VC6 CString的内存管理并不是一种缺憾,这只不过是一个取舍的问题。严格说CString的内存管理方式并不叫内存池,所谓内存池是指预先开辟一大块空间,程序需要使用时便从池中取出一定的空间,使用完毕即归还池中,可提高效率和防止系统级的内存碎片。CString的做法是用过的内存并不释放掉,而是回收起来供下一次使用,性质跟内存池是一样的,都是程序自身介入内存管理,一般来说程序自己管理内存均比系统管理高效。你对比下VC6的CString和std::string的执行效率就知了。但是有得必有失,在效率提升的同时也造成空间得不到及时释放,但不是泄露,它不会象内存泄露那样可能造成内存逐渐的耗尽,因为内存始终在自己控制之内。还有一个缺点是造成理解的困难,因为一般程序员不知道CString使用内存块回收管理的话,就认为一个CSting释放了它储存字符串的空间也必定释放了,这样程序的内部实现与程序员的预期不一样,这样就有可能产生问题(虽然一般不会)。所以如果VC7的CString放弃了了内存块回收管理,应该是出于稳定大于一切的考虑,取与舍而已,不过我觉得它最好有一个编译开关,由程序员决定用哪一种方式。
的确用惯C++的new、delete很多人都忘了malloc realloc ,其实没有构造函数时new的调用就相当于malloc。至于realloc,它有可能接近于一次malloc的开销,也可能稍大于一次free加一次malloc的开销。
这个大错! 应为: delete [] m_charArray[i];
为什么第一次释放了所有的数据内存并没有下降. 其实这里下降会有的,不过响应太快,你没看出来.但是为什么会停在某一水平? 这是因为CArray 的内存管理引起的. 你可以看看 CArray 的源代码, RemoveAll 是不会 释放 已经申请的内存.
其后,你再进行分配串操作, 这时CArray 已经不用再重新分配内存了. 楼主将数组定义为 函数的局部变量试试,我相信这种情况将不复存在. 内存会完全释放掉.=================
引用计数是用来处理引用安全问题的(共享方问,数据保护),new 与delete的动作是被它引发的.
nRefs > 1 == 共享 :如果调用者要对数据修改,则拷贝一个副本到本地后再修改.
nRefs == -1 上锁:读,写前都先拷贝一个副本.
==================================================================
To: loveghb(温柔的毒药)
你应该在new 之前与 之后与delete之后,中断一下可以看出区别.
==================================================================
To: ipgk(loboho) ( )
==================================================================
VC7中是改了, 从IAtlMemMgr派生 crt,local,global,com几个对象
CStringData中加了一个IAtlMemMgr指针.
不觉得更安全,但这样写代码结构要比VC6清楚些.
应该是出于稳定大于一切的考虑,取与舍而已==>我不这么看,我们也得允许MS有犯错误权利不是^_^
realloc开销接近于malloc+free的情况只会在realloc时连续空间不足才会出现.
对于字符串处理中,大部分情况是realloc的开销要小于malloc的.PS: VC7 的 CString现在是内存还是只增加不减少,不过VC6是不会释放,现在它会释放了.哈哈
…………………………………………………………………………………………
这句我不理解。
Release 下不大于512b的时CString析构后内存得不到回收,算不算.2:. VC7 中 share | Lock 时拷贝副本不谈,当正常情况下时 GetBuffer
调用的是PrepareWrite2
ATL_NOINLINE void PrepareWrite2( int nLength )
{
CStringData* pOldData = GetData();
if( pOldData->nDataLength > nLength )
{
nLength = pOldData->nDataLength;
}
if( pOldData->IsShared() )
{
Fork( nLength );
}
else if( pOldData->nAllocLength < nLength ) // 只有大于原有内存才会调用Reallocate
{
int nNewLength = pOldData->nAllocLength;
if( nNewLength > 1024 )
{
nNewLength += 1024;
}
else
{
nNewLength *= 2;
}
if( nNewLength < nLength )
{
nNewLength = nLength;
}
Reallocate( nNewLength );
}
}
===============================================================
就是说正常情况下CString(7)引发Reallocate的条件是nRefs=1 And nLength > nAllocLength.
这么一来是不是没有办法将内存缩小了?
我这没装VC.net 你可以试下,先GetBuffer(1M),ReleaseBuffer,再GetBuffer(1K),ReleaseBuffer
看看GetAllocLength()是什么变化.
还过7中CStringDaTa::Release 是会调用Free的,所以析构后内存还是能正常回收的
——————————————————————-
这点我说了是取舍问题了,他并不是MFC程序员的失误,而是故意这样做的,就算不得错。其实如果我来写CString的话,我还是宁愿选VC6的内存块回收方式,因为这样的确大大提高了效率。另外要说内存用完就忙不失交还系统,表面是节约内存,实际上造成系统内存碎片的增多,系统的内存维护链表增大,访问内存就越慢,同时即使你的空余内存总和够用,碎片太多的话,也可能出现申请不到内存的情况。第2点我就不说了,我也没.net。不过想问roscoe(草上飞)的机子内存是多少,为什么这么紧张内存:)
对于string对象,内存碎片倒确是首要考虑的