SQLite 3没有在Golang中释放内存

Ali*_*xel 7 memory sqlite memory-leaks out-of-memory go

我有问题让Go与SQLite很好地玩,我过去做过没有问题,但它已经有一段时间了,我不记得我做了什么让它正常工作.我正在使用Go和mattn/go-sqlite3软件包一起处理并将大量数据插入到SQLite数据库中,但不知怎的,Go总是最终吃掉我所有的RAM,直到最后退出时出现错误状态代码.为了确保我已经将内存饥饿问题隔离到SQLite,我编写了以下简单程序来测试它:

package main

import (
    "database/sql"
    "fmt"
    "log"
    _ "github.com/mattn/go-sqlite3"
)

func main() {
    db, err := sql.Open("sqlite3", "./test.db"); if err != nil {
        log.Fatal(err)
    }; defer db.Close()

    ddl := `
        PRAGMA automatic_index = ON;
        PRAGMA cache_size = 32768;
        PRAGMA cache_spill = OFF;
        PRAGMA foreign_keys = ON;
        PRAGMA journal_size_limit = 67110000;
        PRAGMA locking_mode = NORMAL;
        PRAGMA page_size = 4096;
        PRAGMA recursive_triggers = ON;
        PRAGMA secure_delete = ON;
        PRAGMA synchronous = NORMAL;
        PRAGMA temp_store = MEMORY;
        PRAGMA journal_mode = WAL;
        PRAGMA wal_autocheckpoint = 16384;

        CREATE TABLE IF NOT EXISTS "user" (
            "id" TEXT,
            "username" TEXT,
            "password" TEXT
        );

        CREATE UNIQUE INDEX IF NOT EXISTS "id" ON "user" ("id");
    `

    _, err = db.Exec(ddl); if err != nil {
        log.Fatal(err)
    }

    queries := map[string]*sql.Stmt{}

    queries["user"], _ = db.Prepare(`INSERT OR REPLACE INTO "user" VALUES (?, ?, ?);`); if err != nil {
        log.Fatal(err)
    }; defer queries["user"].Close()

    tx, err := db.Begin(); if err != nil {
        log.Fatal(err)
    }

    for i := 0; i < 10000000; i++ {
        user := map[string]string{
            "id": string(i),
            "username": "foo",
            "password": "bar",
        }

        _, err := tx.Stmt(queries["user"]).Exec(user["id"], user["username"], user["password"]); if err != nil {
            log.Fatal(err)
        }

        if i % 32768 == 0 {
            tx.Commit()
            db.Exec(`PRAGMA shrink_memory;`)

            tx, err = db.Begin(); if err != nil {
                log.Fatal(err)
            }

            fmt.Println(i)
        }
    }

    tx.Commit()
}
Run Code Online (Sandbox Code Playgroud)

当我运行上面的代码,围棋吃超过100 MIB的记忆每一秒每没有任何释放,一分钟后,或因此结束了耗时6/7吉布然后程序就会被杀死.我已经尝试了有和没有定义SQLite PRAGMA的变体,但没有运气.

根据定义的PRAGMA,SQLite永远不应该使用超过128 MiB的RAM.

我是否犯了任何错误或者使用mattn/go-sqlite3或Go GC有什么问题?


与剖析davecheney/profile按照这些指令产生这种不那么有用的输出:

alix@900X4C:~/Go/src$ go tool pprof --text ./test /tmp/profile102098478/mem.pprof
Adjusting heap profiles for 1-in-4096 sampling rate
Total: 0.0 MB
     0.0 100.0% 100.0%      0.0 100.0% runtime.allocm
     0.0   0.0% 100.0%      0.0 100.0% database/sql.(*DB).Exec
     0.0   0.0% 100.0%      0.0 100.0% database/sql.(*DB).conn
     0.0   0.0% 100.0%      0.0 100.0% database/sql.(*DB).exec
     0.0   0.0% 100.0%      0.0 100.0% github.com/mattn/go-sqlite3.(*SQLiteDriver).Open
     0.0   0.0% 100.0%      0.0 100.0% github.com/mattn/go-sqlite3._Cfunc_sqlite3_threadsafe
     0.0   0.0% 100.0%      0.0 100.0% main.main
     0.0   0.0% 100.0%      0.0 100.0% runtime.cgocall
     0.0   0.0% 100.0%      0.0 100.0% runtime.gosched0
     0.0   0.0% 100.0%      0.0 100.0% runtime.main
     0.0   0.0% 100.0%      0.0 100.0% runtime.newextram
Run Code Online (Sandbox Code Playgroud)

这只是1000000次迭代,内存仍然像没有明天一样增长.

我还在两个MacBook Pro上尝试了相同的代码,两个都运行最新版本的Go from brew(1.3.1),其中一个内存使用量超过10 GiB,另一个平均为2 GiB的RAM消耗.这看起来像一个奇怪的行为,我可以做些什么来追查差异并修复内存占用?

pet*_*rSO 6

我无法重现您的结果.它使用大约100 MiB的内存.

$ go version
go version devel +7ab3adc146c9 Sun Oct 19 10:33:50 2014 -0700 linux/amd64
$ sqlite3 --version
3.8.2 2013-12-06 14:53:30 27392118af4c38c5203a04b8013e1afdb1cebd0d
$ go get -v github.com/mattn/go-sqlite3
github.com/mattn/go-sqlite3 (download)
github.com/mattn/go-sqlite3
$ go run simple.go
0
32768
65536
<SNIP>
9928704
9961472
9994240
$
Run Code Online (Sandbox Code Playgroud)

runtime.MemStats记录有关Go内存分配器的统计信息.它不包括SQLite管理的内存.例如,在程序结束时,

var ms runtime.MemStats
runtime.ReadMemStats(&ms)
fmt.Println(
    ms.Alloc,      // bytes allocated and still in use
    ms.TotalAlloc, // bytes allocated (even if freed)
    ms.Sys,        // bytes obtained from system (sum of XxxSys below)
    ms.Mallocs,    // number of mallocs
    ms.Frees,      // number of frees
)
Run Code Online (Sandbox Code Playgroud)

输出:

12161440 7953059928 18757880 160014535 159826250
Run Code Online (Sandbox Code Playgroud)

它也适用于Go 1.4 Beta 1

$ go version
go version go1.4beta1 linux/amd64