JVM实战与原理


目录

字节码执行引擎

1. 方法区

2. 栈帧

2.1 局部变量表

2.2 操作数栈

2.3 动态连接

2.4 方法返回地址


字节码执行引擎

章节目的:虚拟机是如何找到正确的方法,如何执行方法内的字节码,以及执行代码时涉及的内存结构


引言:前面我们已经知道虚拟机加载类的过程,并且开辟出怎么样的内存结构,那么接下来虚拟机是怎么执行类代码的呢?

我们以Person类为例,讲解其从main开发到结束,字节码执行引擎是如何进行的。

class Person {public static void main(String[] args) {System.out.println(methodA(true, 0));}public static String methodA(boolean isShow, int count) {String str = new String("");if(isShow) {str = new String("show");count = count + 1;}return str;}
}

其对应的Class文件内容反编译结果如下

class Personminor version: 0major version: 52flags: ACC_SUPER
Constant pool:#1 = Methodref          #10.#23        // java/lang/Object."<init>":()V#2 = Fieldref           #24.#25        // java/lang/System.out:Ljava/io/Print                                                                                                                Stream;#3 = Methodref          #9.#26         // Person.methodA:(ZI)Ljava/lang/Strin                                                                                                                g;#4 = Methodref          #27.#28        // java/io/PrintStream.println:(Ljava/                                                                                                                lang/String;)V#5 = Class              #29            // java/lang/String#6 = String             #30            //#7 = Methodref          #5.#31         // java/lang/String."<init>":(Ljava/la                                                                                                                ng/String;)V#8 = String             #32            // show#9 = Class              #33            // Person#10 = Class              #34            // java/lang/Object#11 = Utf8               <init>#12 = Utf8               ()V#13 = Utf8               Code#14 = Utf8               LineNumberTable#15 = Utf8               main#16 = Utf8               ([Ljava/lang/String;)V#17 = Utf8               methodA#18 = Utf8               (ZI)Ljava/lang/String;#19 = Utf8               StackMapTable#20 = Class              #29            // java/lang/String#21 = Utf8               SourceFile#22 = Utf8               Person.java#23 = NameAndType        #11:#12        // "<init>":()V#24 = Class              #35            // java/lang/System#25 = NameAndType        #36:#37        // out:Ljava/io/PrintStream;#26 = NameAndType        #17:#18        // methodA:(ZI)Ljava/lang/String;#27 = Class              #38            // java/io/PrintStream#28 = NameAndType        #39:#40        // println:(Ljava/lang/String;)V#29 = Utf8               java/lang/String#30 = Utf8#31 = NameAndType        #11:#40        // "<init>":(Ljava/lang/String;)V#32 = Utf8               show#33 = Utf8               Person#34 = Utf8               java/lang/Object#35 = Utf8               java/lang/System#36 = Utf8               out#37 = Utf8               Ljava/io/PrintStream;#38 = Utf8               java/io/PrintStream#39 = Utf8               println#40 = Utf8               (Ljava/lang/String;)V
{Person();descriptor: ()Vflags:Code:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>                                                                                                                ":()V4: returnLineNumberTable:line 1: 0public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=3, locals=1, args_size=10: getstatic     #2                  // Field java/lang/System.out:Ljav                                                                                                                a/io/PrintStream;3: iconst_14: iconst_05: invokestatic  #3                  // Method methodA:(ZI)Ljava/lang/S                                                                                                                tring;8: invokevirtual #4                  // Method java/io/PrintStream.prin                                                                                                                tln:(Ljava/lang/String;)V11: returnLineNumberTable:line 4: 0line 5: 11public static java.lang.String methodA(boolean, int);descriptor: (ZI)Ljava/lang/String;flags: ACC_PUBLIC, ACC_STATICCode:stack=3, locals=3, args_size=20: new           #5                  // class java/lang/String3: dup4: ldc           #6                  // String6: invokespecial #7                  // Method java/lang/String."<init>                                                                                                                ":(Ljava/lang/String;)V9: astore_210: iload_011: ifeq          2814: new           #5                  // class java/lang/String17: dup18: ldc           #8                  // String show20: invokespecial #7                  // Method java/lang/String."<init>                                                                                                                ":(Ljava/lang/String;)V23: astore_224: iload_125: iconst_126: iadd27: istore_128: aload_229: areturnLineNumberTable:line 8: 0line 9: 10line 10: 14line 11: 24line 13: 28StackMapTable: number_of_entries = 1frame_type = 252 /* append */offset_delta = 28locals = [ class java/lang/String ]
}
SourceFile: "Person.java"

1. 方法区

在类加载后,会将Class文件中的类信息、常量、静态变量以及每个方法的信息和对应的字节码指令放入方法区内存中,并将其中的static修饰的变量通过<clinit>方法进行初始化。

2. 栈帧

作用:支持虚拟机进行方法调用和方法执行的数据结构,存储了方法的局部变量表、操作数栈、动态连接和方法返回地址

一个方法对应着一个栈帧,同时当前方法位于栈顶,例如进入main方法,此时main方法在栈顶,此时main方法中调用了methodA方法,则methodA入栈,位于栈顶。

下面我们详细介绍栈帧的组成部分

2.1 局部变量表

作用:存放方法参数和方法内部定义的局部变量。

局部变量表以容量槽Slot为最小单位,其中boolean、byte、char、short、int、float、reference或returnAddress为一个Slot,long和double为两个Slot。一般来说一个Slot占用32位长度的内存空间,其中reference类型表示对一个对象实例的引用,通过引用,能查找到对象在堆中数据存放的起始地址索引。returnAddress已经很少见了,已被异常表代替。

实例:Person类的methodA方法中,isShow、count和str便是存放在局部变量表中,其中isShow为boolean类型,count为int类型,str则是reference类型。

如果执行的是实例(非static)方法,那局部变量表的第0位索引的Slot默认是方法所属对象实例的引用,在方法中用关键字this来访问到这个隐含的参数

2.2 操作数栈

作用:用于存放方法执行过程中,字节码指令进行入栈和出栈操作时,写入和提取的内容。

boolean、byte、char、short、int、float所占的栈容量为1,long和double所占的栈容量为2。

实例:Person类的methodA方法中,count = count + 1;便是在操作数栈中进行,先将count入栈,接着1入栈,然后执行栈顶头两个元素相加的指令,然后出栈。

2.3 动态连接

符号引用一部分会在类加载阶段或者第一次使用的时候就转化为直接引用,这种转化称为静态解析。

另外一部分将在每一次运行期间转化为直接引用,这部分成为动态连接。

2.4 方法返回地址

作用:方法退出之后,需要返回到方法被调用的位置,程序才能继续执行。

方法有两种退出方式

1. 正常完成出口:执行引擎遇到任意一个方法返回的字节码指令,这时候可能会有返回值传递给上层的调用者,此种退出方式,栈帧中会保存调用者PC计数器的值。

2. 异常完成出口:当方法执行过程遇到异常,且异常没有在方法体内得到处理,只要在方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,此种退出方式,返回地址是要通过异常处理器表来确定,栈帧中不保存这部分信息。

Person类的方法执行过程:

虚拟机会自动寻找到main方法进行执行。执行方法,则会将main方法生成栈帧压入虚拟机栈中。然后main方法的字节码如下

0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: iconst_1
         4: iconst_0
         5: invokestatic  #3                  // Method methodA:(ZI)Ljava/lang/String;
         8: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        11: return

main方法中调用了methodA方法,则会将methodA压入栈,此时methodA的局部变量表会存有isShow、count及str变量。

methodA的字节码如下

0: new           #5                  // class java/lang/String
 3: dup
 4: ldc           #6                  // String
 6: invokespecial #7                  // Method java/lang/String."<init> 
 9: astore_2
10: iload_0
11: ifeq          28
14: new           #5                  // class java/lang/String
17: dup
18: ldc           #8                  // String show
20: invokespecial #7                  // Method java/lang/String."<init> 
23: astore_2
24: iload_1
25: iconst_1
26: iadd
27: istore_1
28: aload_2
29: areturn
String类信息在加载阶段会被加载至方法区中,new指令会在堆中开辟空间存放对象实例,引用str压入操作数栈栈顶。

dup指令会将栈顶的str复制并压入栈顶

ldc将空字符串从方法区的常量池中推送至栈顶

invokespecial指令则会调用String类的初始化方法

astore_2指令将栈顶引用型数值存入第三个本地变量中

这样,经过以上四个指令,str则被初始化成功,其类信息在方法区中,引用在栈的局部变量表中,实例信息则在堆中。

最后的areturn,则是方法的退出指令,此时方法返回地址记录着main方法调用methodA方法的地址,在methodA方法结束返回后,便会根据这个返回地址返回到main方法中。

main方法中调用println打印后,调用return指令同样也会进行方法退出,至此,整个main方法便执行结束了。

JVM实战与原理---字节码执行引擎相关推荐

  1. JVM类加载机制_字节码执行引擎_Java内存模型

    类加载机制: 类加载生命期:加载(Loading),验证(Verification),准备(Preparation),解析(Resolution),初始化(Initialization),使用(Usi ...

  2. JVM(5)—字节码执行引擎

    JVM中的执行引擎在执行java代码的时候,一般有解释执行(通过解释器执行)和编译执行(通过即时编译器产生本地代码执行)两种选择. 栈帧 定义: 栈帧是用于支持快看小说网虚拟机进行方法调用和方法执行的 ...

  3. 深入理解JVM虚拟机(七):虚拟机字节码执行引擎

    代码编译的结果就是从本地机器码转变为字节码.我们都知道,编译器将Java源代码转换成字节码?那么字节码是如何被执行的呢?这就涉及到了JVM字节码执行引擎,执行引擎负责具体的代码调用及执行过程.就目前而 ...

  4. 第六章JVM虚拟机字节码执行引擎——类文件和类加载之前必看

    文章目录 虚拟机字节码执行引擎 运行时栈帧结构 局部变量表(Local Variables) 操作数栈 动态链接(Dynamic Linking) 方法返回地址 附加信息 方法调用 解析 分派 虚方法 ...

  5. jvm(8)-虚拟机字节码执行引擎

    [0]README 0.1)本文转自 "深入理解jvm",旨在学习 虚拟机字节码执行引擎 的基础知识: [1]概述 1)物理机和虚拟机的执行引擎: 物理机的执行引擎是直接建立在处理 ...

  6. 《深入理解Java虚拟机》笔记5——类加载机制与字节码执行引擎

    第七章 虚拟机类加载机制 7.1 概述 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 在J ...

  7. JAVA 虚拟机类加载机制和字节码执行引擎

    引言 我们知道java代码编译后生成的是字节码,那虚拟机是如何加载这些class字节码文件的呢?加载之后又是如何进行方法调用的呢? 一 类文件结构 无关性基石 java有一个口号叫做一次编写,到处运行 ...

  8. 深入理解Java虚拟机(周志明第三版)- 第八章:虚拟机字节码执行引擎

    系列文章目录 第一章: 走近Java 第二章: Java内存区域与内存溢出异常 第三章: Java垃圾收集器与内存分配策略 代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言 ...

  9. JAVA类加载对字节码的处理_深入理解Java虚拟机(类文件结构+类加载机制+字节码执行引擎)...

    [本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 周志明的< ...

最新文章

  1. QIIME 2用户文档. 18序列双端合并read-joining(2019.7)
  2. worktools-源码下拉问题
  3. Nginx配置——防盗链
  4. 修改android init.rc,busybox及bash在android中的安装及init.rc修改
  5. centos 下载oracle_Linux镜像下载大全
  6. python重命名窗口_Python:即时重命名方法名称
  7. 数据挖掘算法之-关联规则挖掘(Association Rule)(购物篮分析)
  8. python3调用c语言数组,使用Python中的ctypes访问数组
  9. 百度-提高效率的高级搜索语法
  10. 资源配置文件的目录结构以及相应的读取方式
  11. oralce 存储过程、函数和触发器
  12. Head First 设计模式 —— 工厂模式与工厂方法
  13. 【CTF整理】电子取证之Easy_dump(18护网杯)
  14. DHCP的IP地址租约、释放
  15. linux jnlp显示异常,Web启动异常 JNLP
  16. 计算数的位数c语言 百度作业帮,C语言多位数计算
  17. 数组里的字符串转换成数字或者把数字转换成字符串
  18. vim编辑器的简单使用(参考别人文章的学习笔记)
  19. android版本分类
  20. 深度分析:云控系统有什么功能,工作室用的话咋样?

热门文章

  1. Nginx+tomcat集群的session共享问题
  2. Python3学习笔记2:简易Web爬虫
  3. Zookeeper与Paxos
  4. Android零基础入门第24节:自定义View简单使用
  5. Spring嵌套事务控制
  6. Android自定义退出弹出框
  7. 字符串多模式精确匹配(脏字/敏感词汇搜索算法)——TTMP算法 之理论(转)...
  8. Silverlight实用窍门系列:40.Silverlight中捕捉视频,截图保存到本地【附带实例源码】...
  9. vista下安装vs2005
  10. 【Rain in ACStar HDU-3340】