點解啲軟件食 RAM 好似黑洞咁?—— 記憶體管理由盤古初開講起

你有冇試過咁:部機明明 16GB RAM,開住幾個 Chrome 分頁,加個 WhatsApp 同 Spotify,再開個 VS Code,成部機就開始喘氣,風扇轉到好似飛機引擎咁,甚至程式直接崩潰?好多人會鬧「啲 software 愈寫愈爛」,但查實背後嘅故事,遠比你想像中複雜。今次就同你由阿爺年代嘅電腦講起,一路拆解點解而家啲軟件咁大食,中間仲有不少你未必聽過嘅冷知識㗎。


一、冷知識篇:RAM 嘅前世今生,你知幾多?

1. 磁芯記憶體:一粒甜甜圈先儲得一個位

喺 1950 至 1970 年代嗰陣,電腦主記憶體係靠人手將成千上萬粒細過芝麻嘅磁芯(magnetic core)織出嚟,每粒磁芯只係儲到 0 或者 1。成部巨型電腦可能得幾 KB RAM,貴到飛起。程式員要將「慳住每一個 byte」刻落 DNA 度,甚至會玩一啲現代人覺得癲嘅技巧,例如直接修改自己個程式碼本身。呢種慳家精神,同今日郁下就食幾 GB 嘅軟件比較,真係差天共地。

2. 「Memory leak」個名點嚟㗎?

「記憶體洩漏」(memory leak)呢個比喻,早喺幾十年前已經出現。當程式問系統借咗一截 RAM 嚟用,用完又唔還,呢啲空間就好似水咁慢慢漏走咗,永遠俾唔返其他程式用。如果你長開一個 program(例如伺服器),佢就會慢慢蠶食晒所有 RAM,最後得返一個出路:死機。有啲舊式太空探測器上面嘅 software,為咗避呢個問題,直頭會定時自動 reboot,就係要清走呢啲「無主孤魂 RAM」。

3. 點解紅白機 Game 永遠唔會漏 RAM?

細個打紅白機、Game Boy,你有冇見過佢地 hang 機?幾乎冇。因為呢啲遊戲機根本冇現代作業系統,遊戲卡匣直接控制晒所有硬件。程式員對每 byte RAM 嘅用途都瞭如指掌,冇得亂咁動態分配。你熄機一刻,所有記憶體歸零。現代軟件為咗方便同靈活,反而多咗好多出錯嘅機會,呢個就係 trade-off。

4. Chrome 係「食 RAM 獸」,但佢特登㗎喎

有冇人話過你知,Chrome 咁大食其實係一個安全設計?後文會詳細拆解,而家賣個關子先。


二、管理 RAM 嘅三大門派:手動 vs 自動 vs 嚴格規管

要知道啲軟件點解咁大食,一定要明程式點樣問系統「借 RAM」同「還 RAM」。幾十年嚟,電腦科學家發展咗三種主流方法。

1. 手動還 RAM:C 語言嘅雙面刃

喺 C 同 C++ 呢啲語言,程式員要親自叫 malloc()(借 RAM)同 free()(還 RAM)。咁俾你好大權力,可以寫出超高效嘅程式,但同時亦係惡夢嘅開始:

  • 唔記得還:借咗唔還,形成 memory leak,RAM 隨時間乾塘。
  • 太早還:還咗之後,第二個程式拎咗嗰個位,但原來個程式仲 keep 住個舊地址(懸空指標),一用就爆,輕則 crash,重則可以成為保安漏洞。
  • 重複還:同一舊 RAM 你 free 兩次,底層管理結構會亂晒龍,死俾你睇。

呢類 bug 好難搵,仲係史上最多安全事故嘅元兇之一。心臟出血漏洞(Heartbleed),本質上就係 C 程式冇檢查邊界,讀咗隔籬嘅 RAM 出嚟。

2. 自動垃圾回收:Java、C# 嘅「唔使煩」哲學

為咗唔使人類咁痛苦,Java、C#、Python 呢啲語言導入了垃圾回收(Garbage Collection,簡稱 GC)機制。你只管借,唔使理還;程式背景有條「清潔工 thread」,會周不時掃晒所有物件,睇下邊啲已經冇人用,就自動回收佢霸住嘅 RAM。

好處:大大減少 memory leak,程式員開心專心寫商業邏輯。
代價

  • Stop-the-world 暫停:某啲 GC 做法喺收垃圾嗰陣,要全個程式停低晒,造成 latency。對遊戲、高頻交易嚟講,呢下暫時可以死人。
  • RAM 碎片化:不停借借還還,會令到 RAM 好似瑞士芝士咁,得返啲散修修窿位,當你需要一大塊連續 RAM 時,系統逼住要再問作業系統拎更多新 RAM,愈食愈多。
  • GC 鍾意有咁大借咁大:為咗減少清潔次數,GC 傾向一次過向系統大手筆借 RAM,用唔著都袋住先,搞到程式 baseline 佔用好多 RAM,就算佢乜都未做。
  • 循環參照陷阱:兩個物件互相指住對方,但出面已經冇路入到去,有啲簡單 GC 演算法唔識處理,結果就變咗隱形 leak。

呢個就係點解好多用 Java 寫嘅企業程式,乜都未做就已經霸住幾百 MB RAM 嘅原因。

3. 擁有權同借用:Rust 嘅革命

近年冒起嘅 Rust,行咗第三條路:唔靠執行期 GC,亦唔靠人類亂咁 free,而係喺 compile 嗰陣,就透過「所有權」(ownership)同「借用」(borrowing)嘅嚴格規則,去證明每一舊 RAM 都有明確嘅主人,而且所有存取都係合法咁授權咗。程式碼如果過唔到呢個檢查,直情 compile 唔到。

呢套機制做到 零成本抽象:執行效能拍得住 C++,但冇 memory leak 同懸空指標嘅問題,仲天然防止咗多線程嘅資料競爭。好多新一代基建軟件,甚至部分 OS 核心模組,都開始改用 Rust 重寫,就係想徹底終結呢類記憶體 bug。Rust 證明咗,安全同高效唔一定係魚與熊掌。


三、現代軟件咁大食嘅幾大元兇

知道咗背景之後,就可以答個核心問題:點解而家啲 app 同 browser 咁鍾意吞 RAM?

1. 功能通脹同抽象層堆疊

今時今日嘅軟件,功能多到爆:靚靚 UI、網路通訊、加密解密、多國語言、無障礙支援……每一層功能都疊加咗大量 abstraction,而每個 abstraction 又可能會自己 allocate 緩衝區、建立物件,RAM 消耗就好似滾雪球咁,愈滾愈大。

2. 咩都快取

為咗俾你順暢嘅使用體驗,現代軟件盛行大規模快取。Browser 會 cache 網頁圖片、字型;app 會 cache 資料庫查詢結果;連 OS 都會 cache 磁碟讀寫。呢啲 cache 可以令到嘢行得好快,但同時霸住好多 RAM。理想上,當 RAM 唔夠使嗰陣,佢哋應該自動釋放,但現實往往冇咁理想,個個都死揸住唔放。

3. JavaScript 同動態語言嘅豪爽開銷

JavaScript、Python 呢類動態語言,俾到開發者好大彈性,但喺記憶體使用上就好似開水喉咁。一個簡單整數,喺 C 可能佔 4 或 8 bytes,但去到呢啲語言,要包裝成一個帶有型別標籤、參考計數等一大堆額外 info 嘅物件,體積即刻膨脹幾倍。當現代前端框架每秒鐘都建立大量 temporary 物件時,GC 就要頻頻跑出嚟做嘢,仲會拖慢整體表現。

4. 瀏覽器戰爭同 Chrome 嘅「隔離」設計

終於嚟到最多人鬧嘅「食 RAM 獸」——瀏覽器。Chrome 行嘅係多進程架構,將每個分頁、每個擴充功能、甚至 GPU 運算,都分別放喺獨立嘅 OS process 入面。咁做嘅好處極大:一個分頁死咗唔會攬炒成個 browser;惡意網站亦好難偷睇到你另一分頁嘅密碼。但代價係每個 process 都要各自複製一份底層執行環境嘅 RAM,堆疊起嚟就非常可觀。

除此之外,瀏覽器引擎為咗令網頁行得更快,內部做咗極複雜嘅 Just-In-Time 編譯同最佳化快取,全部都係燒 RAM 嘅大戶。將呢堆嘢乘返你開住嘅十幾廿個分頁,RAM 自然好似放水咁流走。


四、你我可以做啲乜?—— 自救生存指南

1. 善用分頁管理工具

裝返啲 extension,例如自動暫停閒置分頁嗰啲,迫佢哋放返 RAM 出嚟。

2. 得閒重啟大食應用程式

尤其係嗰啲用 Electron 寫嘅桌面 app(例如 Slack、Discord、VS Code),佢哋本身就帶住一整個 Chromium 內核,長開唔熄可能內傷積累,定期重啟可以清一清碎片同隱性 leak。

3. 了解軟件嘅「母語」

如果你同時開幾個 Java 大工具(例如 Eclipse、Android Studio),佢哋各自嘅虛擬機都會預留一大舊 RAM,可以入 settings 手動限制 heap size,唔使俾佢哋自由膨脹。

4. 歡迎新世代輕量級軟件

而家好多工具已經開始用 Rust、Go 等語言重寫,佔用少好多 RAM 而且仲穩定。例如用 Rust 寫嘅終端機 Alacritty,或者用 Go 寫嘅容器技術,都為「輕量」定咗新標準。不妨留意下呢類代替品。


結語:RAM 管理,係一場冇聲嘅戰爭

由手刻 address、到自動清潔、再到 compile 時嘅嚴格驗證,人類同記憶體管理嘅搏鬥,反映出軟件可靠性嘅進化史。每次你埋怨 Chrome 搶晒你啲 RAM,其實你正見證緊一個為咗安全同隔離而做嘅取捨。明白呢啲取捨之後,我哋可以更精明咁揀工具、tune 系統,甚至原諒嗰啲唔係有心大食嘅軟件。話唔定有一日,呢個黑洞終有一日會被馴服㗎。