jvm指令入门(一)

本文旨在介绍java虚拟机(jvm)指令集合入门,在介绍指令集之前需要做一点准备工作:

jvm内存分配

字节码指令集的简单性很大程度上是由于 Sun 设计了基于堆栈的 VM 架构,而不是基于寄存器架构。有各种各样的进程使用基于JVM 的内存组件, 但基本上只有 JVM 堆需要详细检查字节码指令。

PC寄存器:对于Java程序中每个正在运行的每一个线程都有一个PC寄存器。在任意时刻,一条Java虚拟机线程只会执行一个犯法的代码,这个正在被执行的方法称之为当前方法。如果这个方法不是native的,那么pc寄存器就保存java虚拟机正在执行的字节码指令的地址,如果该方法是native的,那么pc急促其的值是undefined。

JVM 栈:每一个java虚拟机线程都有自己私有的Java虚拟机栈(Java Virtual Machine stack),这个栈与线程同时创建,用来存放本地变量、方法参数和返回值。下面是一个显示3个线程的堆栈示例。

这里写图片描述

Java堆:是可供各个线程共享的运行时内存区域,也是供所有类实例和数组对象分配内存的区域。Java堆在虚拟机启动的时候创建,它存储了被自动内存管理系统,也就是常说的垃圾回收器所管理的各种对象,这些受管理的对象无需也无法显式地销毁。

方法区:对于每个已加载的类,它储存了每一个类的结构信息,例如,运行时常量池,字段和方法数据,构造函数和普通方法的字节码内容,还包括以写类,实例,接口初始化时用到的特殊方法。如下入所示:

这里写图片描述

JVM堆栈是由帧组成的,当方法被调用时,每个帧都被推到堆栈上,当方法完成时从堆栈中弹出(通过正常返回或抛出异常)。每一帧还包括:

  • 本地变量数组,索引从0到它的长度-1。长度是由编译器计算的。一个局部变量可以保存任何类型的值,long和double类型的值占用两个局部变量。
  • 用来存储中间值的栈,它存储指令的操作数,或者方法调用的参数

内存结构如下图所示:

这里写图片描述

jvm指令集简介

Java虚拟机的指令由一个字节长度的,代表着某种特定操作含义的操作码(opcode)以及跟随其后的零至多个代表此操作所需参数的操作数(operand)所构成。虚拟机中许多指令并不包含操作数,只有一个操作码.

格式如下:

opcode (1 byte)  operand1 (optional)  operand2 (optional)    

在当前执行方法的栈帧里,一条指令可以将值在操作栈中入栈或出栈,可以在本地变量数组中悄悄地加载或者存储值。

先看一个入门案例(int类型为例):

public class Hello {
    public static void main(String[] args) {        
        int a = 1;
    }
}

找到类的字节码所在的文件夹,在dos窗口执行如下命令:

javap -c Hello.class

得到如下结果:

Compiled from "Hello.java"
public class Hello {
  public Hello();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: istore_1
       2: return
}

我们这里关心的指令只有两个

0: iconst_1 //将一个常量加载到操作数栈
1: istore_1 //将一个数值从操作数栈存储到局部变量表

将一个常量加载到操作数栈和将一个数值从操作数栈存储到局部变量表还有其他的以写指令,这里我们通过一个案例来说明:

Java代码如下:

public class Hello {
    public static void main(String[] args) {    
        int a20 = -2147483647;
        System.out.println(a20);
        int a18 = -65537;
        int a17 = -65536;
        int a16 = -32769;
        System.out.println(a16);
        int a15 = -32768;
        System.out.println(a15);
        int a14 = -129;
        int a13 = -128;
        int a12 = -2;
        int a11 = -1;
        int a = 0;
        int a01 = 1;
        int a02 = 5;
        int a03 = 6;
        int a04 = 127;
        int a05 = 128;
        int a06 = 32767;
        int a07 = 32768;
        int a08 = 65535;
        int a09 = 65536;
        int a10 = 2147483647;
        System.out.println(a10);
    }
}

再次执行javap -c命令,结果如下:

D:\develop\a\bin>javap -c Hello.class
Compiled from "Hello.java"
public class Hello {
  public Hello();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #16                 // int -2147483647
       2: istore_1
       3: getstatic     #17                 // Field java/lang/System.out:Ljava/io/PrintStream;
       6: iload_1
       7: invokevirtual #23                 // Method java/io/PrintStream.println:(I)V
      10: ldc           #29                 // int -65537
      12: istore_2
      13: ldc           #30                 // int -65536
      15: istore_3
      16: ldc           #31                 // int -32769
      18: istore        4
      20: getstatic     #17                 // Field java/lang/System.out:Ljava/io/PrintStream;
      23: iload         4
      25: invokevirtual #23                 // Method java/io/PrintStream.println:(I)V
      28: sipush        -32768
      31: istore        5
      33: getstatic     #17                 // Field java/lang/System.out:Ljava/io/PrintStream;
      36: iload         5
      38: invokevirtual #23                 // Method java/io/PrintStream.println:(I)V
      41: sipush        -129
      44: istore        6
      46: bipush        -128
      48: istore        7
      50: bipush        -2
      52: istore        8
      54: iconst_m1
      55: istore        9
      57: iconst_0
      58: istore        10
      60: iconst_1
      61: istore        11
      63: iconst_5
      64: istore        12
      66: bipush        6
      68: istore        13
      70: bipush        127
      72: istore        14
      74: sipush        128
      77: istore        15
      79: sipush        32767
      82: istore        16
      84: ldc           #32                 // int 32768
      86: istore        17
      88: ldc           #33                 // int 65535
      90: istore        18
      92: ldc           #34                 // int 65536
      94: istore        19
      96: ldc           #35                 // int 2147483647
      98: istore        20
     100: getstatic     #17                 // Field java/lang/System.out:Ljava/io/PrintStream;
     103: iload         20
     105: invokevirtual #23                 // Method java/io/PrintStream.println:(I)V
     108: return

这里制作一个思维导图来说明(这里仅仅是int类型):

这里写图片描述

boolean类型:

public static void main(String[] args) {    
    boolean b1 = true;
    System.out.println(b1);
    boolean b2 = false;
    System.out.println(b2);
}

反汇编之后的结果为:

public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: istore_1
       2: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
       5: iload_1
       6: invokevirtual #22                 // Method java/io/PrintStream.println:(Z)V
       9: iconst_0
      10: istore_2
      11: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
      14: iload_2
      15: invokevirtual #22                 // Method java/io/PrintStream.println:(Z)V
      18: return

可以看出,boolean类型使用了iconst_,istore__,iload_在底层是当作int类型来处理的。

byte,short,char,long,float,double类型有相同指令,这里略去,读者可以自行测试。

猜你喜欢

转载自blog.csdn.net/guo20082200/article/details/80181907