YVM是用C++寫的一個Java虛擬機,現在支持Java大部分功能,以及一個基於標記清除算法的併發垃圾回收器. 不過還有很多bug等待修復。
感興趣的朋友pull request/fork/star吧。
Github repo
https://github.com/racaljk/yvm
已支持語言特性
高級特性逐步支持中,可以開Issue提議或者直接PR
- Java基本算術運算,流程控制語句,面向對象。
- RTTI
- 字符串拼接(+,+=符號重載)
- 異常處理(可輸出stacktrace)
- 創建異步線程
- Synchronized(支持對象鎖)
- 垃圾回收(標記清除算法)
構建和運行
- 先決條件
- Boost(>=1.65) 請在
CMakeLists.txt
中手動配置Boost庫位置 - CMake(>=3.5)
- C++14
- gcc/msvc/mingw均可
- Boost(>=1.65) 請在
- 老生常談
$ 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協議