- 浏览: 425675 次
- 性别:
- 来自: 成都
文章分类
最新评论
-
binghc:
能使用浏览器作为客户端么,用socket构建一个最简单的htt ...
HTTPS那些事 用java实现HTTPS工作原理 -
littleShyBoy:
如果是使用http client请求https的接口,http ...
HTTPS那些事 用java实现HTTPS工作原理 -
skw1975:
...
HTTPS那些事 用java实现HTTPS工作原理 -
sealinesu:
看了半天,真的是半天,总算是把这些概念都理清了,谢谢博主
spring事务传播机制实例讲解 -
wanghaozdw:
请问下,在内外层事务均是REQUIRED的情况下,内层事务抛出 ...
spring事务传播机制实例讲解
理解JVM的指令的一个基础是理解JVM的栈内存,因此在开始之前最好先参阅一下《Java 栈内存介绍
》。本篇将结合例子对JVM的主要指令进行描述。
在开始之前,我们先了解一下如下的 “常识”:
- 字长是根据JVM不同而定的,一般(并非一定)在32位机上是4个字节,64位机上是8个字节(使用8个字节很可能会潜在地存在内存浪费的情 况),JVM规范上要求1个字必须至少能容纳integer型的值(4字节),2个字必须至少能容纳long型的值(8个字节)。JVM有不少定义会以字 为单位,譬如reference(引用)、本地变量和栈
- JVM操作由操作码和操作数组成,操作码是1字节的,因此最多只有256个操作码,操作数从0-n个字节不等(0表示没有操作数,一般是指令参数通过操作栈来获取,n不定,譬如像TABLESWITCH和LOOKUPSWITCH指令)
- 每个操作如果需要从操作栈中读参数,则总是将这些参数出栈,如果操作有结果结果,总是会将结果入栈,后面可能会重复提到一点,如果没有提到,这是一个参考准则
- 本地变量是以字为单位(如上,32位机一般是4个字节,也有一些64位的JVM字长是8个字节)为单位的,即使值是byte或short,对于long、double型的数据,在本地变量区中会占用2个位置(slot)
- 操作栈是以字为单位(如上,32位机一般是4个字节) ,即使值是byte或short,而对于long、double型的数据,在操作栈中会占用2个位置(slot)
- 在如下的描述中,指令=操作码+操作数
1.主要运算指令
1)常量操作指令
- BIPUSH x :将x(单字节,-128—127)入操作栈,注意,如上常识说明,入栈的值在操作栈中会占用1个字长,BIPUSH就是将B(Byte,1个字节)转换成I(Integer ,4个字节),然后PUSH到操作栈
- SIPUSH x :将x(双字节,?32768— 32767)入操作栈,注意,如上常识说明,在操作栈中会占用1个字长,BIPUSH就是将S(Short,2个字节)转换成I(Integer ,4个字节),然后PUSH到操作栈
- ICONST_n(n是-1—5)/ LCONST_n(n是0-1):对于x比较小的情况的BIPUSH的高效版本,没有操作数,指令是单字节的。另外,LCONST_n在操作栈中会占用2个位置(long型)
- FCONST_n(n是0-2)/DCONST_n(n是0-1):与上面类似,F表示float、D表示double,float会占用1个操作栈位置(slot,1个字长),double会占用2个操作栈位置
- ACONST_NULL:将null指针入栈
- LDC cst:将常量池偏移量为cst的值入栈,譬如LDC #12,在操作栈中会占用1个字长
- LDC2_W cst:将常量池偏移量为cst的值入栈,在栈中会占用2个位置,譬如LDC _W #13。
可能会很奇怪地发现,JVM使用了前缀I、F、A这种实际效果差不多的不同指令,实际上其目的是为了JVM在进行字节码验证的时候更好地检查类型。
2)Local变量操作指令
- ILOAD/LLOAD/FLOAD/DLOAD/ALOAD x:将第x个本地变量入栈,其中I是integer、L是long、F是float、D是double、A是地址(指对象地址),需要注意的是,本地变量 是以字长为单位的,因此像LLOAD x/DLOAD x,实际上会将本地变量第x+1和x+2两个位置的值入栈。
- ILOAD_n/LLOAD_n/FLOAD_n/DLOAD_n/ALOAD_n(n是0-3):单字节指令,如上操作的高效版本,将第n个本地变量入栈
- ISTORE/LSTORE/FSTORE/DSTORE/ASTORE x:从栈顶pop出值,存到第x个本地变量中,与上类似,本地变量是以4个字节为单位,因此像LSTORE x/DSTORE x,实际上会pop出两个值,分别存储到地变量第x+1和x+2两个位置上
- ISTORE_n/LSTORE_n/FSTORE_n/DSTORE_n/ASTORE_n(n是0-3):单字节指令,如上操作的高效版本,从栈顶pop出值存储到第n个本地变量中
- IINC x n:将第x个本地变量递增n
与上类似的,JVM使用了前缀I、F、A这种实际效果差不多的不同指令,实际上其目的是为了JVM在进行字节码验证的时候更好地检查类型。
3)栈操作指令
- POP /POP2:从栈中弹出1/2个位置内容
- DUP/DUP2/DUP_X1/DUP_X2/DUP2_X1/DUP2_X2:栈复制指令,DUP/DUP2表示复制栈顶1/2个字的内容, 并入到栈顶,DUP_X1表示复制堆顶偏移1个位置的1个字的内容,并入到顶栈(譬如堆顶是word1, word2,word1是栈顶,执行后变成word2,word1,word2), DUP_X2表示复制堆顶偏移2个位置的1个字的内容,并入到顶栈(譬如堆顶是word1, word2,word3,word1是栈顶,执行后变成word3,word1,word2,word3),DUP2_X1表示复制堆顶偏移1个位置的2 个字的内容,并入到顶栈(譬如堆顶是word1, word2,word3,word1是栈顶,执行后变成word2,word3,word1,word2,word3),DUP2_X2表示复制堆顶偏移 2个位置的2个字的内容,并入到顶栈(譬如堆顶是word1, word2,word3,word4,word1是栈顶,执行后变成word3,word4,word1,word2,word3,word4)
- SWAP:交换栈顶2个字的内容,譬如堆顶是word1, word2,交换后是word2,word1
4)运算指令
运算指令非常简单,单指令,没有操作数,操作的参数放在栈中,运算的时候都是从栈顶弹出2个参数(对于D开头的指令,每个参数是2个字,则会弹出2*2个
字的信息),运算完后把结果入栈顶(根据类型不同,结果会占用1-2个字),更详细可以看看《Java 栈内存介绍
》中的范例
- IADD/LADD/FADD/DADD: a + b
- ISUB/LSUB/FSUB/DSUB: a - b
- IMUL/LMUL/FMUL/DMUL: a * b
- IDIV/LDIV/FDIV/DDIV: a / b
- IREM/LREM/FREM/DREM: a % b
- INEG/LNEG/FNEG/DNEG: -a
- ISHL/LSHL: a < < n
- ISHR/LSHR: a > > n
- IUSHR/LUSHR: a > > > n
- IAND/LAND :a & b
- IOR/LOR: a | b
- IXOR/LXOR: a ^ b
- LCMP:a == b ? 0 : (a < b ? -1 : 1)
- FCMPL, FCMPG: a == b ? 0 : (a < b ? -1 : 1)
- DCMPL, DCMPG ... , a , b ... , a == b ? 0 : (a < b ? -1 : 1)
5)类型转换指令
类型转换指令,非常简单,看后面例子就能明白
- I2B/I2C/I2S/I2L/I2F/I2D
- L2I/F2I/L2F/L2D
- D2I/D2L/D2F
- F2D/F2L
- CHECKCAST class:检查栈顶的元素是否为指定的类型,class是常量池的偏移量
6)范例:
下面的例子演示了如上的大部分类型的字节码的功能
public long compute() { short i = 80; int j = 1000; long result = i * j + 1000000; return result; }
public long compute();
Code:
Stack=2, Locals=5, Args_size=1 //声明了栈的最大深度、本地字数和传入参数数,对于对象方法,会传入this引用,因此这里Arg_szie=1,如上的程序,this会占用1个 字,i和j分别占1个字,result占2个字(long),因此这里Locals=5
0: bipush 80 //将80入到栈,在栈中会占1个字的位置
2: istore_1 //将栈顶值弹出设给第2个本地变量(传入参数也会以本地变量的方式存在,在这了第1个参数是this),这两段指令等价于short i = 80,从这里可以看出,JVM直接把short当做integer来运算的
3: sipush 1000 //与上类似,把1000入到栈顶,这里1000超过了b所能表示的范围,所以是sipush
6: istore_2 //同样的,把堆栈值弹出并设给第3个本地变量,这两段等价于int j = 1000
7: iload_1
8: iload_2 //把第2个本地变量(i和j)入栈
9: imul //乘运算,弹出2个栈顶值(i和j),并把运算结果入栈,这时候栈顶值就是 i* j
10: ldc #16; //1000000超过short能够表示的范围,会以常量池中条目的形式存在,这里#16就是1000000,这里把1000000入栈
12: iadd //弹出栈顶值2个字的值,并进行add操作,把add结果再入栈,这时i*j和1000000被弹出栈,并把i*j+1000000的值入栈
13: i2l //从栈顶弹出1个字的值,并转换成l型,再入到栈中(这时候,i*j+1000000会占用栈顶2个字的位置。
14: lstore_3 //从栈顶弹出2个字(因为是l型的),并把结果赋给第4和第5个local位置(l需要占2个位置),想当于把运算结果赋给result
15: lload_3 //将第4和第5个local位置的值入栈
16: lreturn //返回指令,将栈顶2个位置的值弹出,并压入方法调用者的操作栈(上一个方法的操作栈),同时把本方法的操作栈清空
2.操作指令
1)数组操作指令:
- NEWARRAY type :创建基本类型(int、long等)数组,数组的长度从操作栈中获取
- ANEWARRAY/MULTIANEWARRAY class :ANEWARRAY创建指定类型数组,需要注意的是,数组本身就是一种类型,因此创建多维数组可以使用这个指令来进行,MULTIANEWARRAY是对齐多维数组的一个快速版本
- BALOAD/CALOAD/SALOAD/IALOAD/LALOAD/FALOAD/DALOAD/AALOAD :将数组的某个下标的值入栈
- BASTORE/CASTORE/SASTORE/IASTORE/LASTORE/FASTORE/DASTORE/AASTORE:将操作栈中的值设给数组的某个小标
- ARRAYLENGTH:将数组的长度入栈
下面我们看一个演示例子
public void test() { //单维数组 int[] iarray = new int[10]; iarray[3] = 10; int length = iarray.length; int result = iarray[3]; //对象数组 Object[] objs = new Object[10]; }
Code:
Stack=3, Locals=6, Args_size=1
0: bipush 10 //将数组长度入栈
2: newarray int //创建int[10],并将数组引用入栈
4: astore_1 //将创建的数组的引用出栈,赋给第2个本地变量,即iarray
5: aload_1 //将iarray入栈
6: iconst_3 //数组下标是3
7: bipush 10 //值是10
9: iastore //设置iarray[3] = 10,并将3个值出栈
10: aload_1 //将iarray入栈
11: arraylength //将iarray出栈,获得数组长度,并将长度值入栈
12: istore_2 //将数组长度值出栈,并赋给第3个本地变量,即length
13: aload_1 //将iarray入栈
14: iconst_3 //数组下标是3
15: iaload //将如上2个参数出栈,并将iarray[3]的值入对栈
16: istore_3 //将栈顶值(即iarray[3])出栈,并赋给第4个本地变量,即使result
17: bipush 10
19: anewarray #3; //class java/lang/Object,创建Object数组
22: astore 4
24: return
2)对象相关指令
- NEW class:创建对象,并将对象入栈顶
- GETFIELD/PUTFIELD/GETSTATIC/PUTSTATIC field:读属性、写属性值,如果是读,将结果入栈顶
- INVOKEVIRTUAL/INVOKESPECIAL/INVOKESTATIC/INVOKEINTERFACE:方法调用,参见上一篇的描述
- INSTANCEOF class:栈顶值是否为指定的类型,instanceof语句的字节码对应
- MONITORENTER/MONITOREXIT:进入、退出monitor,即对象锁,在JVM级别上支持synchronized
我们来看一个范例
private int age; public void test() { Object obj = new Object(); synchronized (obj) { int result = age; } }
Code:
Stack=2, Locals=4, Args_size=1
0: new #3; //class java/lang/Object //创建Object对象,并将引用入栈
3: dup
4: invokespecial #10; //Method java/lang/Object."<init>":()V //调用Object对象的构造函数,因为方法调用会弹出参数(这里是Object对象),因此需要上面的dup指令,保证在调用构造函数之后栈顶上还是 Object对象的引用,很多种情况下dup指令都是为这个目的而存在的
7: astore_1
8: aload_1
9: dup
10: astore_2
11: monitorenter //进入Object对象的锁,这里会弹出Object的引用,因此需要注意保存锁对象引用本身
12: aload_0
13: getfield #17; //Field age:I //读age属性,注意,这里可能会抛出异常,这里需要确保进入Object对象的锁后准确地在退出的时候调用monitorexit,看后面的异常表
16: istore_3
17: aload_2
18: monitorexit
19: goto 25
22: aload_2
23: monitorexit
24: athrow
25: return
Exception table:
from to target type
12 19 22 any
22 24 22 any
3)跳转指令
- IF_ICMPEQ/IF_ICMPNE/IF_ICMPLT/IF_ICMPGE/IF_ICMPGT/IF_ICMPLE/IF_ACMPEQ/IF_ACMPNE position:比较2个栈顶值,如果符合条件,则跳转到position位置
- IFEQ/IFNE/IFLT/IFGE/IFGT/IFLE/IFNULL/ IFNONNULL position:将栈顶值与0(如果是引用,则是null)比较,如果符合条件,则跳转到position位置
- GOTO position:无条件转移
- TABLESWITCH/LOOKUPSWITCH:在JVM级别上直接支持switch 语句,非常有意思的是,编译器编译的时候,会根据case条件的不同翻译成TABLESWITCH或者LOOKUPSWITCH, LOOKUPSWITCH在实现上会逐条比较case语句中的值,如果匹配,则跳转到相应的语句,TABLESWITCH则适用于case中的值比较连 续,譬如case为1,2, 3,4这种,TABLESWITCH会存储最小值(如1)和最大值(如4),比较时会先判断是否在这个范围内,如果在这个范围内,则直接根据偏移量就可以 定位到时间目标语句,否则则跳到default语句,TABLESWITCH可以理解为是符合某种特征行为的LOOKUPSWITCH的高效实现。在 JDK5之后switch语句支持enum型的处理,实际是在编译器级别处理的,有兴趣可以自己写一个范例测试一下编译器是如何处理成字节码的。
我们看看例子来理解一下这些跳转指令
public int test(int i) { if (i > 100) { return 200; } //case语句比较连续,会翻译成tableswitch switch (i) { case 1: return 1; case 2: return 2; } //case语句不连续,会翻译成lookupswitch switch (i) { case 1: return 1; case 100: return 100; } return 0; }
Code:
Stack=2, Locals=2, Args_size=2
0: iload_1 //将第2个参数入栈,即i
1: bipush 100 //将100入栈
3: if_icmple 10 //如果i<=100,则跳转到第10条语句
6: sipush 200
9: ireturn //返回200
10: iload_1 //将第2个参数入栈,即i
11: tableswitch{ //1 to 2
1: 32;
2: 34;
default: 36 }
//case语句比较连续,使用tableswitch
32: iconst_1
33: ireturn
34: iconst_2
35: ireturn
36: iload_1
37: lookupswitch{ //2
1: 64;
100: 66;
default: 69 }
//case语句不连续,使用lookupswitch
64: iconst_1
65: ireturn
66: bipush 100
68: ireturn
69: iconst_0
70: ireturn
4)返回指令
返回指令没什么好说,上面几个例子都会涉及到
- IRETURN/LRETURN/FRETURN/DRETURN/ARETURN:将栈顶值返回
- RETURN:返回
- ATHROW:抛出栈顶对象
5)指令quick版本
在上面指令中,有很多指令是需要涉及到引用解析的,譬如NEW、LDC、NEWARRAY等,虽然整个过程只需要一次解析,但每次都需要去判断需不需要解
析。为了避免这种无谓的判断,Sun
JVM在实现上对这些指令有一个快速的指令版本,在运行的时候,如果已经引用已经解析过了,则把相应的指令替换快速的指令版本,快速引用会直接使用解析后
的结果。
- LDC_QUICK/LDC_W_QUICK/LDC2_W_QUICK
- GETFIELD_QUICK/PUTFIELD_QUICK/GETFIELD2_QUICK/PUTFIELD2_QUICK/GETSTATIC_QUICK/PUTSTATIC_QUICK/GETSTATIC2_QUICK/PUTSTATIC2_QUICK/GETFIELD_QUICK_W/PUTFIELD_QUICK_W
- INVOKEVIRTUAL_QUICK/INVOKENONVIRTUAL_QUICK/INVOKESUPER_QUICK/INVOKESTATIC_QUICK/INVOKEINTERFACE_QUICK/INVOKEVIRTUALOBJECT_QUICK/INVOKEVIRTUAL_QUICK_W
- NEW_QUICK/ANEWARRAY_QUICK/MULTIANEWARRAY_QUICK
- CHECKCAST_QUICK/INSTANCEOF_QUICK
2.其他相关
1)Exception表
异常的支持是JVM级别的,对于每个方法,class文件中会携带该方法的异常和处理handler信息,如下,第1行表示代码在执行0-8行的时候,如
果有java.lang.Exception抛出,则跳转到11条指令,第2行表示代码在执行0-20行的时候,如果有异常抛出(不管什么类型的异常),
则跳转到第29条指令
[0-8): 11 - java.lang.Exception
[0-20): 29
关于异常更多信息,可参见《[字节码系列]ObjectWeb ASM构建Method Monitor
》
2)LineNumber表
LineNumber存储了字节码与源码的行数的对应关系,其存在是为了支持Exception或者调试的时候,可以正确地获得代码所在的行数,如下
line 8: 0
line 9: 8
line 11: 12
line 9: 17
line 13: 25
这个表不是必要的,有些编译器或者选项会选择不编译到class文件中
3)LocalVariable表
LocalVariable存储了本地变量在源码中的实际名字,如下
Start Length Slot Name Signature
0 25 0 this Ltest/Test3;
5 20 1 iarray [I
13 12 2 length I
17 8 3 result I
24 1 4 objs [Ljava/lang/Object;
这个表不是必要的,有些编译器或者选项会选择不编译到class文件中
发表评论
-
volatile使用场景以及注意事项
2014-08-14 10:40 776Java 语言中的 volatile 变 ... -
HTTPS那些事 用java实现HTTPS工作原理
2014-08-13 19:34 64639今天被问到关于https原理的问题,结果 ... -
实现一个字符串的压缩功能
2013-03-15 17:52 975如题: 写一个函数,实现对字符串的压缩 String ... -
activeMQ-failover协议细节
2013-02-01 15:31 5693Apache ActiveMQ - The Failover ... -
分布式网络爬虫构建参考
2013-01-24 17:31 1909本文转自csdn 设计和实 ... -
GC原理
2013-01-24 14:58 906GC的基本原理 Java ... -
jvm参数设置
2013-01-24 14:51 918本文转自http://blog.csdn.net/zsugu ... -
同样的代码和数据文件,为什么在eclipse中运行和在控制台运行的结果不一样?
2012-12-04 10:42 2680今天遇到一个很诡异的问题,同一样的代码和同一个数据文件,在ec ... -
spring配置多个PropertyPlaceholderConfigurer
2012-09-28 13:25 2874在spring中配置多个PropertyPlaceholder ... -
jboss集群配置
2012-09-25 10:02 916JBoss cluster ... -
spring事务传播机制实例讲解
2012-09-18 11:16 36077天温习spring的事务处理机制,总结如下 ... -
spring事务传播机制实例讲解
2012-09-17 18:19 0天温习spring的事务处理机制,总结如下 对于 ... -
spring事务传播机制实例讲解
2012-09-17 18:18 0天温习spring的事务处理机制,总结如下 对于 ... -
spring事务传播机制实例讲解
2012-09-17 18:16 0天温习spring的事务处理机制,总结如下 对于 ... -
oracle锁类型
2012-09-07 18:20 935为了防止用户在同一时间并发地访问和修改资源,ORACLE使用不 ... -
ibatis更新clob
2012-08-31 17:22 1836今天使用ibatis进行更新clob字段,网上查了一大堆 ... -
java url connection
2012-08-28 13:55 0Java发送http请求 (get 与 post方法请求),以 ... -
js 数组操作
2012-07-26 09:58 764js数组的操作 用 js有很久了,但都没有 ... -
spring quartz配置
2012-05-16 13:51 933spring多个定时任务quartz ... -
java编译为exe可执行文件
2012-04-28 19:35 3713huliqing 沉淀... ...
相关推荐
在了解JVM字节码之前,对于Java程序员而言,掌握Java底层知识,尤其是虚拟机和字节码的工作原理,对于成为一名优秀的开发者是很有帮助的。因此,本文从最简单的Java程序——打印"Hello, World"开始,逐步介绍如何将...
JVM指令手册详细记录了JVM的所有操作码(opcode),也就是字节码指令。这些指令是给JVM解释器或者即时编译器(JIT)使用的低级指令集。在JVM上运行的Java程序会被编译成一系列指令,然后由JVM执行。 从给定文件的...
Java虚拟机(JVM)是Java程序运行的核心,它通过解析和执行字节码来实现程序的运行。字节码是一系列二进制指令,这些指令在类文件中以16进制形式表示,每条指令占据一个或多个字节。`JVM指令码表.zip`包含的`JVM指令...
这个压缩包文件"JVM优化3(Tomcat参数调优,JVM参数调优,jvm字节码,代码优化).zip"显然包含了关于如何优化Java应用程序运行效率的四个主要方面:Tomcat服务器的参数调整、JVM参数调优、JVM字节码理解和优化以及代码...
Java虚拟机(JVM)是Java程序运行的基础,它负责解释和执行Java字节码,为程序员提供了跨平台的运行环境。JVM指令手册是理解JVM内部工作原理的重要参考资料,其中包含了JVM执行的所有操作指令,这些指令是构建和优化...
程序计数器是当前线程所执行的字节码的行号指示器,是线程私有的内存区域。由于Java是多线程并发执行的,每个线程都需要有一个独立的程序计数器,以便记录线程切换后继续执行的位置。 #### 虚拟机栈 虚拟机栈是Java...
它们可以帮助开发者查看类文件的结构,理解字节码指令的含义,甚至修改字节码以实现调试、优化或逆向工程的目的。 5. **字节码优化**: 通过理解字节码,开发者可以进行针对性的优化,比如减少对象创建、优化循环...
Java字节码指令集定义了一系列指令,用于控制JVM的行为,包括数据加载、存储、算术运算、跳转等。 #### 指令解析 下面将详细介绍部分Java字节码指令及其功能: ##### A 类型指令 这些指令主要涉及对象引用的加载和...
这些指令共同构成了JVM字节码指令集,是Java程序运行时的基础。了解这些指令有助于深入理解Java程序的运行机制,并有助于性能调优和错误排查。由于文档内容中存在OCR技术导致的错误和遗漏,因此在实际分析字节码时,...
通过深入学习JVM指令,开发者可以更好地理解字节码层面的运行机制,从而编写出更高效、更稳定的Java程序。同时,对于JVM调优、内存泄漏检测、性能瓶颈定位等工作,掌握这些指令的含义和用法也是必不可少的。
Java虚拟机(JVM)是Java程序运行的基础,它通过解释和执行字节码来实现跨平台的特性。JVM指令集是JVM的核心组成部分,它定义了JVM能够理解和执行的一系列低级操作指令。这些指令构成了Java程序在运行时的微观世界,...
JVM指令分为五类:操作数栈管理指令、局部变量表操作指令、控制流指令、字节码操作指令和对象及数组操作指令。这些指令共同构成了Java程序的运行基础。 2. **操作数栈管理指令** 操作数栈是JVM中存储数据的地方。...
Java字节码指令集是Java虚拟机(JVM)执行程序的基础,它是Java源代码经过编译后的二进制表示形式。...通过深入研究字节码指令,开发者可以更好地理解JVM如何执行程序,从而编写更高效、更可靠的Java应用。
这份手册将涉及JVM字节码指令,这些指令是Java程序在JVM上运行时所执行的基本命令。在Java源代码编译成.class文件后,其中包含的Java字节码指令可通过Java的反汇编工具(如javap)转换为人类可读的文本形式。本手册...
JVM指令集的助记符通常由操作码对应的英文缩写构成,它们是字节码指令的符号化表示。 字节码是JVM能够识别和执行的一系列指令和数据,是Java平台无关性的基础,因为它允许Java程序在不同的操作系统上运行。字节码...
每条字节码指令通常只占用一个或两个字节,因此得名“字节码”。这种设计使得Java程序能够跨平台运行,因为字节码并不直接对应于特定硬件的机器指令,而是由JVM负责解释执行。这就是Java的“一次编写,到处运行”...
JVM指令集,也称为字节码指令集,是一系列二进制编码的指令,每个指令都对应一个特定的操作。这些指令在Java源代码被编译成.class文件时生成,每个类文件包含了一个方法区、一个堆、一个栈以及若干个本地方法栈。...
这份指南的目的是让读者能够理解JVM字节码指令在数值操作、常量加载和变量操作方面的具体行为。掌握了这些知识后,编程人员可以更高效地阅读和理解编译后的Java字节码,对于调试、性能优化和开发工具开发等领域都有...
Java虚拟机(JVM)是Java程序运行的核心组件,它负责解释执行字节码指令,为Java应用程序提供了一个跨平台的运行环境。JVM指令集是JVM内部使用的微指令集合,这些指令构成了Java字节码的基础。在《JVM指令手册》中,...