Skip to main content

CLDC 的使用方向

你可以把 CLDC 切割為兩大塊, 這兩大塊的關係分別是 "上與下" 的關係. 位於下層的是一個小型的 Java 虛擬機器 (Java Virtual Machine), 名字叫做 KVM. 而位於上層的則是一些 Java 環境中最基本的 class 模組. 而目前在國內提到有關 CLDC 的使用時, 不外乎可以區分為下面幾大類:
  1. 修改 KVM:

    Ok, 在討論這一項之前, 我先描述一個名詞, 這個名詞叫做 R.I, 它的全名叫做 Reference Implementation. Ok, 那麼甚麼叫做 Reference Implementation 呢? 基本上, 你可以想像一個情景, 有一本專門用來規範 KVM 架構的書, 而這本書就叫做 Reference. 而依照這本規格書中所定義的內容所撰寫出來的程式碼, 就叫做 Reference Implementation.

    而 Sun 這家公司呢, 有撰寫它自己的 Reference Implementation, 而其中的某個版本便可以從 Sun 的網站 (http://wwws.sun.com/software/communitysource/j2me/cldc/download.html) 下載回來使用. 不過想當然爾, 能夠讓你下載的原始碼, 並沒有非常認真的考慮到程式撰寫的最佳化, 以及執行時期的效率等等議題.

    但也就是因為 Sun 有釋放出一個版本的 KVM R.I., 因此目前大多數廠商在 CLDC 上所作的使用就是去 Sun 的網站下載原始碼, 然後回來作一些修改. 基本上, 目前大部份的修改不外乎屬於下面幾類:

    • 移植到其他的作業系統:

      由於 Sun 的 R.I. 是以 UNIX 或 Mircosoft Windows 為預設的作業系統所撰寫出來的, 因此, 假如說今天你所要跑得作業系統跟這兩者差很多的話, 那你就得作出相對應的修改. 比方說, 在某些情況下, 你得在 Nucleus (http://www.acceleratedtechnology.com/) 這個作業系統上執行 KVM, 那麼你就必須要針對 Nucleus 的特性來在 KVM 裡面作修改.

      通常這些修改包括了下列數項:

      • Big-endian 以及 little-endian 的選擇.

      • 64-bit 整數型態的加減乘除:

        如果你所使用的編譯器 (Compiler) 有支援 64-bit 整數型態的話, 那麼你就很簡單的直接使用編譯器內建的 64-bit 整數型態. 但假如你的編譯器不支援這種資料型態的話, 你就必須撰寫程式碼來模擬它.

    • 嘗試讓 Sun 的 R.I. 執行的更快:

      由於 Sun 的 R.I. 只是一個 Reference Implementation, 它並沒有非常詳細的考慮到程式的最佳化, 因此雖然 Sun R.I. 的執行速度還算可以, 但實際上, 我們可以作些手腳讓它執行的更快, 而且加快的空間還不小. 目前常見的加速方法大概有下列兩種:

      • 使用組合語言 (Assembly Language) 來改寫某些程式碼.

        雖然說編譯器 (Compiler) 可以幫我們作很多形式的最佳化, 但是再好的編譯器所產生出來的程式碼, 或多或少都還有人為調整的空間. 比方說, Java 虛擬機器內部通常都有一個非常大的迴圈在反覆地執行 Java bytecode, 而 Java 虛擬機器大部份的時間便花在這個迴圈上, 因此這個迴圈便是我們以組合語言來改寫的好目標.

        但是使用組合語言就像是 "跟惡魔打交道", 因為當你使用了組合語言來最佳化之後, 最主要將會碰到的問題是 - 後續的程式碼維護工作會變得很難. 因為所有本來可以由 compiler 來幫你 take care 的東西, 現在都必須要由你自己來處理了. 而且你的文件也必須要寫的夠詳細, 否則不要說是之後維護的其他人, 縱使是你自己, 在隔個半年, 一年之後再回來看你所寫的組合語言程式碼, 可能也都無法一下子就看出來當初為甚麼要這麼撰寫.

      • 加入 JIT (Just-In-Time Compiler) 或 Hot-Spot Compiler 的技術.

        在編譯器的世界裡, 有好幾種分類的方法, 其中的一種便是把編譯器分成兩大類, 分別是:

        • 靜態編譯器 (Static Compiler)

          在程式執行前就先把程式整個編譯出來. 這是最常見的方式, 比方說一般常見的 Visual C++, GNU GCC 編譯器等等, 都是屬於 static compiler 的一種. 這種編譯器的好處是, 編譯的時間並沒有受到限制, 不論花多久的時間, 就是要把程式給編譯出來. 因此 static compiler 的設計者便可以在編譯器內部實作任何他認為合適的最佳化方法, 來加速最後程式的執行.

        • 動態編譯器 (Dynamic Compiler)

          這種編譯器一般來說, 我們很少會去使用到. 並且很有可能的, 大部份的使用者都沒有使用過動態編譯器的經驗. 縱使有, 你也不會知道自己使用到了動態編譯器. 為甚麼呢? 最主要的原因是, 動態編譯器是在程式的執行過程中, 程式邊執行, 邊把下一步所要執行的程式碼給編譯出來 (所以才叫做動態編譯器阿). 所以你只能看到程式執行的表象過程與結果, 並沒有辦法看到程式內部實際在運作的編譯過程. 基本上, 為甚麼要發展出所謂的動態編譯器呢? 主要的原因大概有下列幾點:

          • 可以在上一代的機器所執行的程式, 不需要經過修改或重新編譯, 便可以跑在下一代的機器上.

            比方說, 有些廠商所出產的電腦系統, 上一代跟下一代並不相容. 而在正常的狀況下, 那些原本在上一代電腦所執行的程式, 必須要經過重新編譯之後, 才能夠執行在下一代電腦上. 但是如果沒有辦法拿到舊程式的原始碼, 那麼也就無從編譯起了. 在這種情況下的解決辦法, 就是將舊程式的執行碼 (executable code) 把他翻譯成新電腦看得懂得格式.

            而這種翻譯的動作, 可以靜態編譯器或動態編譯器的方式來實做出來. 那為甚麼需要發展動態編譯呢? 有一部份的原因是因為靜態編譯需要人為的事先介入來進行編譯的動作, 然而對於不懂電腦系統的人來說, 這將會是一個門檻. 如果改採用動態編譯的話, 對一般使用者來說, 他所作的一樣是直接執行程式, 而程式將會動態的把自己編譯成在下一代電腦上可以執行的型態.

            因此你可以把動態編譯器, 看成是以某一種電腦的執行碼為輸入, 而以另外一種電腦的執行碼格式為輸出的執行時期編譯器.

          • 加速模擬器的執行速度.

            所謂模擬器, 就是用軟體來模擬一台真實的電腦. 想而易見的, 這種用軟體模擬出來的電腦, 它的執行速度一定是緩慢的. 這是因為本來只要數個 CPU clock cycle 便可執行完畢的指令, 在軟體模擬器下, 將需要數十個, 甚至數百個 CPU clock cycle 才能夠模擬完成, 因此, 軟體模擬器的執行速度將會比所模擬的真實機器要慢上數十倍或數百倍之多.

            所以, 有些方法就被發展出來加速軟體模擬器的執行, 其中的一種方法便是在軟體模擬器當中加入所謂的動態編譯器, 在邊執行的過程當中, 邊去把另一個電腦系統的執行碼翻譯成本機電腦系統所看得懂得執行碼. 並且藉由編譯器的最佳化技術來達到加速執行的目的.

        Ok, 那麼再重新回到在 KVM 內部加入動態編譯器的議題. 這個題目跟剛剛所提到的動態編譯器有關係嗎? 答案是有的. 原因是 Java 虛擬機器本來就是一個軟體模擬器, 它用來模擬一個假想的 Java 機器. 而既然它是一個軟體模擬器, 我們就可以使用之前在軟體模擬器領域中被發展出來的動態編譯技術, 來加速 Java 虛擬機器的執行.

        而由於 Java 虛擬機器的特性, 所以在其內部進行動態編譯動作時, 是以一個 Java method 為單位. 這也就是說, 如果今天位於 Java 虛擬機器內部的動態編譯器決定要把某一段 Java bytecode 翻譯成本機電腦所用的 machine code 時, 該段 Java bytecode 將會是一整個 Java method.

        而在 Java 虛擬機器內部常用的動態編譯器主要分成下面兩類:

        • Just-In-Time (JIT) Compiler

          所謂的 Just-In-Time 編譯器, 就是只要碰到函式呼叫 (比方說執行到 Invoke virtual, Invoke static, Invoke interface, 或 Invoke special 這四個 Java bytecode), 就會把該函式翻譯成本機電腦所用的 machine code. 這個作法的好處是較為簡單. 但是壞處是記憶體的需求量將會較大. 這是因為並不是每個函式都會被重複的執行到, 某些函式可能只會被用到一次, 而這些只被用到一次的函式, 如果花費了額外的時間去進行翻譯的動作, 但在之後卻不會執行到它, 那麼整體來說, 反而無助於整體執行速度的提昇. 而且還必須花費額外的記憶空間來儲存之後完全用不到的 machine code. 所以, 就有下一種類的動態編譯器被發展出來.

        • Hot-Spot Compiler

          所謂的 Hot-Spot 編譯器, 就是指當呼叫某個函式達到一定的次數之後, 動態編譯器才會去編譯該函式. 這種架構比較複雜, 因為你要處理原本位於虛擬機器內部的 interpreter 跟動態編譯器之間的互動以及連結. 但是這種 Hot-Spot 編譯器將可以避免上述的 Just-In-Time 編譯器的若干缺點.

Comments