`
lin_yp
  • 浏览: 26290 次
社区版块
存档分类
最新评论

Java字节码简单介绍

    博客分类:
  • JVM
阅读更多

首先了解一下理论知识:

字节码:

Class文件是8位字节流,按字节对齐。之所以称为字节码,是因为每条指令都只占据一个字节,所有的操作码和操作数都是按字节对齐的。如:0x03表示iconst_0

Class文件的头4个字节称为魔数(Magic Number),它的唯一作用是用于确认该文件是否是能被JVM接受的Class文件。魔数值为:0xCAFEBABE。

紧接着魔数的4个字节是Class文件的版本号:第5和第6字节是次版本号(Minor Version),第7和第8字节是主版本号(Major Version)。Java的版本号从45开始的,JDK6的版本号是50。

javap –verbose class文件,查看字节码内容

全限定名:把类全名中的“.”替换成“/”最后加入一个“;”表示结束。如com/test/TestClass;

描述符:基本类型及void用大写字符表示,对象类型用字符L加对象的全限定名表示。

标识字符 含义
B 基本类型byte
C char
D double
F float
I int
J long
S short
Z boolean
V void
L 对象类型,如Ljava/lang/Object;

对于数组类型,每一维度将使用一个前置的“[”字符来描述,如定义一个“java.lang.String[][]”类型的二维数组,将被记录为:“[[Ljava/lang/String;”,一个整型数组“int[]”将被记录为“[I”.

用描述符来描述方法时,按照先参数列表,后返回值的顺序描述,参数列表按照参数的严格顺序放在一组小括号“( )”之内,如方法void inc()的描述符为“( )V”,方法java.lang.String toString() 的描述符合为“( )Ljava/lang/String;”,方法int indexOf(char[] source, int sourceOffset,int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex)的描述符为“([CII[CIII)I”

类构造器“<clinit>”方法,实例构造器“<init>”

JVM中最基本的数据单元是字,字长必须足够大,至少一个字长足以持有byte、short、int等的值,2个字长足以持有long、double的值,字长可以选择32位或者64位。

字长是CPU的主要技术指标之一,指的是CPU一次能并行处理的二进制位数,字长总是8的整数倍,通常PC机的字长为16位(早期),32位,64位。

栈帧的2个部分:局部变量区和操作数栈,是按字来定义的。当把值放入局部变量区或者操作数栈时,它将占有1个或2个字单元。

每启动一个新线程,JVM都为它分配一个Java栈,Java栈以帧为单位保存线程的运行状态,JVM对Java栈执行2种操作:以帧为单位的压栈和出栈。

每当线程调用一个Java方法时,JVM会在线程的Java栈中压入一个新帧,而这个新帧也成了当前帧,当执行这个方法时,它使用这个帧来存储参数、局部变量、中间运算结果等数据。

Java方法有2种方式完成,一种通过return返回,一种通过抛出异常中止,不管那种方式,JVM都将当前帧弹出Java栈然后释放掉,这样上一个方法的帧就成为当前帧了。

Java栈上的所有数据都是此线程私有的,任何线程都不能访问另一个线程的栈数据,因此栈数据是线程安全的。

栈帧由3部分组成:局部变量区、操作数栈、帧数据区。

局部变量区和操作数栈是以字长(32位)为单位的数组。

局部变量区包含方法的参数和局部变量,编译器首先按把这些参数放入局部变量数组。Java栈帧的局部变量区被组织为一个以字长为单位,从0开始计数的数组。字节码指令通过从0开始的索引来使用其中的数据,如iload_1(把局部变量区的第2个变量压入栈顶),byte、short、int等的值在数组中只占据1项,而long、double的值在数组中占据连续的2项。

操作数栈:和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组,但是它不是通过索引来访问,而是通过标志的栈操作:压栈和出栈来访问的。如iadd指令,从操作数栈中弹出2个整数,执行加法运算,然后将其结果压回操作数栈。

iload_0 //将局部变量区的第1个变量压入栈

iload_1 //将局部变量区的第2个变量压入栈

iadd   //栈中弹出2个整数,执行加法运算,然后将其结果压回操作数栈

istore_2 //将栈顶的整数出栈,并存入局部变量区的第3个变量

一般读取局部变量区的数据,需要把局部变量区的变量压入栈,

把值写到局部变量区,也需要先压入栈,再写到局部变量区

帧数据区:存放常量池(要访问的类、字段、方法名等),异常表等数据。

LineNumberTable:字节码偏移量与源代码之间的映射关系。

常见指令:iload_1, istore_1, iconst_1, ldc, bipush, pop, dup, iadd, isub, imul, idiv, return, goto,invoke…, new, newarray, arraylength, instanceof,athrow,monitorenter, monitorexit

然后用字节码了解一下JVM的语法糖:

语法糖:

泛型、自动装箱、自动拆箱、循环遍历、变长参数、条件编译、内部类、枚举类、断言语句、对枚举的switch

类型擦除:

public class TestCls3

{//编译失败,因为List<String>List<Integer>的泛型被擦除,变成原生类型List

    public static void method(List<String> list)

    {

        System.out.println("invoke method1");

    }

   

    public static void method(List<Integer> list)

    {

        System.out.println("invoke method2");

}

}

public class TestCls3

{//可以执行,因为在Class文件中,只有描述符不完全一致的两个方法就可以共存

//也就是说两个方法如果有相同的名称和特征签名,但返回值不同,也是可以共存在一个Class文件的

    public static String method(List<String> list)

    {

        System.out.println("invoke method1");

        return "";

    }

   

    public static int method(List<Integer> list)

    {

        System.out.println("invoke method2");

        return 1;

    }

   

    public static void main(String[] args)

    {

        method(new ArrayList<String>());

        method(new ArrayList<Integer>());

    }

}

javap –verbose demo.TestCls3

Constant pool:

const #17 = Asciz       (Ljava/util/List<Ljava/lang/String;>;)Ljava/lang/String;

;

const #39 = Asciz       (Ljava/util/List<Ljava/lang/Integer;>;)I;

public static java.lang.String method(java.util.List);  //方法名擦除为List

  Signature: length = 0x2

   00 11   //指到常量池中的第17

public static int method(java.util.List);     //方法名擦除为List

  Signature: length = 0x2

   00 27   //指到常量池中的第39

类型擦除,仅仅对方法的Code属性中的字节码进行擦除,元数据Signature还是保留了泛型数据。(Method类的signature变量)

Java的条件编译

只有条件为常量且只有if语句才能有这种效果

public class TestCls5

{

    public static void main(String[] args)

    {

        if (true)

        {

            System.out.println("true");

        }

        else

        {

            System.out.println("false");

        }

    }

}

编译后的字节码只包含:System.out.println("true");

public class TestCls5

{

  public static void main(String[] args)

  {

    System.out.println("true");

  }

}

自增++操作的线程非安全:

public class TestCls5

{

    private static volatile int count;

    public static void main(String[] args)

    {

        count++;

    }

}

对应字节码(分为4个指令,在多线程下访问可能出现脏数据):

Code:

 0:   getstatic       #18; //Field count:I   获取指定类的静态域,并将其值压入栈顶

 3:   iconst_1                         将整型常量1压入栈顶

 4:   iadd                             将栈顶的2个值出栈并相加,然后将结果入栈顶

 5:   putstatic       #18; //Field count:I   为指定的类的静态域赋值

 8:   return                            方法返回

字符串的+操作(javac编译器会对String连接做自动优化):

public String constractStr(String str1, String str2, String str3)

    {

        return str1 + str2 + str3;

}

对应字节码(JDK1.5之后转换为调用StringBuilder.append方法):

  Code:

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

   3:   dup

   4:   aload_1

   5:   invokestatic    #26; //Method java/lang/String.valueOf:(Ljava/lang/Objec

t;)Ljava/lang/String;

   8:   invokespecial   #32; //Method java/lang/StringBuilder."<init>":(Ljava/la

ng/String;)V

   11:  aload_2

   12:  invokevirtual   #35; //Method java/lang/StringBuilder.append:(Ljava/lang

/String;)Ljava/lang/StringBuilder;

   15:  aload_3

   16:  invokevirtual   #35; //Method java/lang/StringBuilder.append:(Ljava/lang

/String;)Ljava/lang/StringBuilder;  ――调用StringBuilder的append方法

   19:  invokevirtual   #39; //Method java/lang/StringBuilder.toString:()Ljava/l

ang/String;

   22:  areturn     ――返回引用

public String constractStr()

    {

        return "str1" + "str2" + "str3";

}

对应的字节码:

Code:

 0:   ldc     #24; //String str1str2str3         --将字符串常量压入栈顶

 2:   areturn

public String constractStr(String str3)

    {

        return "str1" + "str2" + str3;

}

对应的字节码:

  Code:

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

   3:   dup

   4:   ldc     #26; //String str1str2   --将字符串常量str1str2压入栈顶

   6:   invokespecial   #28; //Method java/lang/StringBuilder."<init>":(Ljava/la

ng/String;)V

   9:   aload_1

   10:  invokevirtual   #31; //Method java/lang/StringBuilder.append:(Ljava/lang

/String;)Ljava/lang/StringBuilder;     ――调用StringBuilder的append方法

   13:  invokevirtual   #35; //Method java/lang/StringBuilder.toString:()Ljava/l

ang/String;

   16:  areturn

分享到:
评论

相关推荐

    Java字节码简单说明.docx

    Java字节码简单说明

    Java 字节码简单说明.zip

    Java 字节码简单说明

    java字节码分析工具

    Java字节码分析工具,系统分析了java字节码文件,即java class类文件,对该文件中的各种成分以树的形式描述出来,只能针对未加密的class文件,一般由标准java编译器编译生成的class文件都未加密,该系统在vs2003下面...

    javassist, Java字节码工程工具包.zip

    javassist, Java字节码工程工具包 Java字节码工程工具包 版本 3版权所有( C ) 1999 -2017按 Shigeru Chiba,保留所有权利。Javassist ( Java编程助手) 使Java字节码操作简单。 它是一个类库,用于在Java中编辑字节码

    Java 字节码编辑工具

    优秀的Java字节码可视化编辑工具,使用方便上手简单。

    基于Soot的JAVA字节码优化及性能分析.pdf

    本文将从Java字节码优化的角度,介绍Soot框架的应用和优化技术,并展示相关的应用实例。 Java语言的特点 Java语言具有以下特点: 1. 简单易学:Java语言的语法简单易学,易于开发者快速上手。 2. 解释执行:Java...

    免费开源!!Java字节码工程工具包

    如果用户使用源代码级API,他们可以在不了解Java字节码规范的情况下编辑类文件。整个API仅使用Java语言的词汇进行设计。您甚至可以以源文本的形式指定插入的字节码;Javassist 即时编译它。另一方面,字节码级API...

    从一个class文件深入理解Java字节码结构

    我们都知道,Java程序最终是转换成class文件执行在虚拟机上的,那么class文件是个怎样的结构,虚拟机又是如何处理去执行class文件里面的内容呢,这篇文章带你深入理解Java字节码中的结构。 1.Demo源码 首先,编写一...

    jclasslib.rar_Java 8_java8 下载_java字节码查看工具;_jclasslib.jar_允许修改Jav

    jclasslib是一款免费开源的java字节码查看工具,该软件不但可以查看java字节码,同时还包含一个类库允许开发者读取,修改,写入Java Class文件与字节码。简单的说:用户可以通过jclasslib修改jar包下面的类,是一个...

    javassist:Java字节码工程工具包

    Javassist(JAVA编程ASSISTant)使Java字节码操作变得简单。 它是一个用Java编辑字节码的类库。 它使Java程序可以在运行时定义新类,并在JVM加载它时修改类文件。 与其他类似的字节码编辑器不同,Javassist提供了两...

    java字节码查看工具(jclasslib)v5.1官方版

    jclasslib是一款免费开源的java字节码查看工具,该软件不但可以查看java字节码,同时还包含一个类库允许开发者读取,修改,写入Java Class文件与字节码。简单的说:用户可以通过jclasslib修改jar包下面的类,是一个...

    voc:一个将Python代码转换为Java字节码的编译器

    一个将Python代码转换为Java字节码的编译器。 这是实验代码。 如果破裂,您将保留所有闪亮的碎片。 它能做什么: 提供一个API,可让您以编程方式创建Java类文件。 将Python 3.4源文件编译为Java类文件,使您...

    Java字节码操作类库,基于这个api标准完成一个新的asmsupport实现

    asmsupport是一个字节码操作类库,它能够让程序员非常简单的在动态创建和修改类,该框架是基于asm开发的,不同与asm的是,它避免了直接操作jvm指令,栈和局部变量。这个模块包含了asmsupport使用的第三方依赖包的...

    JWebAssembly:Java字节码到WebAssembly编译器

    JWebAssembly是编译器的Java字节码。 它使用Java类文件作为输入。 它可以编译任何可编译为Java字节码的语言,例如Clojure,Groovy,JRuby,Jython,Kotlin和Scala。 作为输出,它生成二进制格式(.wasm文件)或文本...

    01-JavaSE-JAVA运行原理.ppt

    编译成,Java字节码(*.class),执行Java字节码, Java字节码经过JVM的解释为具体平台 的具体指令,并执行; 高性能:运行速度随着编译器技术提高 ; 多线程:java内置了对多 线程的支持,编写更简单; ...

    JBE:一个简单的 Java 字节码编辑器,灵感来自 Reflexil

    JBE 一个简单的 Java 字节码编辑器,灵感来自 Reflexil。

    jclasslib 修改class字节码工具

    jclasslib 修改class字节码工具,日常java开发者经常引用第三方sdk,需修改里面一些简单的内容,该工具真的很实用

    Class(字节码)了解基础

    简单了解字节码,便于以后深入学习,简单了解字节码,便于以后深入学习

    ExprEngine:ExprEngine在运行时将表达式字符串编译为内存中的Java字节码

    ExprEngine在运行时将表达式字符串编译为内存中的Java字节码。 字节码表示实现用户定义接口的静态方法或类。 该方法可以称为普通Java方法,并返回Java对象。 以下代码显示了一些简单的示例。 1.快速示例:exec()-&gt;...

    xalanjava源码-rcjp:使用ASM库检测Java字节码,实现一个简单的引用计数模型来测试Java程序

    字节码,实现一个简单的引用计数模型来测试 Java 程序。 描述 rcjp 是引用计数 Java 程序的缩写。 本项目旨在从Java程序中发现Whole-Part关系中存在的一些详细属性。 构建检测程序以跟踪 Java 程序中实例的行为并...

Global site tag (gtag.js) - Google Analytics