一段java代码是如何执行的?

穆明明💤🌙

共 2583字,需浏览 6分钟

 · 2021-03-31


当你学会了java语言之后,你写了一些代码,然后你想要执行你的代码,来达成某些功能。那么,你都知道这段代码都是如何执行的吗?


1. 编译成class


众所周知,java代码是不能直接在jvm上执行的,执行的是class文件,将java代码编程class文件,需要编译


常用的编译方法是:javac xxx.java


但目前常见的java编辑工具,如eclipse和ideal都自带自动编译动能


2. jvm的构成


让我们回忆一下jvm的构成:


5c9f0e56dd82492c8657ac13944f4ee6.webp


主题上分为五个部分:


方法区,本地方法栈,java堆,java栈,程序计数器


其中,java栈,本地方法栈,程序计数器为线程私有,其余为线程共享


那么,方法在哪个地方执行呢?


java栈。


栈的遵循的方式是先进后出,java栈中方法的执行也遵循此规律,方法执行的步骤又称为栈帧。


3. 方法的顺序执行和栈帧


上代码:


Java 代码:


public class Main {

    public static void a(){

        b();

    }

 

    public static void b(){

        c();

    }

 

    public static void c(){

        System.out.println("Hello world!");

    }

 

    public static void main(String[] args) {

        a();

    }

}


上面是一段很简单的代码,主体上就是:


(1)一个Main类


(2)上面定义了一个main方法


(3)该main方法调用了静态方法a


(4)方法a调用方法b


(5)方法b调用方法c


(6)方法c打印了“Hello world!”


前文说过,java定义的非本地方法都是在java栈内执行的,一方法一栈帧


所以假设


mian方法对应栈帧m


  • a方法对应栈帧a

  • b方法对应栈帧b

  • c方法对应栈帧c


根据方法的调用,入栈顺序为:m,a,b,c


所以,栈帧出栈(即方法执行)顺序为:c,b,a,m


4. class文件反编译过后的样子


上一节,方法或栈帧在java栈的执行顺序,但在方法体内的内容是怎么执行的呢。


前文提到,jvm执行的是class文件,而class文件内是什么?


class文件内是一组指令集。


如何证明呢,还是再看一段代码。


Java 代码


public class Calculator{

    public int add(){

        int n = 10;

        int m = 20;

        int r = n + m;

        return r;

    }

 

    public static void main(String[] args) {

        Calculator calculator = new Calculator();

        int a = calculator.add();

        System.out.println(a);

    }

}

如上代码,实现的功能是:


(1)定义两个变量,相加


(2)main方法new对象,调用方法


但,class文件是不可以直接查看的。


我们可以采用反编译的方法,反编译命令:


javap -c xxx.class


cef6442f77c691362e5773b64751169b.webp


上述文件反编译后的样子如下:


415ea30b345f36a4c8d27e8fb89fcecf.webp


每个方法下面的Code,都是一组指令集。


5. 指令集详解


在讨论指令集之前,首先要讲一个概念,那就是对栈帧进一步拆分。


栈帧一共分为四个部分:局部变量表、操作数栈、动态链接、方法返回地址


其中,局部变量表和操作数栈是最重要的两个部分,局部变量表存放在方法中定义的局部变量,操作数栈相当于jvm的一个缓存,所有的操作都必须在此处进行,所有的变量都必须加载到操作数栈才能被使用。所以,所谓指令,就是在局部变量表和操作数栈来回倒腾的过程。


下面对指令进行分类讲解:


(1)入栈指令


整型入栈指令:


  • 取值-1~5采用iconst指令;

  • 取值-128~127采用bipush指令;

  • 取值-32768~32767采用sipush指令;

  • 取值-2147483648~2147483647采用ldc指令。


非整型入栈指令:


  • float,String类型也使用ldc指令

  • double和long类型使用ldc_2w

  • boolean类型视作0和1

  • null的入栈指令为:

    aconst_null


(2)存储指令


将操作数栈中的常量保存在局部变量表中的某个位置


如:


  • istore_1:

    将上面入栈的整型常量保存在局部变量表中的第1个位置

  • fstore_2:

    将上面入栈的浮点常量保存在局部变量表中的第2个位置

  • dstore_10:将上面入栈的双浮点常量保存在局部变量表中的第10个位置

  • lstore_20:将上面入栈的长整常量保存在局部变量表中的第20个位置

  • astore_100:将上面入栈的引用常量保存在局部变量表中的第100个位置


(3)变量入栈指令


  • iload_1:

    局部变量表中的第1个位置的整型变量入栈

  • fload_2:

    局部变量表中的第1个位置的浮点型变量入栈

  • dload_10:局部变量表中的第1个位置的双浮点型变量入栈

  • lload_20:局部变量表中的第1个位置的长整型变量入栈

  • aload_100:局部变量表中的第100个位置的引用型变量入栈


(4)计算指令


  • 加:

    iadd、ladd、fadd、dadd
    减:

    isub、lsub、fsub、dsub
    乘:

    imul、lmul、fmul、dmul
    除:

    idiv、ldiv、fdiv、ddiv


注意:栈顶计算,一次只能计算一个表达式


————————————————

本文分享自华为云社区《一段java代码是如何执行的》,原文作者:技术火炬手 。

原文链接:https://blog.csdn.net/devcloud/article/details/115198284

end


*版权声明:转载文章和图片均来自公开网络,版权归作者本人所有,推送文章除非无法确认,我们都会注明作者和来源。如果出处有误或侵犯到原作者权益,请与我们联系删除或授权事宜。


长按识别图中二维码

关注获取更多资讯




不点关注,我们哪来故事?



284cf6b5a0abbec904ee161992ffbae7.webp

点个再看,你最好看






               

       

   


浏览 23
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报