# 跑 SQLite 查詢不及時使用 sqlite3_finalize() 的嚴重後果,望周知 本事件已被我投稿至純靠北工程師(「#純靠北工程師 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()」是不是得在每次查詢之後都執行一次。 於是我補上了這句試了試: ```swift 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 發出去的話會坑死很多人。 然後我就將這次經歷投稿到靠北工程師,但沒想到反應非常空前。 居然還有人說我積陰德。還有人在轉發時提到這麼個例子: > 超市的系統在頻繁的查詢資料後都會有整個系統延遲的狀況,不知道跟這有沒關係... > 雖然資料庫跟我沒啥屁關係,也先備份再說。 於是我決定將這個故事也放在 HackMD 這邊讓各位 SQLite 用戶共勉。
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.