java

位置:IT落伍者 >> java >> 浏览文章

Java字节码深入解析


发布日期:2023年10月13日
 
Java字节码深入解析

Java字节代码的组织形式

类文件{

OxCAFEBABE小版本号大版本号常量池大小常量池数组访问控制标记当前类信息父类信息实现的接口个数实现的接口信息数组域个数域信息数组方法个数方法信息数组属性个数属性信息数组

}

查看方法 javap命令

例子有一个Java类Demojava

public class Demo {

private String str;

private String str;

private int num;

private int num;

public static final String STATIC_DATA = hello world;

private void sayHello(){

Systemoutprintln(this is method);

}

private void sayHello(){

Systemoutprintln(this is method);

}

public void sayHello(){

Systemoutprintln(this is method);

}

}

通过jdk自带的反编译工具命令 javap 可以查看class文件的字节码信息

D:\>javap verbose Demo >> Demotxt

Demotxt

Compiled from Demojava

public class Demo extends javalangObject

SourceFile: Demojava

minor version:

major version:

Constant pool:

const # = class #; // Demo

const # = Asciz Demo;

const # = class #; // java/lang/Object

const # = Asciz java/lang/Object;

const # = Asciz str;

const # = Asciz Ljava/lang/String;;

const # = Asciz str;

const # = Asciz num;

const # = Asciz I;

const # = Asciz num;

const # = Asciz STATIC_DATA;

const # = Asciz ConstantValue;

const # = String #; // hello world

const # = Asciz hello world;

const # = Asciz <init>;

const # = Asciz ()V;

const # = Asciz Code;

const # = Method ##; // java/lang/Object<init>:()V

const # = NameAndType #:#;// <init>:()V

const # = Asciz LineNumberTable;

const # = Asciz LocalVariableTable;

const # = Asciz this;

const # = Asciz LDemo;;

const # = Asciz sayHello;

const # = Field ##; // java/lang/Systemout:Ljava/io/PrintStream;

const # = class #; // java/lang/System

const # = Asciz java/lang/System;

const # = NameAndType #:#;// out:Ljava/io/PrintStream;

const # = Asciz out;

const # = Asciz Ljava/io/PrintStream;;

const # = String #; // this is method

const # = Asciz this is method;

const # = Method ##; // java/io/PrintStreamprintln:(Ljava/lang/String;)V

const # = class #; // java/io/PrintStream

const # = Asciz java/io/PrintStream;

const # = NameAndType #:#;// println:(Ljava/lang/String;)V

const # = Asciz println;

const # = Asciz (Ljava/lang/String;)V;

const # = Asciz sayHello;

const # = String #; // this is method

const # = Asciz this is method;

const # = Asciz sayHello;

const # = String #; // this is method

const # = Asciz this is method;

const # = Asciz SourceFile;

const # = Asciz Demojava;

{

public static final javalangString STATIC_DATA;

Constant value: String hello world

public Demo();

Code:

Stack= Locals= Args_size=

: aload_

: invokespecial #; //Method java/lang/Object<init>:()V

: return

LineNumberTable:

line :

LocalVariableTable:

Start Length Slot Name Signature

this LDemo;

public void sayHello();

Code:

Stack= Locals= Args_size=

: getstatic #; //Field java/lang/Systemout:Ljava/io/PrintStream;

: ldc #; //String this is method

: invokevirtual #; //Method java/io/PrintStreamprintln:(Ljava/lang/String;)V

: return

LineNumberTable:

line :

line :

LocalVariableTable:

Start Length Slot Name Signature

this LDemo;

}

解析

版本号 major version //java版本 jdk显示的是 jdk显示的是jdk显示的是 高版本能执行低版本的class文件

常量池Constant pool

Method方法

Field字段

String字符串

Asciz签名如<init>由jvm调用其他是不能够去调用它的

NameAndType变量名的类型

Class

通过字节码我们可以看到Demo类 继承于javalangObject如果类中没有显式声明构造函数的话编译器会插入一个缺省无参的构造函数(构造函数在JVM级别是显示成<init>的普通函数)

检测代码的效率问题

学习Java的过程中都会了解到字符串合并时要用到StringBuffer 来代替String那下面就来通过Java字节码来验证两种方式的效率性

例子一个Java类 TestStringjava

<strong>public class TestString {

public String testString(String str String str){

return str + str;

}

public String testStringBuffer(StringBuffer sb String str){

return sbappend(str)toString();

}

}

</strong>

javap –c TestString 后字节码信息

Compiled from TestStringjava

public class TestString extends javalangObject{

public TestString();

Code:

: aload_

: invokespecial #; //Method java/lang/Object<init>:()V

: return

public javalangString testString(javalangString javalangString);

Code:

: new #; //class java/lang/StringBuilder

: dup

: aload_

: invokestatic #; //Method java/lang/StringvalueOf:(Ljava/lang/Object;)Ljava/lang/String;

: invokespecial #; //Method java/lang/StringBuilder<init>:(Ljava/lang/String;)V

: aload_

: invokevirtual #; //Method java/lang/StringBuilderappend:(Ljava/lang/String;)Ljava/lang/StringBuilder;

: invokevirtual #; //Method java/lang/StringBuildertoString:()Ljava/lang/String;

: areturn

public javalangString testStringBuffer(javalangStringBuffer javalangString);

Code:

: aload_

: aload_

: invokevirtual #; //Method java/lang/StringBufferappend:(Ljava/lang/String;)Ljava/lang/StringBuffer;

: invokevirtual #; //Method java/lang/StringBuffertoString:()Ljava/lang/String;

: areturn

}

从上面编译后的字节码信息可以看出来方法testString 调用了五个方法new invokestatic invokespecial 和两个invokevirtual 而testStringBuffer 方法只调用了两个invokevirtual 方法第一个方法比第二个方法多做了好多工作其效率当然是要低的而且我们从java/lang/StringBuilderappend(Ljava/lang/String)Ljava/lang/StringBuilder

可以看出来其实对于String字符串合并内部还是转化为StringBuilder的方法调用这是因为String是长度不可变的所以不如直接采用StringBuilder(与StringBuffer 长度都是可变的只不过前者是非线程安全后者是线程安全)进行字符串合并

上一篇:Java发送邮件

下一篇:Java远程通信技术——Axis实战