Sqlite3内存调试

Posted 林多

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Sqlite3内存调试相关的知识,希望对你有一定的参考价值。

Sqlite3内存调式

  • 基本上来说Sliqte3提供的接口,只要正确操作,是不会发生内存泄漏的问题。它有一套严格的内存理论,以及实现机制。这里主要讨论,如何进行内存方面的调式、确认。

Sqlite3动态内存

  • 查看sqllite3当前持有的动态内存大小(byte)。该接口通过获取SQLITE_STATUS_MEMORY_USED状态值实现。大概原理为,sqlite3在申请或释放动态内存时,会将SQLITE_STATUS_MEMORY_USED对应的状态值做加或减去分配的bytpe的操作。
// 接口
sqlite3_int64 sqlite3_memory_used(void);

// 接口的实现
// 该接口会返回sqlite3持有的动态分配的内存大小。
sqlite3_int64 sqlite3_memory_used(void)
  int n, mx;
  sqlite3_int64 res;
  sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, 0);
  res = (sqlite3_int64)n;  /* Work around bug in Borland C. Ticket #3216 */
  return res;


// sqlite3内部实现逻辑
/*
** Free memory previously obtained from sqlite3Malloc().
*/
void sqlite3_free(void *p)
 	// 省略
 	// 记录释放的内存字节数(负数)
   sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -     sqlite3MallocSize(p));



/*
** Do a memory allocation with statistics and alarms.  Assume the
** lock is already held.
*/
static int mallocWithAlarm(int n, void **pp)
     // 记录申请的内存szie(正数)
    nFull = sqlite3MallocSize(p);
    sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nFull);

  • 获取sqlite3内部状态接口,第一个in参数表示状态类型,第二个out参数表示状态的当前值,第三个output参数为状态记录的最高值,第四个参数为ture时表示该函数后清空记录最高值。
int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
  • 可用的状态值
// 当前内存
#define SQLITE_STATUS_MEMORY_USED          0
// 页内存
#define SQLITE_STATUS_PAGECACHE_USED       1
#define SQLITE_STATUS_PAGECACHE_OVERFLOW   2
#define SQLITE_STATUS_SCRATCH_USED         3
#define SQLITE_STATUS_SCRATCH_OVERFLOW     4
#define SQLITE_STATUS_MALLOC_SIZE          5
#define SQLITE_STATUS_PARSER_STACK         6
#define SQLITE_STATUS_PAGECACHE_SIZE       7
#define SQLITE_STATUS_SCRATCH_SIZE         8
// 执行过malloc的数量(malloc时+1,free时-1)
#define SQLITE_STATUS_MALLOC_COUNT         9
  • 修改sqlite代码,检查是否存在内存泄漏。修改sqlite3StatusAdd函数,让其在执行时,输出分配的内存的size。同时修改sqlite3MemSizesqlite3MemFree函数,让其输出指针的地址。例:

// 输出内存增加/释放的size
void sqlite3StatusAdd(int op, int N)
  if (SQLITE_STATUS_MEMORY_USED == op) 
    printf("sqlite3StatusAdd memory change [%d]", N);
  
  // ....


// 一般来讲,内存分配、释放都会调用该函数,或者size,然后使用sqlite3StatusAdd记录。所以可以加在该函数中。
static int sqlite3MemSize(void *pPrior)
#ifdef SQLITE_MALLOCSIZE
  return pPrior ? (int)SQLITE_MALLOCSIZE(pPrior) : 0;
#else
  sqlite3_int64 *p;
  if( pPrior==0 ) return 0;
  p = (sqlite3_int64*)pPrior;
  p--;
  printf("sqlite3MemSize buffer addr[%p]", p);
  return (int)p[0];
#endif


// 看一下释放的内存的地址。
static void sqlite3MemFree(void *pPrior)
#ifdef SQLITE_MALLOCSIZE
  SQLITE_FREE(pPrior);
#else
  sqlite3_int64 *p = (sqlite3_int64*)pPrior;
  assert( pPrior!=0 );
  p--;
  printf("sqlite3MemFree addr[%p]", p);
  SQLITE_FREE(p);
#endif

  • 通过在源码中,追加输出信息,可以更直接的观察sqlite分配的内存信息。

  • 另外,也可以使用varglind进行内存调式。

关于Sqlite3内存无法释放的问题

  • 近期遇到了一个Sqlite3内存无法释放的问题,通过sqlite3_finalize函数释放后,查看进程的内存仍然没有降低。关于这个问题,通过分析后,最终发现sqlite3对于所有动态申请的内存,都调用了free操作。而问题的真因在于,运行进程的 系统上,对于malloc的内存,free后不会立即释放导致(关于该原因及解决方法,网上有一些文章,可自行百度)。

以上是关于Sqlite3内存调试的主要内容,如果未能解决你的问题,请参考以下文章

sqlite3调试

调试快速应用程序中的内存泄漏

sqlite3 是不是在使用之前将整个数据库读入内存?

如何创建 sqlite3 内存数据库?

sqlite只有几条数据内存很大

使用内存选项创建 Sqlite3 数据库