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

162 天前
 ShikiSuen

本事件已被我投稿至纯靠北工程师(「#純靠北工程師 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 用户共勉。

926 次点击
所在节点    程序员
4 条回复
ShikiSuen
162 天前
补:为什么专门用 Xcode 14.2 的 Instruments ?因为 Xcode 15.0 的 Instruments 抓不到运存泄漏,详见 Apple 的 Xcode 15.1 Beta Release Notes 。
julyclyde
161 天前
你这个保险真是好……能在 bug 写出来之前就写好防御机制,佩服佩服!

另外,我听说台湾话里边靠北是骂人的话啊,为什么会有这么个讨论组?以及英文其实是神户工程师?
ShikiSuenVEVO
159 天前
@julyclyde 靠北=抱怨。
ShikiSuenVEVO
159 天前
(另:我是楼主,换号了。之前的号不能用了。)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/996370

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX