学习Java字节码有助于理解Java内存结构,加深对JVM的理解。
首先需要知道JVM内存由堆、栈、方法区、本地方法栈组成。
堆中存放JVM生命周期里所有的类的实例。
栈中存放函数中的基础类型局部变量、函数中实例变量的引用。
方法区存放Class、Method的信息以及Static的变量。
本地方法栈存放调用本地方法时用到的变量。
JVM为每个线程都分配了一个方法栈,方法栈包含不同方法的栈帧,每个方法都有一个栈帧,当前方法的栈帧称为当前栈帧,栈帧由局部变量区和操作数栈组成,所有线程共享堆中的数据。
下面以一个简单的Java程序HelloWorld.java为例,介绍一下Java方法调用时的内存结构。
源代码
Java代码
public class HelloWorld {
private String myWorld;
public HelloWorld(String world) {
this.myWorld = world;
}
public String getMyWorld() {
return myWorld;
}
public void setMyWorld(String myWorld) {
this.myWorld = myWorld;
}
public boolean isNull(){
if(this.myWorld==null){
return true;
}else{
return false;
}
}
public static void main(String[]args){
HelloWorld myWorld = new HelloWorld("smallWorld");
boolean isNull = false;
myWorld.setMyWorld("bigWorld");
isNull = myWorld.isNull();
}
}
public class HelloWorld {
private String myWorld;
public HelloWorld(String world) {
this.myWorld = world;
}
public String getMyWorld() {
return myWorld;
}
public void setMyWorld(String myWorld) {
this.myWorld = myWorld;
}
public boolean isNull(){
if(this.myWorld==null){
return true;
}else{
return false;
}
}
public static void main(String[]args){
HelloWorld myWorld = new HelloWorld("smallWorld");
boolean isNull = false;
myWorld.setMyWorld("bigWorld");
isNull = myWorld.isNull();
}
}
Javap -C HelloWorld
字节码:
Java代码
Compiled from "HelloWorld.java"
public class com.sitech.core.HelloWorld extends java.lang.Object{
public com.sitech.core.HelloWorld(java.lang.String);
Code:
0: aload_0
1: invokespecial #10; //Method java/lang/Object."<init>":()V
4: aload_0
5: aload_1
6: putfield #13; //Field myWorld:Ljava/lang/String;
9: return
public java.lang.String getMyWorld();
Code:
0: aload_0
1: getfield #13; //Field myWorld:Ljava/lang/String;
4: areturn
public void setMyWorld(java.lang.String);
Code:
0: aload_0
1: aload_1
2: putfield #13; //Field myWorld:Ljava/lang/String;
5: return
public boolean isNull();
Code:
0: aload_0
1: getfield #13; //Field myWorld:Ljava/lang/String;
4: ifnonnull 9
7: iconst_1
8: ireturn
9: iconst_0
10: ireturn
public static void main(java.lang.String[]);
Code:
0: new #1; //class HelloWorld
3: dup
4: ldc #27; //String smallWorld
6: invokespecial #29; //Method "<init>":(Ljava/lang/String;)V
9: astore_1
10: iconst_0
11: istore_2
12: aload_1
13: ldc #31; //String bigWorld
15: invokevirtual #33; //Method setMyWorld:(Ljava/lang/String;)V
18: aload_1
19: invokevirtual #35; //Method isNull:()Z
22: istore_2
23: return
}
Compiled from "HelloWorld.java"
public class com.sitech.core.HelloWorld extends java.lang.Object{
public com.sitech.core.HelloWorld(java.lang.String);
Code:
0: aload_0
1: invokespecial #10; //Method java/lang/Object."<init>":()V
4: aload_0
5: aload_1
6: putfield #13; //Field myWorld:Ljava/lang/String;
9: return
public java.lang.String getMyWorld();
Code:
0: aload_0
1: getfield #13; //Field myWorld:Ljava/lang/String;
4: areturn
public void setMyWorld(java.lang.String);
Code:
0: aload_0
1: aload_1
2: putfield #13; //Field myWorld:Ljava/lang/String;
5: return
public boolean isNull();
Code:
0: aload_0
1: getfield #13; //Field myWorld:Ljava/lang/String;
4: ifnonnull 9
7: iconst_1
8: ireturn
9: iconst_0
10: ireturn
public static void main(java.lang.String[]);
Code:
0: new #1; //class HelloWorld
3: dup
4: ldc #27; //String smallWorld
6: invokespecial #29; //Method "<init>":(Ljava/lang/String;)V
9: astore_1
10: iconst_0
11: istore_2
12: aload_1
13: ldc #31; //String bigWorld
15: invokevirtual #33; //Method setMyWorld:(Ljava/lang/String;)V
18: aload_1
19: invokevirtual #35; //Method isNull:()Z
22: istore_2
23: return
}
重点看一下main方法中调用isNull方法的操作:
Java代码
public static void main(java.lang.String[]);
Code:
0: new //在堆中分配内存,返回对象引用,压入操作数栈
3: dup //复制引用,压入栈
4: ldc //将常量池中的常量smallWorld压入栈
//JVM会将main方法操作数栈中的变量弹出放入构造函数的
//局部变量区
6: invokespecial //调用构造函数,弹出对象引用和参数
//调用结束后,JVM会将构造函数返回值压入main方法操作数栈
9: astore_1 //将对象引用弹出,存储在main方法局部变量1的位置
10: iconst_0 //将false值压栈
11: istore_2 //弹出false并存储于main方法局部变量2的位置
12: aload_1 //将对象引用压栈
13: ldc //将将常量池中的常量bigWorld压入栈
//JVM会将main方法操作数栈中的变量弹出放入实例方法
//的局部变量区
15: invokevirtual //调用实例函数setMyWorld,弹出对象引用和参数
18: aload_1 //将对象引用存储在main方法局部变量1的位置
//JVM会将main方法操作数栈中的变量弹出放入实例方法的
//局部变量区
19: invokevirtual //调用实例函数isNull,弹出对象引用和参数
//调用结束后,JVM会将构造函数返回值压入main方法操作数栈
22: istore_2 //弹出true并存储于main方法局部变量2的位置
23: return
public static void main(java.lang.String[]);
Code:
0: new //在堆中分配内存,返回对象引用,压入操作数栈
3: dup //复制引用,压入栈
4: ldc //将常量池中的常量smallWorld压入栈
//JVM会将main方法操作数栈中的变量弹出放入构造函数的
//局部变量区
6: invokespecial //调用构造函数,弹出对象引用和参数
//调用结束后,JVM会将构造函数返回值压入main方法操作数栈
9: astore_1 //将对象引用弹出,存储在main方法局部变量1的位置
10: iconst_0 //将false值压栈
11: istore_2 //弹出false并存储于main方法局部变量2的位置
12: aload_1 //将对象引用压栈
13: ldc //将将常量池中的常量bigWorld压入栈
//JVM会将main方法操作数栈中的变量弹出放入实例方法
//的局部变量区
15: invokevirtual //调用实例函数setMyWorld,弹出对象引用和参数
18: aload_1 //将对象引用存储在main方法局部变量1的位置
//JVM会将main方法操作数栈中的变量弹出放入实例方法的
//局部变量区
19: invokevirtual //调用实例函数isNull,弹出对象引用和参数
//调用结束后,JVM会将构造函数返回值压入main方法操作数栈
22: istore_2 //弹出true并存储于main方法局部变量2的位置
23: return
再来看看isNull方法:
Java代码
public boolean isNull();
Code:
0: aload_0 //将局部变量区的对象引用参数压栈
1: getfield //访问堆区的类实例
4: ifnonnull //是否为空
7: iconst_1 //如果是true,将1压入操作数栈
8: ireturn
9: iconst_0 //如果是false,将0压入操作数栈
10: ireturn
public boolean isNull();
Code:
0: aload_0 //将局部变量区的对象引用参数压栈
1: getfield //访问堆区的类实例
4: ifnonnull //是否为空
7: iconst_1 //如果是true,将1压入操作数栈
8: ireturn
9: iconst_0 //如果是false,将0压入操作数栈
10: ireturn
方法调用结束后,JVM会将isNull操作数栈中的返回值压入main方法的操作数栈中。
分享到:
相关推荐
Java 中 Method 的 Invoke 方法详解 Java 中的 Method 对象提供了 invoke 方法,这个方法可以动态地调用 Java 方法。下面详细介绍了 invoke 方法的实现机制。 首先,在调用 invoke 方法之前,需要检查 ...
【Java深度历险——探索Java字节码操纵】 在Java编程世界中,深入理解Java字节码对于优化程序性能和实现高级功能至关重要。Java字节码是Java虚拟机(JVM)执行的基础,它是一种平台无关的中间表示,使得Java程序...
以下将详细讲解如何在Java中获取字节码文件的相关内容,包括构造函数、成员变量和成员函数。 一、获取字节码文件的构造函数 在Java中,我们可以使用`Class`类的`getConstructor`方法来获取指定类型的构造函数。...
在Android系统中,Java字节码会通过DX工具转换为Dalvik字节码,也就是Smali代码,以适应Android的运行环境。 Smali是一种低级语言,它的语法结构和汇编语言类似,每条指令都对应DVM的特定操作。Smali代码是由反编译...
`javax.tools.JavaCompiler`接口和相关类允许我们在运行时将源代码编译为字节码并加载到JVM。这种方式适用于需在运行时创建大量新代码的场景,如自定义脚本引擎。与前三种方式相比,它提供了编译时检查,但编译过程...
在Android应用开发中,Smali是一种低级的字节码语言,与Dalvik虚拟机的字节码格式相对应,而Java则是开发者通常使用的高级编程语言。当需要分析或修改已编译的APK时,Smali2JavaUI提供了便捷的桥梁,将二进制Smali...
`invokedynamic`是Java虚拟机(JVM)中的一个字节码指令,它允许程序在运行时动态地解析方法调用并执行。这个特性对于支持如JavaScript、Ruby等动态类型语言的Java实现,如JRuby和Rhino,以及Java自身的动态代理和...
不过,由于Java的动态代理类是基于接口生成的,所以我们无法直接获取到它的字节码,除非先将代理类反编译为源代码,然后再编译成字节码。 总的来说,Java JDK动态代理为我们提供了一种灵活的方式,可以在不修改原有...
CGLIB(Code Generation Library)作为一款强大的Java字节码操作库,常常被用来实现这一目标。 CGLIB是一个高性能的代码生成库,它可以在运行期扩展Java类与实现Java接口。在并行化执行中,CGLIB可以帮助我们动态地...
Java虚拟机(JVM)负责加载类的字节码文件(.class文件)到内存中,并解析这些字节码,生成对应的Class对象。类加载器(ClassLoader)是这个过程中的关键角色,它负责定位并加载类的字节码。通常,JVM有三个内置的类...
通常情况下,Java程序在运行前需要通过`javac`命令将.java源文件编译成.class字节码文件,然后通过`java`命令执行。但在某些场合,我们可能希望在程序运行时动态地编译新的.java文件,以便快速响应代码变动。 Java...
在Java中,类加载器负责查找并加载类的字节码文件(.class文件)。这些字节码文件通常位于类路径(ClassPath)中定义的位置。Java虚拟机(JVM)有多个内置的类加载器,如Bootstrap ClassLoader、Extension ...
1. **字节码注入**:首先,Shellcode需要被转换成Java字节码,因为Java虚拟机(JVM)只能理解和执行这种格式的代码。这可能涉及到对原始Shellcode的解析和转换。 2. **内存中的类定义**:加载器会创建一个新的Java...
4. 字节码操作:ASM、Javassist等库用于动态生成和修改字节码,反射是其基础。 5. 测试工具:JUnit等测试框架利用反射调用私有方法或构造函数进行测试。 6. 反序列化:将序列化的对象数据反序列化为Java对象。 三、...
在编译期间,Java代码被转换为字节码,而反射是在运行时解析这些字节码的能力。这使得程序可以检查和修改自身的行为,增加代码的灵活性和可扩展性。 `Class`类是反射的核心,它代表了Java中的每一个类。我们可以...
Java 类加载器是Java运行时环境的一个重要组成部分,它的主要职责是将编译后的字节码(.class文件)加载到JVM中,使得程序能够运行。类加载器的机制保证了类的唯一性,同时也提供了灵活性,允许我们自定义加载逻辑。...
CGLIB是基于ASM库,通过字节码技术动态生成子类来实现代理。Spring框架默认使用CGLIB作为AOP(面向切面编程)的底层实现。 动态代理的应用场景广泛,包括: - AOP:在不修改源代码的情况下,为方法添加预处理和后...
尽管反射带来了很多便利,但它也有一定的性能成本,因为反射操作通常比直接的Java字节码执行要慢。因此,对于性能敏感的代码,应谨慎使用反射。 7. **注意事项**: 在使用反射时,需要注意安全性问题,因为反射...
这种转换过程由DX工具完成,它将字节码转换为Smali指令,便于DVM执行。 Java是一种高级编程语言,具有丰富的类库和面向对象特性,如封装、继承和多态。它的语法清晰,易于理解和编写。例如,Java中的方法定义如下:...
- **字节码操作库**:例如使用CGLIB、ASM等库自动生成代理类的字节码。 - **动态语言支持**:例如使用`java.lang.reflect.Proxy`类来创建动态代理类。 - **自定义代码生成**:通过编写代码来手动生成代理类的字节码...