V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ShikiSuen
V2EX  ›  程序员

跑 SQLite 查询不及时使用 sqlite3_finalize() 的严重后果,望周知。

  •  
  •   ShikiSuen · 150 天前 · 916 次点击
    这是一个创建于 150 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本事件已被我投稿至纯靠北工程师(「#純靠北工程師 7ib 」): https://www.facebook.com/init.kobeengineer/posts/pfbid02hDLMenCJsnpQw59AcsJdFeiu7nFcKqnbqT71SFuY16ZVsWF6S8VC9zz82UGsFKX3l

    教训是:用 SQLite 跑完「每一笔」查询之后一定要「立刻」用 sqlite3_finalize(StatementPointer) 释放运存,不然会产生连 Xcode Instruments 都抓不到的运存泄漏。

    事情是这样:

    为了因应 Apple 对 8GB 运存的病态执着, 我最近刚刚给威注音输入法的原厂词库格式由 JSON 升级至 SQLite 。

    我是第一次用 SQLite ,看这篇学的(威注音是用 Swift 开发的): https://itisjoe.gitbooks.io/swiftgo/content/database/sqlite.html 现在 Swift 用 SQLite 超方便,直接 import SQLite3 就行。 ( Linux 或者旧版 macOS 还可以直接读 SQLite3 的 almagation 。) 只是,窃以为我犯的最大错误就是「没有针对相关的 SQLite API 详读 SQLite 官方的手册」。

    我一开始以为让所有的 SQLite Statement 共用一个(作为静态变数的) nullable OpaquePointer 就可以了, 于是我一开始的设计就是让辞典模组在 deinit() 的时候才跑一次「 sqlite3_finalize()」。 因为原厂词库是只读的、在 App Bundle 内,所以我将 journal 塞在了运存内。

    结果呢?施工完毕之后,单元测试啥的都成功通过了,刚刚开启输入法时的运存占用是 18MB 左右。 当时我感觉很开心,因为这几乎是直接消灭了输入法启动时花在原厂辞典上的载入时间。 但是,周一上午,我用这输入法没打六分钟的字,输入法碰到了我之前设定的保险丝而自尽了: 我早前做了这么个保险设计:只要运存占用超过 768MB ,输入法就自尽、自动重新启动来释放运存。

    然后,我用 Xcode 14.2 的 Instruments 抓运存泄漏, 但只抓到一个与系统输入法选单有关的小微型泄漏(还是无法解决的那种)。 正好我周一那天公司休假,我花了一整个周一的时间排查自己做过的所有修改, 最后开始怀疑「 sqlite3_finalize()」是不是得在每次查询之后都执行一次。 于是我补上了这句试了试:

    extension vChewingLM.LMInstantiator {
      fileprivate static func querySQL(strStmt sqlQuery: String, coreColumn column: CoreColumn, handler: (String) -> Void) {
        guard Self.ptrSQL != nil else { return }
        defer { sqlite3_finalize(Self.ptrStatement) } // 补上了这句。
        sqlite3_prepare_v2(Self.ptrSQL, sqlQuery, -1, &Self.ptrStatement, nil)
        while sqlite3_step(Self.ptrStatement) == SQLITE_ROW {
          guard let rawValue = sqlite3_column_text(Self.ptrStatement, column.id) else { continue }
          handler(String(cString: rawValue))
        }
      }
    ...
    }
    

    结果发现运存泄漏的问题失踪了。 这问题幸好发现得早,不然威注音 3.6.2 正式版带着这个 Bug 发出去的话会坑死很多人。

    然后我就将这次经历投稿到靠北工程师,但没想到反应非常空前。 居然还有人说我积阴德。还有人在转发时提到这么个例子:

    超市的系统在频繁的查询资料后都会有整个系统延迟的状况,不知道跟这有没关系... 虽然资料库跟我没啥屁关系,也先备份再说。

    于是我决定将这个故事也放在 V2EX 这边让各位 SQLite 用户共勉。

    ShikiSuen
        1
    ShikiSuen  
    OP
       150 天前
    补:为什么专门用 Xcode 14.2 的 Instruments ?因为 Xcode 15.0 的 Instruments 抓不到运存泄漏,详见 Apple 的 Xcode 15.1 Beta Release Notes 。
    julyclyde
        2
    julyclyde  
       149 天前   ❤️ 1
    你这个保险真是好……能在 bug 写出来之前就写好防御机制,佩服佩服!

    另外,我听说台湾话里边靠北是骂人的话啊,为什么会有这么个讨论组?以及英文其实是神户工程师?
    ShikiSuenVEVO
        3
    ShikiSuenVEVO  
       147 天前
    @julyclyde 靠北=抱怨。
    ShikiSuenVEVO
        4
    ShikiSuenVEVO  
       147 天前
    (另:我是楼主,换号了。之前的号不能用了。)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   956 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 19:25 · PVG 03:25 · LAX 12:25 · JFK 15:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.