转载来自

首先了解一下理论知识:

字节码:

Class文件是8位字节流,按字节对齐。之所以称为字节码,是因为每条指令都只占据一个字节,所有的操作码和操作数都是按字节对齐的。如:0×03表示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 = 0×2

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

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

Signature: length = 0×2

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之字节码(3) - 简单介绍的更多相关文章

  1. 使用java动态字节码技术简单实现arthas的trace功能。

    参考资料 ASM 系列详细教程 编译时,找不到asm依赖 用过[Arthas]的都知道,Arthas是alibaba开源的一个非常强大的Java诊断工具. 不管是线上还是线下,我们都可以用Arthas ...

  2. 【java虚拟机系列】从java虚拟机字节码执行引擎的执行过程来彻底理解java的多态性

    我们知道面向对象语言的三大特点之一就是多态性,而java作为一种面向对象的语言,自然也满足多态性,我们也知道java中的多态包括重载与重写,我们也知道在C++中动态多态是通过虚函数来实现的,而虚函数是 ...

  3. java虚拟机字节码执行引擎

    定义 java虚拟机字节码执行引擎是jvm最核心的组成部分之一,它做的事情很简单:输入的是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果.在不同的虚拟机实现里,执行引擎在执行java代码 ...

  4. 小师妹学JVM之:java的字节码byte code简介

    目录 简介 Byte Code的作用 查看Byte Code字节码 java Byte Code是怎么工作的 总结 简介 Byte Code也叫做字节码,是连接java源代码和JVM的桥梁,源代码编译 ...

  5. 聊聊Java的字节码

    本文为作者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃 巴山楚水凄凉地,二十三年弃置身.怀旧空吟闻笛赋,到乡翻似烂柯人.沉舟侧畔千帆过,病树前头万木春 ...

  6. java class 字节码

    java class 字节码 协议: class文件 魔数(Magic):4byte -> 0xCAFEBABE 类似2f3f 版本(Version):4Byte -> 0x0000003 ...

  7. Java 动态字节码技术

    对 Debug 的好奇 初学 Java 时,我对 IDEA 的 Debug 非常好奇,不止是它能查看断点的上下文环境,更神奇的是我可以在断点处使用它的 Evaluate 功能直接执行某些命令,进行一些 ...

  8. Java静态检测工具/Java代码规范和质量检查简单介绍(转)

    静态检查: 静态测试包括代码检查.静态结构分析.代码质量度量等.它可以由人工进行,充分发挥人的逻辑思维优势,也可以借助软件工具自动进行.代码检查代码检查包括代码走查.桌面检查.代码审查等,主要检查代码 ...

  9. Java虚拟机--字节码指令集

    1. 字节码指令集简介: Java虚拟机的指令由一个字节长度的,代表着某种特定操作含义的操作码(Opcode)以及跟随其后的零至多个代表此操作所需参数的操作数(Operands)所构成.虚拟机中许多指 ...

随机推荐

  1. 【插头DP】BZOJ3125-city

    开学忙成狗,刷题慢如蜗牛…… [题目大意] 给出一个m*n的矩阵里面有一些格子为障碍物,一些格子只能上下通行,一些格子只能左右通行,一些格子上下左右都能通行.问经过所有非障碍格子的哈密顿回路个数. [ ...

  2. SQL Server 事务复制爬坑记

    SQL Server 复制功能折腾了好几天了,现特将其配置过程以及其间遇到的问题记录下来,以备日后查阅.同时,也让“同道”同学们少走不必要的弯路.如果有不对之处,欢迎大家指正,欢迎沟通交流. 一.复制 ...

  3. hdu 4111 Alice and Bob 记忆化搜索 博弈论

    Alice and Bob Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pi ...

  4. java 随机数种子

    引子:需要实现每天随机获得一个礼包,且全服玩家随出来的都是同一个. 实现方案:以当前时间是一年的第几天作为random的种子,取1~礼包总个数范围内的随机值. public static int ge ...

  5. HashSet,TreeSet和LinkedHashSet

    Set接口 Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false. Set判断两个对象相同不是使用==运算符,而是根据equals方法.也就是说,只要两个对象用 ...

  6. 解决数据库 Table 'content_tags' is marked as crashed and should be repaired 表损坏问题

    今天突然网站TAG页面打不开了,打开debug,发现提示 Table 'content_tags' is marked as crashed and should be repaired 这样的错误 ...

  7. switch语句的基本使用

    switch是一个多分支的选择语句. 1.基本格式: switch(整型表达式){              case  整型字面量: ...... default  : } 解释: 1)整型字面量可 ...

  8. JS学习笔记-事件绑定

    一.传统事件模型 传统事件模型中存在局限性. 内联模型以HTML标签属性的形式使用,与HTML混写.这样的方式无疑造成了改动以及扩展的问题,已经非常少使用了. 脚本模型是将事件处理函数写到js文件里, ...

  9. js:对象的创建(为prototype做铺垫)

    /**  *在js中并不存在类,所以能够直接通过Object来创建对象,可是使用这样的方式创建有一  *弊端:因为没有类的约束,无法实现对象的反复利用,而且没有一种规范约定,在操作时easy带来问题. ...

  10. 数学图形(1.18)Poinsot's spiral螺线

    它是一种螺线,其特点是细分时无限递归. 相关软件参见:数学图形可视化工具,使用自己定义语法的脚本代码生成数学图形.该软件免费开源.QQ交流群: 367752815 vertices = t = *PI ...