对标题给出一个简而言之的结论:MFC经过几次的效能更新的版本,修复一些问题还会产生另一个问题
Realse版本的CString保留了所有512字节以下申请字符串的缓冲区,并未释放,说是为了减少内存碎片增加性能。
Struct { CStrng title; int a; .... }IoriStruct
样例结构如上,其余省略,运行问题与无问题代码说明【版权:不及格的程序员-八神】
CString title = "this is a title"; for(i=0;i<9999;i++) { IoriStruct *temp = new IoriStruct(); temp->title = "this is a title"; //这样里引用上面的title是没有问题的,成员变量titlle字段类型换成std::string也没有问题,但是当直接在这里赋值“this is a thitle”常量并且在release模式就会有问题了,这个字符串会在内存里生成好多。
}
...
delete 上面生成的结构代码省略;
程序在debug模式下运行后,结构占居空间已经不存在,内存被释放feee添充。【版权:不及格的程序员-八神】
--------------CString内存占用问题Release模式运行结果-----------------
程序运行之后,离开字符串变量范转后,内存里大量字符串存留,占居空间。【版权:不及格的程序员-八神】
Windbg观察内存状态, 28大小的为IoriStruct, 1404的为与之对应的CString成员变量
结构大小
当结构被 delete 后,结构的内存空间释放后已经被feee添充,可成员变量指向的内存区仍占用内存。
调试版CRT new 关键源码
LISTROWINFO *list = new LISTROWINFO; void* AFX_CDECL operator new(size_t nSize, LPCSTR lpszFileName, int nLine) { return ::operator new(nSize, _NORMAL_BLOCK, lpszFileName, nLine); } void * __cdecl _nh_malloc_dbg ( size_t nSize, int nhFlag, int nBlockUse, const char * szFileName, int nLine ) { void * pvBlk; for (;;) { #ifdef _MT /* lock the heap */ _mlock(_HEAP_LOCK); __try { #endif /* _MT */ /* do the allocation */ pvBlk = _heap_alloc_dbg(nSize, nBlockUse, szFileName, nLine); #ifdef _MT } __finally { /* unlock the heap */ _munlock(_HEAP_LOCK); } #endif /* _MT */ if (pvBlk || nhFlag == 0) return pvBlk; /* call installed new handler */ if (!_callnewh(nSize)) return NULL; /* new handler was successful -- try to allocate again */ } } void * __cdecl _heap_alloc_dbg( size_t nSize, int nBlockUse, const char * szFileName, int nLine ) { long lRequest; size_t blockSize; int fIgnore = FALSE; _CrtMemBlockHeader * pHead; /* verify heap before allocation */ if (_crtDbgFlag & _CRTDBG_CHECK_ALWAYS_DF) _ASSERTE(_CrtCheckMemory()); lRequest = _lRequestCurr; /* break into debugger at specific memory allocation */ if (lRequest == _crtBreakAlloc) _CrtDbgBreak(); /* forced failure */ if (!(*_pfnAllocHook)(_HOOK_ALLOC, NULL, nSize, nBlockUse, lRequest, szFileName, nLine)) { if (szFileName) _RPT2(_CRT_WARN, "Client hook allocation failure at file %hs line %d.\n", szFileName, nLine); else _RPT0(_CRT_WARN, "Client hook allocation failure.\n"); return NULL; } /* cannot ignore CRT allocations */ if (_BLOCK_TYPE(nBlockUse) != _CRT_BLOCK && !(_crtDbgFlag & _CRTDBG_ALLOC_MEM_DF)) fIgnore = TRUE; /* Diagnostic memory allocation from this point on */ if (nSize > (size_t)_HEAP_MAXREQ || nSize + nNoMansLandSize + sizeof(_CrtMemBlockHeader) > (size_t)_HEAP_MAXREQ) { _RPT1(_CRT_ERROR, "Invalid allocation size: %u bytes.\n", nSize); return NULL; } if (!_BLOCK_TYPE_IS_VALID(nBlockUse)) { _RPT0(_CRT_ERROR, "Error: memory allocation: bad memory block type.\n"); } blockSize = sizeof(_CrtMemBlockHeader) + nSize + nNoMansLandSize; #ifndef WINHEAP /* round requested size */ blockSize = _ROUND2(blockSize, _GRANULARITY); #endif /* WINHEAP */ pHead = (_CrtMemBlockHeader *)_heap_alloc_base(blockSize);//具体申请多少内存 userSize if (pHead == NULL) return NULL; /* commit allocation */ ++_lRequestCurr; if (fIgnore) { pHead->pBlockHeaderNext = NULL; pHead->pBlockHeaderPrev = NULL; pHead->szFileName = NULL; pHead->nLine = IGNORE_LINE; pHead->nDataSize = nSize; pHead->nBlockUse = _IGNORE_BLOCK; pHead->lRequest = IGNORE_REQ; } else { /* keep track of total amount of memory allocated */ _lTotalAlloc += nSize; _lCurAlloc += nSize; if (_lCurAlloc > _lMaxAlloc) _lMaxAlloc = _lCurAlloc; if (_pFirstBlock) _pFirstBlock->pBlockHeaderPrev = pHead; else _pLastBlock = pHead; pHead->pBlockHeaderNext = _pFirstBlock; pHead->pBlockHeaderPrev = NULL; pHead->szFileName = (char *)szFileName; pHead->nLine = nLine; pHead->nDataSize = nSize; pHead->nBlockUse = nBlockUse; pHead->lRequest = lRequest; /* link blocks together */ _pFirstBlock = pHead; } /* fill in gap before and after real block */ memset((void *)pHead->gap, _bNoMansLandFill, nNoMansLandSize); memset((void *)(pbData(pHead) + nSize), _bNoMansLandFill, nNoMansLandSize); /* fill data with silly value (but non-zero) */ memset((void *)pbData(pHead), _bCleanLandFill, nSize); return (void *)pbData(pHead); }
传入0x70+0n32+4 = 0x94 blocksize, return 0xa0; 这个a0就是 -flt s a0 的尺寸由来,下面图示中有A0尺寸。
void * __cdecl _heap_alloc_base (size_t size) { #ifdef WINHEAP void * pvReturn; #else /* WINHEAP */ _PBLKDESC pdesc; _PBLKDESC pdesc2; #endif /* WINHEAP */ #ifdef WINHEAP if ( __active_heap == __V6_HEAP ) { if ( size <= __sbh_threshold ) { #ifdef _MT _mlock( _HEAP_LOCK ); __try { #endif /* _MT */ pvReturn = __sbh_alloc_block(size); #ifdef _MT } __finally { _munlock( _HEAP_LOCK ); } #endif /* _MT */ if (pvReturn) return pvReturn; } } else if ( __active_heap == __V5_HEAP ) { /* round up to the nearest paragraph */ if ( size ) size = (size + _OLD_PARASIZE - 1) & ~(_OLD_PARASIZE - 1); else size = _OLD_PARASIZE; if ( size <= __old_sbh_threshold ) { #ifdef _MT _mlock(_HEAP_LOCK); __try { #endif /* _MT */ pvReturn = __old_sbh_alloc_block(size >> _OLD_PARASHIFT); #ifdef _MT } __finally { _munlock(_HEAP_LOCK); } #endif /* _MT */ if ( pvReturn != NULL ) return pvReturn; } return HeapAlloc( _crtheap, 0, size ); } if (size == 0) size = 1; size = (size + BYTES_PER_PARA - 1) & ~(BYTES_PER_PARA - 1); return HeapAlloc(_crtheap, 0, size); } #else /* WINHEAP */ /* try to find a big enough free block */ if ( (pdesc = _heap_search(size)) == NULL ) { if ( _heap_grow(size) != -1 ) { /* try finding a big enough free block again. the * success of the call to _heap_grow should guarantee * it, but... */ if ( (pdesc = _heap_search(size)) == NULL ) { /* something unexpected, and very bad, has * happened. abort! */ _heap_abort(); } } else return NULL; } /* carve the block into two pieces (if necessary). the first piece * shall be of the exact requested size, marked inuse and returned to * the caller. the leftover piece is to be marked free. */ if ( _BLKSIZE(pdesc) != size ) { /* split up the block and free the leftover piece back to * the heap */ if ( (pdesc2 = _heap_split_block(pdesc, size)) != NULL ) _SET_FREE(pdesc2); } /* mark pdesc inuse */ _SET_INUSE(pdesc); /* check proverdesc and reset, if necessary */ _heap_desc.proverdesc = pdesc->pnextdesc; return( (void *)((char *)_ADDRESS(pdesc) + _HDRSIZE) ); }
/*** *winheap.h - Private include file for winheap directory. * * Copyright (c) 1988-1998, Microsoft Corporation. All rights reserved. * *Purpose: * Contains information needed by the C library heap code. * * [Internal] * ****/ #if _MSC_VER > 1000 #pragma once #endif /* _MSC_VER > 1000 */ #ifndef _INC_WINHEAP #define _INC_WINHEAP #ifndef _CRTBLD /* * This is an internal C runtime header file. It is used when building * the C runtimes only. It is not to be used as a public header file. */ #error ERROR: Use of C runtime library internal header file. #endif /* _CRTBLD */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #include <windows.h> // Declarations and definitions for the multiple heap scheme (VC++ 6.1) // Heap-selection constants #define __SYSTEM_HEAP 1 #define __V5_HEAP 2 #define __V6_HEAP 3 #define __HEAP_ENV_STRING "__MSVCRT_HEAP_SELECT" #define __GLOBAL_HEAP_SELECTOR "__GLOBAL_HEAP_SELECTED" // Heap-selection global variable extern int __active_heap; // Linker info for heap selection typedef struct { union { DWORD dw; struct { BYTE bverMajor; BYTE bverMinor; }; }; } LinkerVersion; extern void __cdecl _GetLinkerVersion(LinkerVersion * plv); // Definitions, declarations and prototypes for the small-block heap (VC++ 6.0) #define BYTES_PER_PARA 16 #define DWORDS_PER_PARA 4 #define PARAS_PER_PAGE 256 // tunable value #define PAGES_PER_GROUP 8 // tunable value #define GROUPS_PER_REGION 32 // tunable value (max 32) #define BYTES_PER_PAGE (BYTES_PER_PARA * PARAS_PER_PAGE) #define BYTES_PER_GROUP (BYTES_PER_PAGE * PAGES_PER_GROUP) #define BYTES_PER_REGION (BYTES_PER_GROUP * GROUPS_PER_REGION) #define ENTRY_OFFSET 0x0000000cL // offset of entry in para #define OVERHEAD_PER_PAGE 0x00000010L // sixteen bytes of overhead #define MAX_FREE_ENTRY_SIZE (BYTES_PER_PAGE - OVERHEAD_PER_PAGE) #define BITV_COMMIT_INIT (((1 << GROUPS_PER_REGION) - 1) << \ (32 - GROUPS_PER_REGION)) #define MAX_ALLOC_DATA_SIZE 0x3f8 #define MAX_ALLOC_ENTRY_SIZE (MAX_ALLOC_DATA_SIZE + 0x8) typedef unsigned int BITVEC; typedef struct tagListHead { struct tagEntry * pEntryNext; struct tagEntry * pEntryPrev; } LISTHEAD, *PLISTHEAD; typedef struct tagEntry { int sizeFront; struct tagEntry * pEntryNext; struct tagEntry * pEntryPrev; } ENTRY, *PENTRY; typedef struct tagEntryEnd { int sizeBack; } ENTRYEND, *PENTRYEND; typedef struct tagGroup { int cntEntries; struct tagListHead listHead[64]; } GROUP, *PGROUP; typedef struct tagRegion { int indGroupUse; char cntRegionSize[64]; BITVEC bitvGroupHi[GROUPS_PER_REGION]; BITVEC bitvGroupLo[GROUPS_PER_REGION]; struct tagGroup grpHeadList[GROUPS_PER_REGION]; } REGION, *PREGION; typedef struct tagHeader { BITVEC bitvEntryHi; BITVEC bitvEntryLo; BITVEC bitvCommit; void * pHeapData; struct tagRegion * pRegion; } HEADER, *PHEADER; extern HANDLE _crtheap; /* * Global variable declarations for the small-block heap. */ extern size_t __sbh_threshold; void * __cdecl _nh_malloc(size_t, int); void * __cdecl _heap_alloc(size_t); extern PHEADER __sbh_pHeaderList; // pointer to list start extern PHEADER __sbh_pHeaderScan; // pointer to list rover extern int __sbh_sizeHeaderList; // allocated size of list extern int __sbh_cntHeaderList; // count of entries defined extern PHEADER __sbh_pHeaderDefer; extern int __sbh_indGroupDefer; extern size_t __cdecl _get_sb_threshold(void); extern int __cdecl _set_sb_threshold(size_t); extern int __cdecl _heap_init(int); extern void __cdecl _heap_term(void); extern void * __cdecl _malloc_base(size_t); extern void * __cdecl _nh_malloc_base(size_t, int); extern void * __cdecl _heap_alloc_base(size_t); extern void __cdecl _free_base(void *); extern void * __cdecl _realloc_base(void *, size_t); extern void * __cdecl _expand_base(void *, size_t); extern void * __cdecl _calloc_base(size_t, size_t); extern size_t __cdecl _msize_base(void *); extern int __cdecl __sbh_heap_init(size_t); extern void * __cdecl __sbh_alloc_block(int); extern PHEADER __cdecl __sbh_alloc_new_region(void); extern int __cdecl __sbh_alloc_new_group(PHEADER); extern PHEADER __cdecl __sbh_find_block(void *); #ifdef _DEBUG extern int __cdecl __sbh_verify_block(PHEADER, void *); #endif /* _DEBUG */ extern void __cdecl __sbh_free_block(PHEADER, void *); extern int __cdecl __sbh_resize_block(PHEADER, void *, int); extern void __cdecl __sbh_heapmin(void); extern int __cdecl __sbh_heap_check(void); // Definitions, declarations and prototypes for the old small-block heap // (shipped with VC++ 5.0) #ifdef _M_ALPHA #define _OLD_PAGESIZE 0x2000 // one page #else /* _M_ALPHA */ #define _OLD_PAGESIZE 0x1000 // one page #endif /* _M_ALPHA */ // Constants and types used by the old small-block heap #define _OLD_PARASIZE 0x10 #define _OLD_PARASHIFT 0x4 #ifdef _M_ALPHA #define _OLD_PARAS_PER_PAGE 454 #define _OLD_PADDING_PER_PAGE 5 #define _OLD_PAGES_PER_REGION 512 #define _OLD_PAGES_PER_COMMITMENT 8 #else /* _M_ALPHA */ #define _OLD_PARAS_PER_PAGE 240 #define _OLD_PADDING_PER_PAGE 7 #define _OLD_PAGES_PER_REGION 1024 #define _OLD_PAGES_PER_COMMITMENT 16 #endif /* _M_ALPHA */ typedef char __old_para_t[16]; #ifdef _M_ALPHA typedef unsigned short __old_page_map_t; #else /* _M_ALPHA */ typedef unsigned char __old_page_map_t; #endif /* _M_ALPHA */ #define _OLD_FREE_PARA (__old_page_map_t)(0) #define _OLD_UNCOMMITTED_PAGE (-1) #define _OLD_NO_FAILED_ALLOC (size_t)(_OLD_PARAS_PER_PAGE + 1) // Small-block heap page. The first four fields of the structure below are // descriptor for the page. That is, they hold information about allocations // in the page. The last field (typed as an array of paragraphs) is the // allocation area. typedef struct __old_sbh_page_struct { __old_page_map_t * p_starting_alloc_map; size_t free_paras_at_start; __old_page_map_t alloc_map[_OLD_PARAS_PER_PAGE + 1]; __old_page_map_t reserved[_OLD_PADDING_PER_PAGE]; __old_para_t alloc_blocks[_OLD_PARAS_PER_PAGE]; } __old_sbh_page_t; #define _OLD_NO_PAGES (__old_sbh_page_t *)0xFFFFFFFF // Type used in small block region desciptor type (see below). typedef struct { int free_paras_in_page; size_t last_failed_alloc; } __old_region_map_t; // Small-block heap region descriptor. Most often, the small-block heap // consists of a single region, described by the statically allocated // decriptor __small_block_heap (declared below). struct __old_sbh_region_struct { struct __old_sbh_region_struct *p_next_region; struct __old_sbh_region_struct *p_prev_region; __old_region_map_t * p_starting_region_map; __old_region_map_t * p_first_uncommitted; __old_sbh_page_t * p_pages_begin; __old_sbh_page_t * p_pages_end; __old_region_map_t region_map[_OLD_PAGES_PER_REGION + 1]; }; typedef struct __old_sbh_region_struct __old_sbh_region_t; // Global variable declarations for the old small-block heap. extern __old_sbh_region_t __old_small_block_heap; extern size_t __old_sbh_threshold; // Prototypes for internal functions of the old small-block heap. void * __cdecl __old_sbh_alloc_block(size_t); void * __cdecl __old_sbh_alloc_block_from_page(__old_sbh_page_t *, size_t, size_t); void __cdecl __old_sbh_decommit_pages(int); __old_page_map_t * __cdecl __old_sbh_find_block(void *, __old_sbh_region_t **, __old_sbh_page_t **); void __cdecl __old_sbh_free_block(__old_sbh_region_t *, __old_sbh_page_t *, __old_page_map_t *); int __cdecl __old_sbh_heap_check(void); __old_sbh_region_t * __cdecl __old_sbh_new_region(void); void __cdecl __old_sbh_release_region(__old_sbh_region_t *); int __cdecl __old_sbh_resize_block(__old_sbh_region_t *, __old_sbh_page_t *, __old_page_map_t *, size_t); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* _INC_WINHEAP */
dataSize = 0x70
flt size = a0, address:2255e70
//vc98/crt/src/dbgint.h #define nNoMansLandSize 4 typedef struct _CrtMemBlockHeader { struct _CrtMemBlockHeader * pBlockHeaderNext; struct _CrtMemBlockHeader * pBlockHeaderPrev; char * szFileName; int nLine; size_t nDataSize; int nBlockUse; long lRequest; unsigned char gap[nNoMansLandSize]; /* followed by: * unsigned char data[nDataSize]; * unsigned char anotherGap[nNoMansLandSize]; */ } _CrtMemBlockHeader; #define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1)) //类型指针+1 == 地址+32 直接指向_ListRowInfo #define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
0:000> dt _CrtMemBlockHeader 02255e70 ...
MSVCRTD!_CrtMemBlockHeader
+0x000 pBlockHeaderNext :
+0x004 pBlockHeaderPrev :
+0x008 szFileName :
+0x00c nLine : 0n73
+0x010 nDataSize : 0x70
+0x014 nBlockUse : 0n1
+0x018 lRequest : 0n87
+0x01c gap : [4] "???"
0:000> ? 02255e90 - 02255e70
Evaluate expression: 32 = 00000020
0:000> ?? sizeof(_LISTROWINFO)
unsigned int 0x70
0:000> ? 02255e90 + 70
Evaluate expression: 36003584 = 02255f00
? 02255e70 +A0
Evaluate expression: 36003600 = 02255f10
02255e68 2cdc0c79
02255e6c 180071c5
02255e70 02255de8
02255e74 02255f28
02255e78 004176a8 TestMFC!THIS_FILE
02255e7c 00000049
02255e80 00000070
02255e84 00000001
02255e88 00000057
02255e8c fdfdfdfd
02255e90 00416540 TestMFC!_LISTROWINFO::`vftable'
02255e94 cdcdcdcd
02255e98 cdcdcdcd
02255e9c 5f4aef8c MFC42D!CStringArray::`vftable'
02255ea0 00000000
02255ea4 00000000
02255ea8 00000000
02255eac 00000000
02255eb0 5f4d0b14 MFC42D!_afxInitData+0xc
02255eb4 cdcdcdcd
02255eb8 cdcdcdcd
02255ebc cdcdcdcd
02255ec0 cdcdcdcd
02255ec4 cdcdcdcd
02255ec8 5f4d0b14 MFC42D!_afxInitData+0xc
02255ecc 02255f54
02255ed0 cdcdcdcd
02255ed4 cdcdcdcd
02255ed8 00000000
02255edc 00000000
02255ee0 00000000
02255ee4 cdcdcdcd
02255ee8 cdcdcdcd
02255eec cdcdcdcd
02255ef0 cdcdcdcd
02255ef4 cdcdcdcd
02255ef8 00000064
02255efc 5f4d0b14 MFC42D!_afxInitData+0xc
02255f00 fdfdfdfd
02255f04 baadf00d
02255f08 baadf00d
02255f0c baadf00d
02255f10 abababab
02255f14 abababab
02255f18 00000000
02255f1c 00000000
02255f20 30dc0c65
02255f24 180071c3
02255f28 02255e70
02255f2c 02251390
02255f30 5f4d0b18 MFC42D!THIS_FILE
Release版:直接就是0x70没有附加内存
0:000> !heap -s NtGlobalFlag enables following debugging aids for new heaps: tail checking free checking validate parameters LFH Key : 0x8ebb7717 Termination on corruption : DISABLED Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast (k) (k) (k) (k) length blocks cont. heap ----------------------------------------------------------------------------- 004d0000 40000062 1020 456 1020 105 50 1 0 0 00820000 40001062 3124 2196 3124 5 12 3 0 0 02470000 40001062 60 16 60 2 12 1 0 0 02440000 40001062 60 20 60 6 2 1 0 0 ----------------------------------------------------------------------------- 0:000> !heap -stat -h 00820000 heap @ 00820000 group-by: TOTSIZE max-display: 20 size #blocks total ( %) (percent of total busy bytes) 70 2711 - 111770 (56.23) 1404 9d - c4674 (40.39)
0:000> !heap -flt s70 _HEAP @ 4d0000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 0051d158 0011 0011 [00] 0051d160 00070 - (busy) 0051dc28 0011 0011 [00] 0051dc30 00070 - (busy) 00520448 0011 0011 [00] 00520450 00070 - (busy) 005270f0 0011 0011 [00] 005270f8 00070 - (busy) _HEAP @ 820000 00821af8 0011 0011 [00] 00821b00 00070 - (busy) TestMFC!_LISTROWINFO::`vftable' 00821b80 0011 0011 [00] 00821b88 00070 - (busy) TestMFC!_LISTROWINFO::`vftable' 00821c08 0011 0011 [00] 00821c10 00070 - (busy)
->? 00821b00 +70 Evaluate expression: 8526704 = 00821b70 ->dds 00821b00 00821b00 00403728 TestMFC!_LISTROWINFO::`vftable' 00821b04 baadf00d 00821b08 baadf00d 00821b0c 751728e8 MFC42!CStringArray::`vftable' 00821b10 00000000 00821b14 00000000 00821b18 00000000 00821b1c 00000000 00821b20 7526fb24 MFC42!_afxInitData+0xc 00821b24 baadf00d 00821b28 baadf00d 00821b2c baadf00d 00821b30 baadf00d 00821b34 baadf00d 00821b38 7526fb24 MFC42!_afxInitData+0xc 00821b3c 00824ff8 //stroldtext 成员; da 00824ff8 -> '...abc' 00821b40 baadf00d 00821b44 baadf00d 00821b48 00000000 00821b4c 00000000 00821b50 00000000 00821b54 baadf00d 00821b58 baadf00d 00821b5c baadf00d 00821b60 baadf00d 00821b64 baadf00d 00821b68 00000064 //100 00821b6c 7526fb24 MFC42!_afxInitData+0xc //最后一个CString成员 00821b70 abababab 00821b74 abababab 00821b78 00000000 00821b7c 00000000
下面是网上搜到的相关问题,它认为CString有bug,也是人之常情。
VC++ 6.0中CArray中保存大量对象时内存无法释放的问题,请各位指点 ![RRS feed](https://i1.social.s-msft.com/globalresources/Images/trans.gif?cver=0001)
南来地,北往的,上班的,下岗的,走过路过不要错过!
======================个性签名=====================
之前认为Apple 的iOS 设计的要比 Android 稳定,我错了吗?
下载的许多客户端程序/游戏程序,经常会Crash,是程序写的不好(内存泄漏?刚启动也会吗?)还是iOS本身的不稳定!!!
如果在Android手机中可以简单联接到ddms,就可以查看系统log,很容易看到程序为什么出错,在iPhone中如何得知呢?试试Organizer吧,分析一下Device logs,也许有用.