You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让SQLite启动时预分配内存?MEMSYS5实测异常排查

问题

我正在开发一个使用SQLite的嵌入式程序,希望实现可预测的内存使用。我了解到SQLite的「Zero-malloc内存分配器」(MEMSYS5)可以让SQLite在启动时分配固定量的内存并独占使用,而非运行时动态调用malloc。

我在Ubuntu桌面环境中编译了带有-DSQLITE_ENABLE_MEMSYS5参数的最新版SQLite,编写了示例程序让SQLite预分配1GB内存后休眠15秒,同时用测试脚本通过free命令查看内存变化,但free输出中并未体现出预分配的内存,程序也没有任何错误返回。

我的测试脚本:

#!/bin/bash
set -o verbose
free -b
./testsqlite3 &
sleep 3
ps aux | grep testsqlite3
free -b

脚本输出:

$ ./runtest.sh 

free -b
              total        used        free      shared  buff/cache   available
Mem:     8421400576   607322112  6244298752    28102656  1569779712  7066603520
Swap:             0           0           0

./testsqlite3 &
sleep 3
Using libsqlite version 3.40.0, sqlite3.h is version 3.40.0
Preallocating using MEMSYS5
Opened database successfully
Table created successfully
Sleeping for 15 secs before exiting. Call 'free -b' now.

echo "bash sleep complete, confirming app still running"
bash sleep complete, confirming app still running
ps aux | grep testsqlite3
user1     7474  0.0  0.0 979960  2380 pts/1    S+   12:11   0:00 ./testsqlite3
user1     7477  0.0  0.0   5116   880 pts/1    S+   12:11   0:00 grep testsqlite3

free -b
              total        used        free      shared  buff/cache   available
Mem:     8421400576   607420416  6244200448    28102656  1569779712  7066505216
Swap:             0           0           0

示例程序testsqlite3:

#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <unistd.h>

static int callback(void *NotUsed, int argc, char **argv, char **azColName) {
    int i;
    for(i = 0; i<argc; i++) {
        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
    return 0;
}

int main(int argc, char* argv[]) {
    sqlite3 *db;
    char *zErrMsg = 0;
    int rc;
    char *sql;

    fprintf(stdout, "Using libsqlite version %s, sqlite3.h is version %s\n", sqlite3_libversion(), SQLITE_VERSION);


    fprintf(stdout, "Preallocating using MEMSYS5\n");
    //int preallocateMemoryBytes = 100000000; //100MB
    int preallocateMemoryBytes =   1000000000; //1GB

    rc = sqlite3_config(SQLITE_CONFIG_HEAP,
                        malloc(preallocateMemoryBytes),
                        preallocateMemoryBytes, 32);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "failed to config SQLITE_CONFIG_HEAP\n");
        return(-1);
    }

    rc = sqlite3_initialize();
    if (rc != SQLITE_OK) {
        fprintf(stderr, "initialize() failed\n");
        return(-1);
    }


    //open DB
    rc = sqlite3_open("test.db", &db);

    if( rc ) {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
        return(-1);
    } else {
        fprintf(stdout, "Opened database successfully\n");
    }


    sql = "CREATE TABLE IF NOT EXISTS COMPANY("  \
          "ID INT PRIMARY KEY     NOT NULL," \
          "NAME           TEXT    NOT NULL," \
          "AGE            INT     NOT NULL," \
          "ADDRESS        CHAR(50)," \
          "SALARY         REAL );";

    rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);

    if( rc != SQLITE_OK ){
        fprintf(stderr, "SQL error: %s\n", zErrMsg);
        sqlite3_free(zErrMsg);
        return(-1);
    } else {
        fprintf(stdout, "Table created successfully\n");
    }


    fprintf(stdout, "Sleeping for 15 secs before exiting. Call 'free -b' now.\n");
    usleep(15000 * 1000);

    sqlite3_close(db);
    return 0;
}
解决方案

问题出在两个核心点:

1. Linux下malloc的延迟分配机制

用malloc分配1GB内存时,Linux内核仅为进程预留虚拟地址空间,不会立即分配对应的物理内存。只有当进程实际写入这块内存时,内核才会真正分配物理页。这就是free命令看不到内存占用变化的根本原因——物理内存还未被实际使用。

2. SQLite MEMSYS5的内存使用逻辑

即使通过SQLITE_CONFIG_HEAP给SQLite指定了堆内存,MEMSYS5也不会立即占用整个堆,而是在SQLite执行实际操作(如创建大表、插入数据)时才从堆中分配小块内存。但你的需求是启动时立即预留物理内存,因此需要主动触发物理内存分配。

具体解决方法:

方法一:手动写入预分配内存块,触发物理内存分配

在调用sqlite3_config之前,对malloc得到的内存块进行写操作,比如用memset填充整个块:

void *heap = malloc(preallocateMemoryBytes);
if (!heap) {
    fprintf(stderr, "malloc failed\n");
    return -1;
}
// 填充内存,触发物理页分配
memset(heap, 0, preallocateMemoryBytes);

rc = sqlite3_config(SQLITE_CONFIG_HEAP, heap, preallocateMemoryBytes, 32);

方法二:用mmap代替malloc,强制立即分配物理内存

使用mmap并指定MAP_POPULATE标志,让内核立即分配物理内存:

#include <sys/mman.h>
// ...
void *heap = mmap(NULL, preallocateMemoryBytes, PROT_READ | PROT_WRITE,
                  MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
if (heap == MAP_FAILED) {
    fprintf(stderr, "mmap failed\n");
    return -1;
}

rc = sqlite3_config(SQLITE_CONFIG_HEAP, heap, preallocateMemoryBytes, 32);

注意:使用mmap分配的内存,最后需要调用munmap手动释放(SQLite不会自动释放你传入的堆内存)。

验证效果

修改后重新编译运行测试脚本,free命令的used列会明显增加1GB左右,available列对应减少,说明物理内存已经被成功预留。

内容的提问来源于stack exchange,提问作者jerkstorecalled

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 19:31:12