很多新手都以為, Java 和相比 C++,JVM 內置了 Garbage Collection 的功能就相等於 "我不用管" / "我可以寫少幾行 code",這種想法,是要不得的。
其實這也難怪,太多書本和教學寫得都不清楚,甚至錯誤百出。
不是為了吸引新手使用 Java,就是把它定義為進階深入的知識,或根本連作者本身都錯誤理解了。
其實無論是 Java , .NET 或其他附有 GC 或其他聲稱有 GC 功能的語言,也要自己面對資源回收的理
為垃圾而設想
Garbage Collection (垃圾回收) 能有效率地運作的大前提是,它要知道甚麼是垃圾,甚麼不垃圾。
理所當然的,當你手上還捉緊著它們,還有 Object 之間千絲萬縷的連結,你又叫 GC Engine 如何替你進行回收呢?
其他的還有 GC 的時機,策略,優化(避免劣化) 也是要由寫程式的人來想。
別忘了它們的手足
GC 本來就只能替你回收記憶體,可是你不要忘了除了記憶體,你還有別的垃圾。IO Handle, native code 的 pointer 或其他向 OS / Hardware 要來的資源 。GC Engine 是不知道如何釋放/回收它們的,GC 能做到的只是告訴你:『我看到它上面有條便條,它要我在回收之前通知你,記得有其他的事情要做』---也就是 finalize method。
當 GC engine 看到將要回收的 Object 上有 finalize method,則會把它們放到一邊,經由獨立的 Finalizer Thread 執行。而你要做的,就是記得在 finalize 內把自己用到的 sub-process / native pointer / windows handle / file handle 經由 JNI 方法自己放掉。正如在 C 當中 delete pointer 之前要把子內容清掉一樣。
可幸地, JDK 內所有一般性會使用 native resource 的 Object 都已實作了 finalize,你在正常情況下都不必為它們底層的 File Handle, Socket, GUI Handle, pointer.... 等等太過粗心。不過你還是要注意和小心的!
你應該知道 SWT / Swing 和 AWT 吧!我以前讀 SWT 介紹的時候都不了解這一小句『SWT doesn't use the Java garbage collector....』當時我看不懂,近年才有所覺悟。它所說的,不是指 Java 中Object 記憶體的回收,而是指不等待不使用 GC 的 Finallizer 來回收 GUI Handler。 先說一點歷史,當年 AWT 設計上最失敗的地方不是其 heavy-weight (這只是 Sun 用來騙人的術語),也不是 GUI 的 cross-platform 問題。而是其而是其設計高估 GC 的能力 Finalizer Thread 的速度。 finalize 最大的多點是所有實現了它的 object 和它的子部件都至才要經過兩次 GC 才能被清掉,基本上所有使用了 AWT 的 Java Process 的 GC 都被劣化(使用 AWT 的大前提是你不應 extends 任何 AWT 元件)。而且 OS 能用的 GUI handle 數目是有限的,可是 AWT 就喜歡拿了一堆 Handle 不放手(還沒有 finalize 掉),令到整個 OS out-of-resource 的機會大大提高。
Swing 就是應用了 light-weight (另一個騙人的術語) 的想法,減少向 OS 要求 resource,把 GUI 放入自己管理的部份。SWING 自行產生,管理,繪畫,事件,消除。(怎麼看都是把原本 OS 做的事搶來自己做,根本是更加重,更加 heavy-weight 才對) 來達到多重目的。 LookAndFeel 是其中最沒有用最浪費,郤又最多人掛在口邊的的。
而 SWT 的想法就是:『好吧,我知道它們是何時成為垃圾,就由我來把它回收吧。』
很簡單,很易了解,其實也只是要求 Progarmmer 去做他們本來就應該要做的工作。
GC 不只是 Garbage Collection
上文不是提到 GC 是回收記憶體中的垃圾和其他垃圾嗎?為甚麼又說它不是垃圾回收呢?
的確,原本的 GC 的用意 (包括 Java / .NET / C++ 和其他) 很可能根本只是垃圾回收。可是把它套用在近代的 JVM 上就有點太過小看 GC 了。GC 的另一樣重要的價值是自動化的記憶體管理。簡單來說,就是能否善用空間的能力。
記憶體管理大概可以被分為:配置,釋放,重置,定位等等。
根據支持者的研究文件指出(忘了出處),老舊的 C/C++ 當中的 配置/釋放記憶體雖然每次都很快,但卻是 stop-the-world 的動作,效能算起來會和有 overhead 的 Java 差不多,甚至比 Java 差。
另外 GC 能避免 memory fragment 。fragment 的出現是因為程式每次向OS 要求 memory 都是以 page 為單位的,反復的配置/釋放會減低 page 真正需要使用的密度。JVM GC 在執行的時候,很多時候也會把 Object 的本體移來移去(e.g. Generational GC)。也是能把 2/5 + 2/5 + 1/5 整合為一個 5/5 的時機。VM 理論上和部份的實作上是做得到把常用的,連著使用的 object 放在一起,而把不太相關的,很有機會被垃圾掉了又放在另一個 page 內,而直接減少底層執行指令的負擔。例是說 String 本身和內在的 char[] 經常放在一起,減少要在多個不同的 page 讀寫的機會。
GC 它能做到的,它替你做到的,都比你想的多;但你也需要寫 GC friendly 的程式才行。
1 則留言:
[...] Java, Garbage Collection與 Memory management 當中提到 AWT 和 Finalize [...]
發佈留言