大约3到4年前,我刚刚开始对BREW进行深入的了解和开发。面对的都是C代码,里面充斥着MALLOC和FREE的调用,难道他们就不会封装一下吗?问题在于,程序可能不是自己写的,嗯,程序的某个地方,在某个时机可能会产生内存泄露;程序在退出时,系统不会自动释放占用的内存吗?答案是NO,直到关机之前,BREW都不会帮你释放,包括最新的BREW 4.0系统。而kddi对此也进行了非常严格的重视,程序进入之前剩余内存和程序退出之后剩余内存误差在5KB之内。系统本身会造成一些内存浪费(碎片),即使在这种情况下,也要求误差5KB。想象一下,查找一个地方的内存泄露是多么的麻烦。因此,换个角度,是否让程序来帮助我们查找,当然,答案是肯定的,不然,我就是在骗人了。
注意到一个事情,BREW中对于函数的调用,基本上都是通过宏来调用的,因此,需要使用一种方法智能的屏蔽掉宏,在合适的时候打开宏。闲话少说,代码上来:
memorylog.h:
#ifndef _H_MEMORYLOG
#define _H_MEMORYLOG
/*!
* @struct MEMSTRUCT
* @brief 内存检测用来记录内存的结构
*
* @author --==RIX==--
* @date 2007年08月14日
*/
typedef struct _MEMSTRUCT{
void* addr;//!< 分配的内存地址
int length;//!< 分配的内存征长度
int count;//!< 分配时的分配索引值
} MEMSTRUCT;
/*!
* @brief 内存记录
*
* @author --==RIX==--
* @date 2007年08月14日
* @warning
*
* 内存检测仅仅可以在wins平台使用,ARM平台不可以使用
*/
struct MEMORYLOG
{
MEMSTRUCT** imem; //!< 已经记录的内存结构
int num;//!< 目前已经记录的内存记录数目
int maxnew;//!< 总共分配的内存次数
int maxdelete;//!< 总共删除的内存次数
};
extern void MEMORYLOG_INIT();
extern void MEMORYLOG_DESTROY();
extern void* myMalloc(int size);
extern void myFree(void* p);
#if MALLOC!=myMalloc
#define MALLOC(size) myMalloc(size)
#endif
#if FREE!=myAlloc
#define FREE(p) myFree(p)
#endif
#endif
MemoryLog.c:
#include "MemoryLog.h"
struct MEMORYLOG ilog;
void* myMalloc(int size)
{
#if MALLOC==myMalloc
#define MALLOC(size) GET_HELPER()->malloc((size))
#endif
return MALLOC(size);
#if MALLOC!=myMalloc
#define MALLOC(size) myMalloc(size)
#endif
}
void myFree(void* p)
{
#if FREE==myFree
#define FREE(p) GET_HELPER()->free((p))
#endif
return FREE(p);
#if FREE!=myFree
#define FREE(p) myFree(p)
#endif
}
void MEMORYLOG_INIT()
{
ilog.imem = NULL;
ilog.num = 0;
ilog.maxnew = 0;
ilog.maxdelete = 0;
}
/*!
* @brief 添加一个记录结构
*
* @param amem 欲添加的记录结构指针
* @return 无
*/
void MEMORYLOG_Add( MEMSTRUCT* amem )
{
MEMSTRUCT** temp = (MEMSTRUCT**)myMalloc( sizeof( MEMSTRUCT* ) * ( ilog.num + 1 ) );
for( int i = 0;i < ilog.num; i++ )
temp[i] = ilog.imem[i];
ilog.temp[num] = amem;
myFree( ilog.imem );
ilog.imem = temp;
if( ilog.maxnew == 6 )
int k = 10;
ilog.imem[num]->count = ilog.maxnew;
ilog.num++;
ilog.maxnew++;
}
/*!
* @brief 从内存记录中删除一个记录
*
* @param aindex 欲删除的记录索引
* @return 无
*/
void MEMORYLOG_SubNode( int aindex )
{
MEMSTRUCT** temp = (MEMSTRUCT**)myMalloc( sizeof( MEMSTRUCT* ) * ( ilog.num - 1 ) );
for( int i = 0;i < ilog.num; i++ )
if( i != aindex )
temp[i] = ilog.imem[i];
else
break;
myFree( ilog.imem[i] );
for( i = i+1; i < ilog.num; i++ )
temp[i-1] = ilog.imem[i];
myFree( ilog.imem );
ilog.imem = temp;
ilog.num--;
ilog.maxdelete--;
}
/*!
* @fn MEMORYLOG_Sub( void* aaddr )
* @brief 从内存记录中删除一个记录
*
* @param aaddr 分配了的内存地址
* @return 无
*/
void MEMORYLOG_Sub( void* aaddr )
{
int i = 0;
for( i = 0; i < ilog.num; i++ )
{
if( ilog.imem[i]->addr == aaddr )
break;
}
if( i == ilog.num )
{
return;
}
MEMORYLOG_SubNode( i );
}
void MEMORYLOG_DESTROY()
{
if( ilog.num != 0 )
{
if( ilog.num > 0 )
{
for( int i = 0;i < ilog.num; i++ )
{
char temp[255];
SNPRINTF(temp, sizeof(temp), "memory leak addr = %x, size = %d, count = %d", ilog.imem[i]->addr, ilog.imem[i]->length, ilog.imem[i]->count );
DBGPRINTF( temp );
}
}
}
char temp[255];
SNPRINTF(temp, sizeof(temp), "memory resulet new count = %d, delete count = %d", ilog.maxnew, ilog.maxdelete );
DBGPRINTF( temp );
for( int i = 0; i < ilog.num; i++ )
myFree(ilog.imem[i]);
myFree( ilog.imem );
}
使用的时候,在初始化的地方调用MEMORYLOG_INIT,一般在InitAppData函数中,在最终退出的时候,调用MEMORYLOG_DESTROY,一般在FreeAppData。如果有内存泄露,在BREW Log中将会打印出memory leak addr=....的字样,重要的是count值,这个表示是第几次分配,修改if( ilog.maxnew == 6 )中的数字为count值,然后下断点,当进行到指定次数的内存分配的时候,就会中断的。
理论上来讲,上面的代码是可以正常工作的。嗯,是的,理论上,因为我从开始做BREW的时候,就决定使用C++的方法,上面的方法,是在帮助别人的时候写的,而代码,则是从C++的代码中修改过来的。顺利的话,过两天会说C++的内存检测,将会全面些。
内存检测只能在模拟器上运行,因为ARM版本不允许使用全局变量,以后可能会提到打破这一限制或者变通的方法的。
不懂编程~!
Couldnt agree more with that, very attractive article