[開源JVM] yvm - 自制Java虛擬機

中文 | English
| Build Status | |

YVM是用C++寫的一個Java虛擬機,現在支持Java大部分功能,以及一個基於標記清除算法的併發垃圾回收器. 不過還有很多bug等待修復。
感興趣的朋友pull request/fork/star吧。

Github repo

https://github.com/racaljk/yvm

已支持語言特性

高級特性逐步支持中,可以開Issue提議或者直接PR

構建和運行

  • 先決條件
    • Boost(>=1.65) 請在CMakeLists.txt中手動配置Boost庫位置
    • CMake(>=3.5)
    • C++14
    • gcc/msvc/mingw均可
  • 老生常談
$ cd yvm
$ cmake .
$ make -j4
$ make test
$ ./yvm --help
Usage:
    --help                List help documentations and usages.
    --runtime arg         Attach java runtime libraries where yvm would lookup 
                        classes at
    --run arg             Program which would be executed soon
You must specify the "runtime" flag to tell yvm where it could find jdk classes, and also program name is required.
$ ./yvm --runtime=C:\Users\Cthulhu\Desktop\yvm\bytecode ydk.test.QuickSort

運行效果

  • helloworld

  • 快速排序

  • and more see its github repository readme.md...

開發文檔

1. 從字節碼到對象

MethodArea負責管理字節碼到JavaClass的完整生命週期。MethodArea的方法是自解釋的:

class MethodArea {
public:
    // 方法區需要從運行時目錄中搜索相關的*.class文件
    MethodArea(const vector<string>& libPaths);
    ~MethodArea();

    // 查看一個類是否存在
    JavaClass* findJavaClass(const string& jcName);
    //加載jcName類
    bool loadJavaClass(const string& jcName);
    //移除jcName(該方法用於垃圾回收器)
    bool removeJavaClass(const string& jcName);
    //鏈接jcName類,初始化static字段
    void linkJavaClass(const string& jcName);
    //初始化jcName,初始化靜態字段,調用static{}
    void initJavaClass(CodeExecution& exec, const string& jcName);

public:
    //輔助方法,如果不存在jcName則加載 
    JavaClass* loadClassIfAbsent(const string& jcName);
    //如果未鏈接jcName則鏈接
    void linkClassIfAbsent(const string& jcName);
    //如果未初始化jcName則初始化
    void initClassIfAbsent(CodeExecution& exec, const string& jcName);
}

假設磁盤存在一個Test.class文件,它會經歷如下過程:

Test.class[磁盤中]-> loadJavaClass("Test.class")[內存中] -> linkJavaClass("Test.class")->initJavaClass("Test.class")

現在虛擬機就可以使用這個JavaClass創建對應的對象了:

// yrt 是全局運行時對象,ma表示方法區模塊,jheap表示堆模塊
JavaClass* testClass = yrt.ma->findJavaClass("Test.class");
JObject* testInstance = yrt.jheap->createObject(*testClass);

2.1 對象內部構造

虛擬機執行時棧上存放的都是JObject,它的結構如下:

struct JObject {
    std::size_t offset = 0; 
    const JavaClass* jc{}; 
};

offset唯一代表一個對象,所有在堆上面的操作都需要這個offset。jc指向對象的Class表示。
堆中的對象是按照<offset,fields>方式進行存放的:

[1]  ->  [field_a, field_b, field_c]
[2]  ->  []
[3]  ->  [field_a,field_b]
[4]  ->  [field_a]
[..] ->  [...]

只要我們持有offset,就可以查找/添加/刪除對應的field

數組幾乎和上面類似,只是多了長度,少了Class指針

struct JArray {
    int length = 0;
    std::size_t offset = 0; 
};
[1]  ->   <3, [field_a, field_b, field_c]>
[2]  ->   <0, []>
[3]  ->   <2, [field_a,field_b]>
[4]  ->   <1, [field_a]>
[..] ->   <..,[...]>

2.2 從對象創建到消亡

上面提到,對象持有一個offset和jc,其中jc表示的JavaClass是由MethodArea負責管理的,offset則是由JavaHeap負責管理。JavaHeap提供了大量API,這裏選取的是最重要的:

class JavaHeap {
public:
    //創建對象和數組
    JObject* createObject(const JavaClass& javaClass);
    JArray* createObjectArray(const JavaClass& jc, int length);

    //獲取對象字段
    auto getFieldByName(const JavaClass* jc, const string& name,
                        const string& descriptor, JObject* object);
    //設置對象字段
    void putFieldByName(const JavaClass* jc, const string& name,
                        const string& descriptor, JObject* object,
                        JType* value);
    //設置數組元素
    void putElement(const JArray& array, size_t index, JType* value);
    //獲取數組元素
    auto getElement(const JArray& array, size_t index);
    
    //移除對象和數組
    void removeArray(size_t offset;
    void removeObject(size_t offset);
};

還是Test.class那個例子,假設對應的Test.java構造如下:

public class Test{
    public int k;
    private String hello;
}

在第一步我們已經獲取到了Test類在虛擬機中的類表示以及對象表示,現在就可以對類的字段進行操作了:

const JavaClass* testClass = yrt.ma->findJavaClass("Test.class");
JObject* testInstance = yrt.jheap->createObject(*testClass);
//獲取hello字段
JObject*  helloField = yrt.jheap->getFieldByName(testClass,"hello","Ljava/lang/String;",testInstance);
//設置k字段
yrt.jheap->putFieldByName(testClass,"k","I",testInstance);

Ⅰ. 關於JDK

部分JDK類是JVM運行攸關的,但由於JDK比較複雜不便於初期開發,所以這裏用重寫過的JDK代替,源碼參見javaclass目錄,可以使用compilejava.bat進行編譯,編譯後*.class文件位於bytecode.
目前重寫過的JDK類有:

  • java.lang.String
  • java.lang.StringBuilder
  • java.lang.Throwable
  • java.lang.Math(::random())
  • java.lang.Runnable
  • java.lang.Thread

Wiki和源碼中有很多詳細的開發文檔,如果想探索關於YVM的更多內容,請移步瀏覽.

License

所有代碼基於MIT協議

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章