<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          我用 Rust 寫了一個(gè) JVM

          共 10028字,需瀏覽 21分鐘

           ·

          2023-08-08 07:56

          最近,我花了相當(dāng)多的時(shí)間來(lái)學(xué)習(xí) Rust,就像任何有理智的人都會(huì)做的那樣,在編寫了幾個(gè) 100 行程序之后,我決定做一些更加雄心勃勃的事情——我用Rust寫了一個(gè) Java 虛擬機(jī)。?? 

          我在其中實(shí)現(xiàn)了很多獨(dú)創(chuàng)特性,我把它稱為『rjvm』。目前代碼已經(jīng)開(kāi)源,各位可以在 GitHub 上獲取。

          https://github.com/andreabergia/rjvm

          我想強(qiáng)調(diào)的是,這是一個(gè)玩具型 JVM,是為了學(xué)習(xí)目的而構(gòu)建的,而不是一個(gè)嚴(yán)肅的實(shí)現(xiàn)。特別是,它不支持:

          • 泛型

          • 線程

          • 反射

          • 注釋

          • 輸入/輸出

          • 及時(shí)編譯器

          • 字符串處理


          實(shí)際上,我已經(jīng)實(shí)現(xiàn)了大多數(shù)重要的事情。比如

          • 控制流語(yǔ)句 ( if, for, ...)

          • 原始和對(duì)象創(chuàng)建

          • 虛擬方法和靜態(tài)方法調(diào)用

          • 例外處理

          • 垃圾收集

          • jar文件中的類解析

          例如,以下是測(cè)試套件的一部分:

          class StackTracePrinting {    public static void main(String[] args) {        Throwable ex = new Exception();        StackTraceElement[] stackTrace = ex.getStackTrace();        for (StackTraceElement element : stackTrace) {            tempPrint(                    element.getClassName() + "::" + element.getMethodName() + " - " +                            element.getFileName() + ":" + element.getLineNumber());        }    }
          // We use this in place of System.out.println because we don't have real I/O private static native void tempPrint(String value);}

          我使用真實(shí)的包,它來(lái)自O(shè)penJDK 7 的rt.jar類。所以在上面的示例中,該類來(lái)自真實(shí)的 JDK!java.lang.StackTraceElement。

          我對(duì)自己學(xué)到的關(guān)于 Rust 知識(shí),以及如何實(shí)現(xiàn)虛擬機(jī)的知識(shí)感到非常滿意。

          特別是,我非常興奮能夠?qū)崿F(xiàn)一個(gè)真正的、有效的垃圾收集器。它很普通,但它是我親自寫出來(lái)的,我很喜歡它。?? 

          鑒于我已經(jīng)實(shí)現(xiàn)了最初的目標(biāo),我決定停止這個(gè)項(xiàng)目。我知道存在一些錯(cuò)誤,但我并不打算修復(fù)它們。

          概述 


          在這篇文章中,我將向概述 JVM 的工作原理。


          代碼組織 


          該代碼是一個(gè)標(biāo)準(zhǔn)的 Rust 項(xiàng)目。我把它分成了三個(gè)代碼空間(即包):

          • reader,它能夠讀取.class文件,并包含對(duì)其內(nèi)容進(jìn)行建模以及各種數(shù)據(jù)類型;

          • vm,其中包含可以將代碼作為庫(kù)執(zhí)行的虛擬機(jī);

          • vm_cli,其中包含一個(gè)非常簡(jiǎn)單的命令行啟動(dòng)器來(lái)運(yùn)行虛擬機(jī),可執(zhí)行java文件。

          我正在考慮將reader提取到單獨(dú)的存儲(chǔ)庫(kù)中并將其發(fā)布到crates.io上,因?yàn)樗鼘?shí)際上對(duì)其他的開(kāi)發(fā)者可能有用。

          解析.class文件 


          如大家所知曉的,Java 是一種半編語(yǔ)言 -javac編譯器獲取.java源文件并生成各種.class文件,通常壓縮在一個(gè).jar文件中 - 這是一個(gè)zip文件因此,執(zhí)行些 Java 代碼要做的第一件事就是加載一個(gè).class文件,其中包含編譯器生成的字節(jié)碼。

          其中,類文件包含如下內(nèi)容:

          • 有關(guān)類的元數(shù)據(jù),例如其名稱或源文件名

          • 超類名稱

          • 實(shí)現(xiàn)的接口

          • 字段及其類型與注釋

          • 接下來(lái)是方法:

            • 它們的描述符,它是一個(gè)字符串,表示每個(gè)參數(shù)的類型和方法的返回類型

            • 元數(shù)據(jù),例如throws子句、注釋、泛型信息

            • 字節(jié)碼以及一些額外的元數(shù)據(jù),例如異常處理程序表與行號(hào)表。


          就像前面所描述的,我將rjvm創(chuàng)建了一個(gè)單獨(dú)的盒子,名為reader,它可以解析類文件,并返回一個(gè)對(duì)類及其所有內(nèi)容進(jìn)行建模的Rust 結(jié)構(gòu)。

          https://github.com/andreabergia/rjvm/blob/main/reader/src/class_file.rs


          執(zhí)行方法 


          vm包的主要 API是Vm::invoke,用于執(zhí)行方法。

          它需要一個(gè)CallStack,其中包含各種CallFrame, 一個(gè)用于正在執(zhí)行的每個(gè)方法。對(duì)于執(zhí)行main,調(diào)用堆棧最初將為空,并且將創(chuàng)建一個(gè)新的棧幀來(lái)運(yùn)行它。接下來(lái),每次函數(shù)調(diào)用都會(huì)向調(diào)用堆棧添加一個(gè)新幀。當(dāng)方法執(zhí)行完成時(shí),其相應(yīng)的幀將被丟棄,并從調(diào)用堆棧中刪除。

          大多數(shù)方法將用 Java 實(shí)現(xiàn),因此它們的字節(jié)碼將被執(zhí)行。但是,rjvm也支持本機(jī)方法,即直接由 JVM 實(shí)現(xiàn)而不是在 Java 字節(jié)碼中實(shí)現(xiàn)的方法。其中相當(dāng)多的部分位于 Java API 的“較低部分”,需要與操作系統(tǒng)進(jìn)行交互(例如執(zhí)行 I/O)或支持運(yùn)行時(shí)。

          你可能見(jiàn)過(guò)的后者的一些示例包括System::currentTimeMillis、System::arraycopy或Throwable::fillInStackTrace。在 中rjvm,這些是由Rust 函數(shù)實(shí)現(xiàn)的。

          JVM是基于堆棧的虛擬機(jī),即字節(jié)碼指令主要在堆棧上操作。還有一組由索引標(biāo)識(shí)的局部變量,可用于存儲(chǔ)值并將參數(shù)傳遞給方法。這些與 中的每個(gè)調(diào)用幀相關(guān)聯(lián)rjvm。

          值與對(duì)象建模 


          類型Value對(duì)局部變量、堆棧元素或?qū)ο笞侄蔚目赡苤颠M(jìn)行建模,并按如下方式實(shí)現(xiàn):

          /// Models a generic value that can be stored in a local variable or on the stack.#[derive(Debug, Default, Clone, PartialEq)]pub enum Value<'a> {    /// An unitialized element. Should never be on the stack,    /// but it is the default state for local variables.    #[default]    Uninitialized,
          /// Models all the 32-or-lower-bits types in the jvm: `boolean`, /// `byte`, `char`, `short`, and `int`. Int(i32),
          /// Models a `long` value. Long(i64),
          /// Models a `float` value. Float(f32),
          /// Models a `double` value. Double(f64),
          /// Models an object value Object(AbstractObject<'a>),
          /// Models a null object Null,}

          順便說(shuō)一句,這里的 sum 類型(如 Rust 的enum)是一種美妙的抽象——它非常適合表達(dá)一個(gè)值可能具有多種不同類型的情況。

          為了存儲(chǔ)對(duì)象和它的值,我最初實(shí)現(xiàn)了一個(gè)名為Object 的簡(jiǎn)單結(jié)構(gòu),Object其中包含對(duì)類的引用(使用對(duì)象類型進(jìn)行建模)和Vec<Value>用來(lái)存儲(chǔ)字段值 。

          在我實(shí)現(xiàn)垃圾收集器時(shí),我修改了它并以使用較低級(jí)別的實(shí)現(xiàn),里帶有大量的指針和強(qiáng)制轉(zhuǎn)換 - 相當(dāng) C 語(yǔ)言風(fēng)格!

          在當(dāng)前的實(shí)現(xiàn)中,一個(gè) AbstractObject(模擬“真實(shí)”對(duì)象或數(shù)組)是指向字節(jié)數(shù)組的指針,其中包含幾個(gè)標(biāo)頭字,然后才是字段值。

          執(zhí)行指令 


          執(zhí)行一個(gè)方法意味著一次執(zhí)行一個(gè)字節(jié)碼指令。


          JVM 有著大量的指令(超過(guò) 200 條!),由字節(jié)碼中的一個(gè)字節(jié)進(jìn)行編碼。許多指令后面都有參數(shù),有些指令的長(zhǎng)度是可變的。


          這是在代碼中通過(guò)Instruction類型建模:

          /// Represents a Java bytecode instruction.#[derive(Clone, Copy, Debug, Eq, PartialEq)]pub enum Instruction {    Aaload,    Aastore,    Aconst_null,    Aload(u8),    // ...


          如上所述,方法的執(zhí)行將保留一個(gè)堆棧和一個(gè)局部變量數(shù)組,指令通過(guò)其索引引用它們。此外,它還會(huì)將程序計(jì)數(shù)器初始化為零,即下一條要執(zhí)行的指令地址。該指令將被處理并更新程序計(jì)數(shù)器 ,通常情況是加 1,但各種跳轉(zhuǎn)指令可以將其移動(dòng)到不同的位置。

          這些用于實(shí)現(xiàn)所有流控制語(yǔ)句,例如if、for或while語(yǔ)言。

          一個(gè)特殊的指令系列由那些可以調(diào)用另一種方法的指令組成。

          有多個(gè)方法可以解決如應(yīng)該調(diào)用哪個(gè)方法的方案。其中虛擬或靜態(tài)查找是主要方法,但還有其它方法。

          當(dāng)解析完正確的指令后,rjvm將向調(diào)用堆棧添加一個(gè)新幀,并立即開(kāi)始該方法的執(zhí)行。特殊的情況,如果該方法的返回值是void,它將被推送到堆棧,并且將恢復(fù)執(zhí)行。

          Java 字節(jié)碼格式相當(dāng)有趣,我后面有計(jì)劃專門寫一篇文章向大家來(lái)介紹各種指令。

          例外與異常處理 


          異常的實(shí)現(xiàn)是相當(dāng)復(fù)雜的,因?yàn)樗鼈兤茐牧苏5目刂屏鳎⑶铱赡軓姆椒ㄖ刑崆胺祷兀ú⒃谡{(diào)用堆棧上傳播)。

          不過(guò),我對(duì)實(shí)現(xiàn)它們的方式非常滿意,這里向各位展示一些相關(guān)代碼。

          你需要知道的第一件事是,任何catch塊都對(duì)應(yīng)于方法異常表的一個(gè)條目,每個(gè)條目包含程序計(jì)數(shù)器范圍、catch 塊中第一條指令的地址以及該塊所處理的異常的類名稱捕獲。

          接下來(lái),CallFrame::execute_instruction的簽名如下:

           fn execute_instruction(    &mut self,    vm: &mut Vm<'a>,    call_stack: &mut CallStack<'a>,    instruction: Instruction,) -> Result<InstructionCompleted<'a>, MethodCallFailed<'a>>

          其中類型為:

          /// Possible execution result of an instructionenum InstructionCompleted<'a> {    /// Indicates that the instruction executed was one of the return family. The caller    /// should stop the method execution and return the value.    ReturnFromMethod(Option<Value<'a>>),
          /// Indicates that the instruction was not a return, and thus the execution should /// resume from the instruction at the program counter. ContinueMethodExecution,}
          /// Models the fact that a method execution has failedpub enum MethodCallFailed<'a> { InternalError(VmError), ExceptionThrown(JavaException<'a>),}

          標(biāo)準(zhǔn) Rust的Result類型是:

          enum Result<T, E> {   Ok(T),   Err(E),}

          因此,執(zhí)行一條指令會(huì)導(dǎo)致四種可能的狀態(tài):

          • 指令執(zhí)行成功,當(dāng)前方法可以繼續(xù)執(zhí)行(標(biāo)準(zhǔn)情況);

          • 該指令執(zhí)行成功,并且它是一個(gè)返回指令,因此當(dāng)前方法應(yīng)該返回(可選)一個(gè)返回值;

          • 該指令無(wú)法執(zhí)行,可能發(fā)生了一些內(nèi)部VM錯(cuò)誤;

          • 或者指令無(wú)法執(zhí)行,因?yàn)閽伋隽藰?biāo)準(zhǔn) Java 異常。


          執(zhí)行方法代碼如下:

          /// Executes the whole methodimpl<'a> CallFrame<'a> {    pub fn execute(        &mut self,        vm: &mut Vm<'a>,        call_stack: &mut CallStack<'a>,    ) -> MethodCallResult<'a> {        self.debug_start_execution();
          loop { let executed_instruction_pc = self.pc; let (instruction, new_address) = Instruction::parse( self.code, executed_instruction_pc.0.into_usize_safe() ).map_err(|_| MethodCallFailed::InternalError( VmError::ValidationException) )?; self.debug_print_status(&instruction);
          // Move pc to the next instruction, _before_ executing it, // since we want a "goto" to override this self.pc = ProgramCounter(new_address as u16);
          let instruction_result = self.execute_instruction(vm, call_stack, instruction); match instruction_result { Ok(ReturnFromMethod(return_value)) => return Ok(return_value), Ok(ContinueMethodExecution) => { /* continue the loop */ }
          Err(MethodCallFailed::InternalError(err)) => { return Err(MethodCallFailed::InternalError(err)) }
          Err(MethodCallFailed::ExceptionThrown(exception)) => { let exception_handler = self.find_exception_handler( vm, call_stack, executed_instruction_pc, &exception, ); match exception_handler { Err(err) => return Err(err), Ok(None) => { // Bubble exception up to the caller return Err(MethodCallFailed::ExceptionThrown(exception)); } Ok(Some(catch_handler_pc)) => { // Re-push exception on the stack and continue // execution of this method from the catch handler self.stack.push(Value::Object(exception.0))?; self.pc = catch_handler_pc; } } } } } }}

          我知道這段代碼中有相當(dāng)多的實(shí)現(xiàn)細(xì)節(jié),但我希望它能讓大有了解如何使用 Rust的Result和模式匹配很奇妙地映射到上述行為。

          不得不說(shuō)我對(duì)自己寫的這段代碼感到由衷地自豪。??

          垃圾收集 


          rjvm最后的里程碑是垃圾收集器的實(shí)現(xiàn)。


          我選擇的算法是一個(gè)停止世界。原因很簡(jiǎn)單,因?yàn)闆](méi)有線程!實(shí)現(xiàn)半空間復(fù)制收集器。


          我已實(shí)現(xiàn)尼算法(https://en.wikipedia.org/wiki/Cheney%27s_algorithm的一個(gè)(較差的體,但我真的應(yīng)該去實(shí)現(xiàn)真正的東西......??


          這個(gè)算是將可用內(nèi)存分成兩部分,稱為半空間:一部分將處于活動(dòng)狀態(tài)并用于分配對(duì)象,另一部分將不再使用。


          當(dāng)空間滿了的時(shí)候,將觸發(fā)垃圾收集,所有活動(dòng)對(duì)象將被復(fù)制到另一個(gè)半空間。然后,所有對(duì)象的引用都將被更新,以便它們被指向新的副本。最后,兩者的角色將互換——類似于藍(lán)綠(https://www.redhat.com/en/topics/devops/what-is-blue-green-deployment)部署的工作原理。

          該算法具有以下特點(diǎn):

          • 很顯然,它浪費(fèi)了大量?jī)?nèi)存(就是最大內(nèi)存的一半!);

          • 分配速度非常快(碰撞指針);

          • 復(fù)制和壓縮對(duì)象,意味著它不必處理內(nèi)存碎片;

          • 由于更好的緩存行利用率,壓縮對(duì)象可以提高性能。

          當(dāng)然,真正的 Java VM 使用更復(fù)雜的算法,通常是分代垃圾收集器,例如 G1 或并行 GC,它們使用復(fù)制策略的演變版本。

          結(jié)論 


          在寫rjvm的過(guò)程中,我學(xué)到了很多,得到了甚多樂(lè)趣。當(dāng)然,不能要求從副業(yè)項(xiàng)目中得到更多......也許下次我會(huì)選擇一些不那么雄心勃勃的東西,來(lái)學(xué)習(xí)另一門新的編程語(yǔ)言!??


          順便再說(shuō)一句,我想說(shuō)從 Rust 中獲得了非常多的樂(lè)趣。我認(rèn)為它是一種很棒的語(yǔ)言,正如我之前寫的那樣,我很喜歡使用它來(lái)實(shí)現(xiàn)自己的JVM!

          編譯:洛逸

          作者:安德里亞

          https://andreabergia.com/blog/2023/07/i-have-written-a-jvm-in-rust/

          推薦閱讀:

          被 GPT-4 Plus 賬號(hào)價(jià)格勸退了!

          世界的真實(shí)格局分析,地球人類社會(huì)底層運(yùn)行原理

          不是你需要中臺(tái),而是一名合格的架構(gòu)師(附各大廠中臺(tái)建設(shè)PPT)

          企業(yè)IT技術(shù)架構(gòu)規(guī)劃方案

          論數(shù)字化轉(zhuǎn)型——轉(zhuǎn)什么,如何轉(zhuǎn)?

          華為干部與人才發(fā)展手冊(cè)(附PPT)

          【中臺(tái)實(shí)踐】華為大數(shù)據(jù)中臺(tái)架構(gòu)分享.pdf

          華為的數(shù)字化轉(zhuǎn)型方法論

          華為如何實(shí)施數(shù)字化轉(zhuǎn)型(附PPT)

          華為大數(shù)據(jù)解決方案(PPT)


          瀏覽 162
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  凹凸福利视频 | 成人av免费在线 成人A片在线观看 | 国产精品成人国产乱 | 天天看天天操 | 亲子乱伦视频 |