成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

JVM系列:幾張圖看懂Java字節碼

開發 前端
常量池中保存著非常豐富的信息。包括:方法的符號引用、類的符號引用、字段或方法描述符、各種字符常量、各種基礎類型字面量等等。

作為一個java程序員,如果你不懂字節碼的話,你只能算是初級程序員了。

這可不是聳人聽聞。了解字節碼你才能真正了解包括“動態代理的原理”、“類加載的細節過程”、“重載和重寫是如何實現的”、“多態是如何實現的”、“泛型究竟是什么”等等。

了解這些對你的工作有實際的意義:

比如,開發時碰到問題,需要確定一個jar包是不是最新的(開發時我們經常使用一個SNAPSHOT版本反復打包)。

比如,你在工具組,要開發一個動態修改運行中的類的工具。

比如,你碰到大廠高階一些的面試,尤其是偏底層的團隊。

比如,你碰到各種詭異的包括類找不到、方法找不到、java版本錯誤無法解析類等問題。

了解字節碼就仿佛給了你另一雙眼睛和更高緯度的視角,來應對各種java相關的工作。

話不多說,我們這就開始。

1、Java代碼從編寫到運行

圖片圖片

如圖所示,我們寫好的java代碼會先編譯成字節碼,然后JVM會加載這些字節碼并執行其中的命令,也就是將字節碼翻譯為機器碼執行。

這里有兩個重點:

1)其他語言寫的代碼,如果可以翻譯成標準的java字節碼,一樣可以在JVM中運行、事實上,有一些語言已經可以這么做了(包括Groovy、JRuby、Scala、Clojure、Kotlin等)。這是字節碼技術面向未來的最大賣點,也是JVM的宏大愿景。

2)如果我們改變了編譯出來字節碼,其實就改變了邏輯。不需要去改原來的java代碼。

2、字節碼怎么看

下面我們用一個例子來看看字節碼到底是什么,長啥樣。

public class Calculator {
    private static final int offset = 100;


    public int add(int x, int y) {
        int z = x + y;
        int m = z - offset;
        return m;
    }
}

這段邏輯很簡單。我定義了一個Calculator類,它有一個add方法,就是傳入兩個數值參數,把他們相加后再減去一個固定值offset,然后返回。

我們使用javac命令編譯后,得到如下文件(十六進制):

圖片圖片

這個class人肉讀起來非常費勁。他有其固定的格式,你需要按照“說明書”一個字節一個字節翻譯過來才行。

這里我不做詳細的介紹,看了下圖你基本上就能大致了解了。

圖片圖片

一般我們看編譯后的文件,絕對不會直接看十六進制的。而是使用javap命令,將其轉化為我們更容易看懂的格式。我們來執行下命令:

javap -verbose Calculator.class

得到如下這樣的內容:

圖片圖片

不要著急,我來逐塊分開和你講講。

3、字節碼的含義

我們將字節碼的內容分為三塊來講:類信息、常量池和方法表。

1)類信息

圖片

這里面的內容不多,也比較容易理解。

major version: 52說明這是一個使用java8編譯出來的class。

flags:ACC_PUBLIC表示這個class是public的。ACC_SUPER在JDK1.2以后編譯出來的類都會帶上這個標志,這和JDK1.2以后invokespecial指令的變化有關。

2)常量池

常量池中保存著非常豐富的信息。包括:方法的符號引用、類的符號引用、字段或方法描述符、各種字符常量、各種基礎類型字面量等等。你可以簡單理解為各種你定義的類名稱、屬性名稱、方法名稱、常量以及系統自身需要的一些字面量,都會在這里定義。

下圖是Calculator類的常量池數據,我做了一些標注,方便你的理解。

圖片圖片

常量池的第一列是“常量類型”,一共有十幾種。每一種決定了第二列的數據格式。我這里就不詳細貼圖了。

常量池的第二列會有其他一些常量信息的引用,這些引用往往還是嵌套的。不過如圖所示,每個復雜的常量后都有注解幫你拼接好了。

3)方法表

在Calculator類中,有兩個方法。其中一個是我們定義的add方法,另一個則是默認構造函數。

【方法1 - 構造函數】

我們先來看下默認構造函數。和上面一樣,我直接在圖中做了說明,方便理解:

圖片圖片

【方法2 - add函數】

在介紹add方法前,我們要先簡單介紹下JVM的執行引擎。

JVM的執行引擎稱之為“基于棧的執行引擎”。也就是說,所有計算的中間結果都存在一個專門的棧中。與之相對應的就是歷史更悠久的經典“基于寄存器的操作引擎”。

我們用一個 x + y * z 的例子,來看下兩種操作引擎的差別。見下圖

圖片圖片

可以看到,完全一樣的代碼,寄存器的每個指令都需要多個參數,而基于棧的操作指令只需要一條。這是因為寄存器需要指定數據從哪個寄存器中拿(cpu有十多個寄存器),但基于棧的操作指令只會涉及一個操作數棧,所以只需要聲明出棧或者入棧即可。

下面就是 x + y * z 這條命令對應的字節碼操作過程:

圖片圖片

介紹完這個后,我們就可以看下Calculator類中add方法的字節碼內容了:

圖片圖片

到這里,我們就把字節碼中主要的三塊內容都講完了。

4、字節碼實踐:直接修改字節碼

既然我們了解了字節碼的結構,我們就要實踐一下直接定位并修改字節碼,不然的話只是紙上功夫了。

我們寫了個main函數來調用上面的Calculator類,這里把Main和Calculator類都貼一下:

public class Main {
  public static void main(String[] args) {
        System.out.println(new Calculator().add(1, 2));
    }
}
public class Calculator {
    private static final int offset = 100;
    public int add(int x, int y) {
        int z = x + y;
        int m = z - offset;
        return m;
    }
}

我們編譯并運行一下,得到如下結果:

$ javac com/codingbetterlife/justmylab/utils/*.java
$ java -cp . com.codingbetterlife.justmylab.utils.Main
-97

結果為 1 + 2 - 100 = -97

下面我來把add方法中的第7行通過字節碼改成 int m = z + offset。

圖片圖片

修改后的class我們通過javap反編譯后能看到,命令已經從isub變成了iadd。我們來運行下看看結果:

$ java -cp . com.codingbetterlife.justmylab.utils.Main
103

這次變成了 1 + 2 + 100 = 103。可以看到,我們并沒有重新編譯,所以你只要找對地方修改正確,就可以直接改變代碼邏輯了。

5、結尾

當然,我并不鼓勵你去修改線上的class。你也看到了,十六進制的代碼是非常容易改錯的。上面這個例子更多的是幫你理解java的字節碼。

看了上面的內容,我不知道你是否聯想到了Spring中的AOP。事實上Cglib就是通過直接修改字節碼的方式來實現切面的。這點我相信你準備面試的時候肯定已經知道了,但是通過上面的內容,我相信你肯定有更深的理解了。

但事實上,更重要的話題是,要如何方便地修改字節碼(不直接修改十六進制文件),這是我們下面一篇JVM系列文章會給大家介紹的內容。

此外,這些都只是編譯過程中“耍的小花招”,如何重載一個運行時的類是更有意思的話題。雖然現在很多熱部署方案存在各種各樣的問題,從而大家都還是信賴靜態編譯后重新部署,但是了解對運行時類的修改和重載,是極有意義的,尤其是開發過程中。

本文轉載自微信公眾號「 CodingBetterLife」,作者「 趙志強 」,可以通過以下二維碼關注。

轉載本文請聯系「 CodingBetterLife」公眾號。

責任編輯:武曉燕 來源: CodingBetterLife
相關推薦

2020-04-21 12:09:47

JVM消化字節碼

2022-01-17 11:28:55

JVM 虛擬機Java

2019-08-26 09:12:20

2011-12-01 14:56:30

Java字節碼

2016-11-10 10:03:02

微軟Power BI組件

2021-04-27 08:31:06

event loopJavaScriptsetTimeout函

2021-12-09 22:36:30

Java 字節碼頁緩存

2010-09-25 10:20:05

JAVA字節碼

2017-09-26 16:32:03

JavaGC分析

2021-03-24 14:32:44

人工智能深度學習

2017-09-20 08:48:09

JVM內存結構

2024-07-30 14:01:51

Java字節碼JVM?

2024-04-29 09:25:19

2020-09-10 14:52:01

JVMJava算法

2019-10-30 08:45:21

JS代碼NodeJS

2013-03-04 11:10:03

JavaJVM

2019-05-16 09:07:42

華為方舟編譯器

2017-11-01 15:38:54

jvm知識點總覽

2017-10-17 14:02:30

jvm調優工具

2017-09-22 15:15:23

jvm調優命令
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: av三级| 国产精品久久久久久网站 | 久久久久久久久久一区 | www.久久久.com| 久久久久国产精品一区二区 | 久久久精品 | 日韩淫片免费看 | 国产精品免费一区二区三区四区 | 久久国产精品偷 | 亚洲福利一区二区 | 在线中文字幕亚洲 | 亚洲精品电影网在线观看 | 日韩免费 | 婷婷激情五月网 | 日本精品久久久久久久 | 亚洲精品www久久久久久广东 | 网站黄色在线 | 国产精品一区二区av | 国产九九av| 一区二区免费在线视频 | 国产激情精品一区二区三区 | 毛片毛片毛片毛片 | 在线免费观看毛片 | 久久人体 | 在线成人精品视频 | 日韩av第一页 | 久久精彩视频 | 欧美日韩一区在线 | 日产精品久久久一区二区福利 | 激情伊人网 | 欧美日韩一区在线播放 | 久久综合一区二区 | 国产精品久久久久久久久久尿 | 日韩精品一区二区三区在线观看 | 国产精品久久久 | 国产欧美精品一区二区色综合朱莉 | 亚洲福利 | 日本超碰| 精品视频国产 | 欧美国产亚洲一区二区 | 成人九色 |