转自:
http://blog.csdn.net/chenzhp/archive/2007/09/24/1798166.aspx
源代码经过编译器编译之后便会生成一个字节码文件,字节码是一种二进制的类文件,它的内容是JVM的指令,而不像C、C++经由编译器直接生成机器码。我们不用担心生成的字节码文件的兼容性,因为所有的JVM全部遵守Java虚拟机规范,也就是说所有的JVM环境都是一样的,这样一来字节码文件可以在各种JVM上运行。 当然也包括KVM。
每一个线程都有一个保存帧的栈。在每一个方法调用的时候创建一个帧。一个帧包括了三个部分:操作栈,局部变量数组,和一个对当前方法所属类的常量池的引用。
局部变量数组也被称之为局部变量表,它包含了方法的参数,也用于保存一些局部变量的值。参数值得存放总是在局部变量数组的index0开始的。如果当前帧是由构造函数或者实例方法创建的,那么该对象引用将会存放在location0处,然后才开始存放其余的参数。
局部变量表的大小由编译时决定,同时也依赖于局部变量的数量和一些方法的大小。操作栈是一个(LIFO)栈,用于压入和取出值,其大小也在编译时决定。某些opcode指令将值压入操作栈,其余的opcode指令将操作数取出栈。使用它们后再把结果压入栈。操作栈也用于接收从方法中返回的值。
以HelloWorld程序为例,经过命令:
E:\JavaExe>javap -c HelloWorld>HelloWorld.bytecode
就会在目录下生成一个字节码文件,用编辑器打开后
Compiled from "HelloWorld.java"
class HelloWorld extends java.lang.Object{
public HelloWorld(java.lang.String,int);
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2; //String
7: putfield #3; //Field name:Ljava/lang/String;
10: aload_0
11: iconst_0
12: putfield #4; //Field idNumber:I
15: aload_0
16: aload_1
17: putfield #3; //Field name:Ljava/lang/String;
20: aload_0
21: iload_2
22: putfield #4; //Field idNumber:I
25: aload_0
26: aload_1
27: iload_2
28: invokevirtual #5; //Method StoreData:(Ljava/lang/String;I)V
31: return
public void StoreData(java.lang.String,int);
Code:
0: bipush 90
2: istore_2
3: return
void print(AnotherClass);
Code:
0: aload_1
1: bipush 10
3: putfield #6; //Field AnotherClass.a:I
6: new #7; //class AnotherClass
9: dup
10: invokespecial #8; //Method AnotherClass."<init>":()V
13: astore_1
14: aload_1
15: bipush 20
17: putfield #6; //Field AnotherClass.a:I
20: return
}
以上是经过编译后的HelloWorld的字节码文件。我们可以对照源文件来查看一些重要的指令。
class HelloWorld
{
private String name = "";
private int idNumber = 0;
public HelloWorld(String strName, int num)
{
name = strName;
idNumber = num;
StoreData(strName,num);
}
public void StoreData(String str,int i)
{
i = 90;
}
void print(AnotherClass another)
{
another.a=10;
another=new AnotherClass();
another.a=20;
}
}
class AnotherClass
{
public int a = 0;
}
void print(AnotherClass);
Code:
0: aload_1
1: bipush 10
3: putfield #6; //Field AnotherClass.a:I
6: new #7; //class AnotherClass
9: dup
10: invokespecial #8; //Method AnotherClass."<init>":()V
13: astore_1
14: aload_1
15: bipush 20
17: putfield #6; //Field AnotherClass.a:I
20: return
aload_1 把存放在局部变量表中索引1位置的对象引用压入操作栈
bipush 10 把整数10压入栈
putfield #2 把成员变量a的值设置成栈中的10,#2代表2号常量项
new #3 创建AnotherClass的对象,把引用放入栈
dup 复制刚放入的引用(这时存在着两个相同的引用)
invokespecial #4 通过其中的一个引用调用AnotherClass的构造器,初始化对象,让另一个相同引用指向初始化的对象,然后前一个引用(this)弹出栈
asstore_1 把引用保存到局部变量表中的索引1位置中,然后引用弹出栈
aload_1 把局部变量表中索引1处的值压入操作栈。
bipush 20 把整数20压入栈
putfield #2 把成员变量a的值设置成栈中的10
return 执行完毕退出
我们继续看构造函数中的一段代码:
public HelloWorld(java.lang.String,int);
Code:
将该(this)对象压入操作栈,对于实例方法和构造函数的局部变量表来说第一个入口总是这个“this”。因为你需要访问一些实例中的方法和变量。
0: aload_0
调用该类的超类构造函数,因为所有类都继承与Java.lang.Object。而该类(HelloWorld)没有new函数操作,所以编译器提供必要的字节码来调用这些基类构造器。
1: invokespecial #1; //Method java/lang/Object."<init>":()V
将该(this)对象压入操作栈
4: aload_0
字符串
5: ldc #2; //String abc
把栈中的name的值置为栈中的”abc”
7: putfield #3; //Field name:Ljava/lang/String;
同样,将this压入栈
10: aload_0
将0压入栈。
11: iconst_0
将idNumber置为栈中的0,就是上一句指令中的操作
12: putfield #4; //Field idNumber:I
将this压入栈
15: aload_0
将位于局部变量表中位置1处的方法的形参strName压入栈
16: aload_1
将name的值置为栈中的strName
17: putfield #3; //Field name:Ljava/lang/String;
将this压入栈,this总是位于局部变量表的index0处!
20: aload_0
将位于局部变量表中位置2处的方法形参num压入栈
21: iload_2
同17号操作,赋值
22: putfield #4; //Field idNumber:I
将this压入操作栈
25: aload_0
将strName压入栈
26: aload_1
将num压入栈
27: iload_2
调用方法StoreData
28: invokevirtual #5; //Method StoreData:(Ljava/lang/String;I)V
31: return
如果有()V 标志方法没有参数列表
我们观察发现,在每一个opcode指令的左边的位置序号都不是连续的。0,1,4,5,7,10……为什么?
每一个方法都有一个对应得ByteCode序列,这些值对应着每一个opcode和其参数存放的序列中的某一个索引值。为什么这些索引不是顺序的?既然每一个指令占据一个字节,那索引为什么不是0,1,2呢?原因是:一些指令的参数占据了一些bytecode数组空间。比如:
Aload_0指令没有参数,所以占有一个字节,第二个指令invokespecial,由于它本身带有参数,结果它本身和参数分别就占据了一个位置,所以,上面的1过了就不是4。
Aload_0
invokespecial
00
05
return
分享到:
相关推荐
"JVM指令手册详细完整版.pdf" 本资源是关于JVM指令的详细手册,涵盖了JVM指令的各种系列命令,包括未归类系列、const系列、push系列、ldc系列、load系列等。每个系列命令都有其特定的功能和用途,下面我们将逐一...
`JVM指令手册`是深入理解JVM内部工作原理的重要参考资料,它详细列出了JVM所支持的所有操作指令,这对于Java开发者提升技能、进行性能优化以及排查问题具有极高的价值。以下是一些关键的JVM指令及相关的知识点: 1....
JVM指令集是JVM的核心组成部分,它定义了JVM能够理解和执行的一系列低级操作指令。这些指令构成了Java程序在运行时的微观世界,对理解JVM的工作原理至关重要。下面我们将深入探讨JVM指令集及其重要性。 1. **JVM...
首先,我们来了解下JVM指令的基本结构。每个字节码由一个单字节的操作码(opcode)和可能跟随的操作数组成。操作码决定了这条指令要执行的动作,比如加载、存储、运算、控制流程等。操作数则提供了指令执行所需的...
JVM指令主要分为:本地变量表到操作数栈类指令、操作数栈到本地变量表类指令、常数到操作数栈类指令、将数组指定索引的数组推送至操作数栈类指令、将操作数栈数存储到数组指定索引类指令、操作数栈其他相关类指令、...
JVM指令集是JVM内部使用的微指令集合,这些指令构成了Java字节码的基础。在《JVM指令手册》中,主要涵盖了栈和局部变量的操作,这是理解JVM工作原理的关键部分。 1. 栈和局部变量操作: JVM使用栈来存储临时计算...
JVM指令集是JVM内部的工作语言,由一系列单字节的指令组成,每条指令都有特定的功能。这份“JVM指令查询手册”很可能包含了JVM的所有公共指令、它们的含义以及使用方式。下面,我们将深入探讨JVM指令集及其在Java...
标题《00-JVM指令手册》和描述“JVM指令手册.java代码经过javap-v可以查看java指令,配合指令手册可以查看详细操作过程”表明文档是一份关于Java虚拟机(JVM)指令的手册。这份手册将涉及JVM字节码指令,这些指令是Java...
本篇将深入探讨JVM指令集,帮助你更好地理解Java程序的内部工作原理。 JVM指令集,也称为字节码指令集,是一系列二进制编码的指令,每个指令都对应一个特定的操作。这些指令在Java源代码被编译成.class文件时生成,...
JVM指令集是JVM的核心组成部分,用于定义和执行Java字节码。每条指令都是一个字节长度的操作码(opcode),后面跟着零个或多个操作数(operand),用于对JVM栈上的数据进行操作。 助记符是一种为指令提供的易于记忆...
### JVM指令手册解析 #### 一、概述 Java虚拟机(JVM)是Java语言的核心执行环境,它负责解释和执行Java字节码。为了更好地理解Java程序在JVM中的执行流程,了解JVM指令集至关重要。本文档将详细介绍《JVM指令手册》...
### JVM指令手册知识点 #### 概述 JVM(Java虚拟机)是执行Java字节码的虚拟机,其指令集是Java程序运行的基石。JVM指令手册是开发者理解和运用JVM指令的重要资料,其中详细介绍了各个指令的作用、参数以及应用场景...
本手册将深入解析JVM指令,特别是数值类型操作、常量推送、变量加载等操作指令。 首先,简单数值类型的指令操作主要指的是将基础的数值类型如int、long、float和double等推送到操作数栈顶。这类操作包括iconst_m1、...
以下是对JVM指令集的一些关键点的详细说明: 1. **常量压入栈的指令**:这些指令用于将基本类型的常量或null压入操作栈。例如,`iconst_1`将int类型的值1压入栈,`lconst_0`将long类型的值0压入栈,`aconst_null`则...
### JVM指令集详解 #### 常量加载指令 在JVM执行代码的过程中,经常会遇到需要将特定的常量加载到操作数栈的情况。这部分内容主要介绍了如何将各种类型的常量值加载到栈中。 - **`aconst_null`**:此指令用于将`...
JVM指令码表,JVM运行原理学习的必备工具。常量入栈指令、局部变量值转载到栈中指令、将栈顶值保存到局部变量中指令、wide指令、通用(无类型)栈操作指令、类型转换指令、整数运算、浮点运算等指令。
1. JVM指令类型:JVM指令可以分为不同类型,包括将常量推送到操作数栈的指令(const系列指令),以及用于将单个数值(如int、long、float、double)推送到操作数栈的指令(iconst系列、lconst系列、fconst系列、d...
JVM指令集是其内部工作原理的基础,每条指令都有特定的功能,用于构建和执行Java应用程序。下面我们将深入探讨JVM指令手册中的关键概念和知识点。 1. **字节码**:Java源代码编译后生成的是字节码文件(.class),...
jvm指令码参照表,可以根据此表找到javap命令解析出来的文件进行翻译。 0x01 aconst_null null值入栈。 0x02 iconst_m1 -1(int)值入栈。 0x03 iconst_0 0(int)值入栈。 0x04 iconst_1 1(int)...