首頁 类库下载 java类库 java 程式運行的基礎知識

java 程式運行的基礎知識

Oct 29, 2016 am 11:31 AM
java

JVM 執行緒棧 到 函數運行

每一個JVM執行緒來說啟動的時候都會建立一個私有的執行緒棧。一個jvm執行緒棧用來儲存棧幀,jvm執行緒棧和C語言中的棧很類似,它負責管理局部變數、部分運算結果,同時也參與函數呼叫和函數傳回的工作。 JVM規範中運行執行緒棧的大小可以是固定的或是動態分配的,也可以是根據一定規則計算的。不同jvm對堆疊的實作會不同,有些可能提供給開發人員自己控制jvm執行緒堆疊初始大小的方式;對於動態分配來說也可能提供對jvm最大和最小值的設定。

當計算一個執行緒所需的分配的大小超出了固定值、或設定的最大值,jvm會拋出StackOverflowError。而對於動態分配棧來說,如果記憶體無法提供足夠的空間來滿足最小值、或需要的值JVM會拋出OutOfMemoryError

棧幀,可以理解成一個函數執行的環境,它管理參數、局部變量、回傳值等等。

每個堆疊幀都包含一個管理局部變數的陣列( local variables),這個陣列的單元數量在編譯成字節碼的時候就能確定了。對於32-bit 一個單位能夠存放 boolean, byte, char, short, int, float, reference,returnAddress;連續兩個單位就能夠用來存放long 、double。局部變數數組的下標是從0開始,一般而言0位置儲存的是this,後面接著是函數的參數,再是函數中出現的局部變數。

每個堆疊幀也都包含一個(LIFO)操作棧的資料結構(operand stack),它的大小同樣也可以在編譯的時候確定,創建的時候會是個空棧。舉個簡單的例子,來描述它公用,對於int a+b來說,先把push a 進入棧中,再樸實b 進入入棧中,然後同時pop 兩個值執行iadd 指令,再將其加後的結果push入棧中完成指令。

除開以上兩個關鍵的結構,每個堆疊幀還有常數池( run-time constant pool)、異常拋出管理等結構。在此就不一一詳細說來了,可以參考其他資料。

再來透過一個簡單的 Demo 來說明,一個堆疊幀的工作。首先,我們來看這樣的一個函數:

public int comp(float number1, float number2){
        int result ;
        if(number1 < number2)
            result = 1;
        else
            result = 2;
        return result;
    }
登入後複製

其中函數內邏輯對應的字節碼,如下:

 0: fload_1

 1: fload_2

 2: fcmpg

🠎 : iconst_1

收到字節碼指令稍微說明下:

fload_x:取局部變數數組中第x個,類型fload,push 入棧;

fcmpg:比較兩個單精度浮點數。若兩數大於結果為1,相等則結果為0,小於的話結果為-1;

ifge:跳轉指令;

iconst_x:push 常數x入棧;

istore_x:pop棧存入局部變數陣列第x個;

iload_x:讀取局部變數數組第x個,入棧;

ireturn:函數結束返回int型;

細心點觀察可以發現i開頭指代int,f開頭指涉fload,load代表載入,if代表跳轉等等,其中字節碼的操作碼定義也是有一定意義的,詳情可以翻譯jvm字節碼相關標準。再來看看,jvm如何在堆疊幀結構上執行情況,以具體調用comp(1.02,2.02)為例:

Java 的 Class

說字節碼,一定少不了.class。不妨,以一個demo類別 來具體看class 的內容,類別非常簡單,兩個函數一個say,另外一個就是上面的cmp函數。

public class Hello {

    public void say(){
        System.out.println("Hello world!");
    }

    public int comp(float number1, float number2){
        int result ;
        if(number1 < number2)
            result = 1;
        else
            result = 2;
        return result;
    }
}
登入後複製

用 javac -g:none Hello.java 來編譯這個類別的,然後用 javap -c -v Hello.class 來解析編譯的class。

Classfile /src/main/java/com/demo/Hello.class
  Last modified 2016-10-28; size 404 bytes
  MD5 checksum 9ac6c800c312d65b568dd2a0718bd2c5public class com.demo.Hello
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:   #1 = Methodref          #6.#14         // java/lang/Object."<init>":()V
   #2 = Fieldref           #15.#16        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #17            // Hello world!
   #4 = Methodref          #18.#19        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #20            // com/demo/Hello
   #6 = Class              #21            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               say
  #11 = Utf8               comp
  #12 = Utf8               (FF)I
  #13 = Utf8               StackMapTable
  #14 = NameAndType        #7:#8          // "<init>":()V
  #15 = Class              #22            // java/lang/System
  #16 = NameAndType        #23:#24        // out:Ljava/io/PrintStream;
  #17 = Utf8               Hello world!
  #18 = Class              #25            // java/io/PrintStream
  #19 = NameAndType        #26:#27        // println:(Ljava/lang/String;)V
  #20 = Utf8               com/demo/Hello
  #21 = Utf8               java/lang/Object
  #22 = Utf8               java/lang/System
  #23 = Utf8               out
  #24 = Utf8               Ljava/io/PrintStream;
  #25 = Utf8               java/io/PrintStream
  #26 = Utf8               println
  #27 = Utf8               (Ljava/lang/String;)V{  public com.demo.Hello();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:      stack=1, locals=1, args_size=1
         0: aload_0         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return

  public void say();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Hello world!
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return

  public int comp(float, float);
    descriptor: (FF)I
    flags: ACC_PUBLIC
    Code:      stack=2, locals=4, args_size=3
         0: fload_1         1: fload_2         2: fcmpg         3: ifge          11
         6: iconst_1         7: istore_3         8: goto          13
        11: iconst_2        12: istore_3        13: iload_3        14: ireturn
      StackMapTable: number_of_entries = 2
        frame_type = 11 /* same */
        frame_type = 252 /* append */
          offset_delta = 1
          locals = [ int ]
}
登入後複製

解釋下其中涉及的新的操作碼

getstatic:获取镜头变量;
invokevirtual:调用函数;
return:void 函数结束返回;
登入後複製

在 public int comp(float, float) code 這段程式碼裡面就能看到上面提到的字節碼運行的例子。有了個感性認識,其實大體看到class檔案裡面,除了字節碼指令外,還包括了常數pool,存取標誌(public 等),類別的相關資訊(屬性、函數、常數等)。因為前面用的是 -g:node進行編譯的,其他模式下還可以有其他擴充、偵錯資訊也包括在class裡面。官方給的class檔案格式,詳細如下:

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}
登入後複製

magic: 就是非常有名的 0xCAFEBABE ,一個識別class檔案;

minor_version 、major_version :指的是java class 文件的版本,一般说class文件的版本是 XX.xx 其中XX 就是major,xx是minor,比如上面demo中的版本是52.0 代表就是 minor 0,major 51.

constant_pool_count:就是常量池元素个数,cp_info constant_pool[constant_pool_count-1] 就是相关的详细信息了。

access_flags:指的是访问标识例如ACC_PUBLIC、ACC_FINAL、ACC_INTERFACE、ACC_SUPER 写过java的相信看名字应该知道啥意思,ACC是access的缩写。

其他具体的,就不一一介绍了详细可以直接参考官方文档。

动态生成java字节码

当然,你可以直接按照官方的class文件格式来直接写 byte[],然后自定义个 class load 载入编写的byte[]来实现动态生成class。不过,这个要求可能也有点高,必须的非常熟悉class文件格式才能做到。这里demo还是借助 ASM 这个类库来简单演示下,就编写下 上面的Hello 不过里面只实现say的方法。如下:

public class AsmDemo {    public static final String CLASS_NAME = "Hello";    
    public static final AsmDemoLoad load = new AsmDemoLoad();    private static class AsmDemoLoad extends ClassLoader {        public AsmDemoLoad() {            super(AsmDemo.class.getClassLoader());
        }        public Class<?> defineClassForName(String name, byte[] data) {            return this.defineClass(name, data, 0, data.length);
        }
    }    public static byte[] generateSayHello() throws IOException {

        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        classWriter.visit(V1_7, ACC_PUBLIC + ACC_SUPER, CLASS_NAME, null, getInternalName(Object.class), null);    
        //默认初始化函数
        Method constructorMethod = Method.getMethod("void <init> ()");
        GeneratorAdapter constructor = new GeneratorAdapter(ACC_PUBLIC, constructorMethod, null, null, classWriter);
        constructor.loadThis();        //每个类都要基础Object
        constructor.invokeConstructor(Type.getType(Object.class), constructorMethod);
        constructor.returnValue();
        constructor.endMethod();

        Method mainMethod = Method.getMethod("void say ()");
        GeneratorAdapter main = new GeneratorAdapter(ACC_PUBLIC, mainMethod, null, null, classWriter);
        main.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class));
        main.push("Hello world!");
        main.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println (String)"));
        main.returnValue();
        main.endMethod();        return classWriter.toByteArray();
    }    public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException,
            InvocationTargetException, InstantiationException, NoSuchMethodException, SecurityException, IOException {        byte[] code = AsmDemo.generateSayHello();        //反射构建 hello 类,调用hello方法。
        Class<?> hello = load.defineClassForName(CLASS_NAME, code);
        hello.getMethod("say", null).invoke(hello.newInstance(), null);
    }
}
登入後複製

关于动态生成字节码用途,一定场景下是可以提升效率与性能,因为动态生成的类和普通的载入类并无太大区别。手工优化后的字节码执行可能比编译的要优,可以替代反射使用的许多场景 同时避免反射的性能消耗。很著名的一个例子,fastJSON 就是使用内嵌 ASM 框架动态生成字节码类,来进行序列和反序列化工作,是目前公认最快的json字符串解析。

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Java Spring 面試題 Java Spring 面試題 Aug 30, 2024 pm 04:29 PM

在本文中,我們保留了最常被問到的 Java Spring 面試問題及其詳細答案。這樣你就可以順利通過面試。

突破或從Java 8流返回? 突破或從Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

PHP:網絡開發的關鍵語言 PHP:網絡開發的關鍵語言 Apr 13, 2025 am 12:08 AM

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP與Python:了解差異 PHP與Python:了解差異 Apr 11, 2025 am 12:15 AM

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

Java程序查找膠囊的體積 Java程序查找膠囊的體積 Feb 07, 2025 am 11:37 AM

膠囊是一種三維幾何圖形,由一個圓柱體和兩端各一個半球體組成。膠囊的體積可以通過將圓柱體的體積和兩端半球體的體積相加來計算。本教程將討論如何使用不同的方法在Java中計算給定膠囊的體積。 膠囊體積公式 膠囊體積的公式如下: 膠囊體積 = 圓柱體體積 兩個半球體體積 其中, r: 半球體的半徑。 h: 圓柱體的高度(不包括半球體)。 例子 1 輸入 半徑 = 5 單位 高度 = 10 單位 輸出 體積 = 1570.8 立方單位 解釋 使用公式計算體積: 體積 = π × r2 × h (4

PHP與其他語言:比較 PHP與其他語言:比較 Apr 13, 2025 am 12:19 AM

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

PHP與Python:核心功能 PHP與Python:核心功能 Apr 13, 2025 am 12:16 AM

PHP和Python各有優勢,適合不同場景。 1.PHP適用於web開發,提供內置web服務器和豐富函數庫。 2.Python適合數據科學和機器學習,語法簡潔且有強大標準庫。選擇時應根據項目需求決定。

創造未來:零基礎的 Java 編程 創造未來:零基礎的 Java 編程 Oct 13, 2024 pm 01:32 PM

Java是熱門程式語言,適合初學者和經驗豐富的開發者學習。本教學從基礎概念出發,逐步深入解說進階主題。安裝Java開發工具包後,可透過建立簡單的「Hello,World!」程式來實踐程式設計。理解程式碼後,使用命令提示字元編譯並執行程序,控制台上將輸出「Hello,World!」。學習Java開啟了程式設計之旅,隨著掌握程度加深,可創建更複雜的應用程式。

See all articles