Java字节码深入解析

    xiaoxiao2022-07-15  181

    一:Java字节代码的组织形式

      类文件{

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

      }

      二:查看方法 --- javap命令

      例子:有一个Java类Demo.java

    public class Demo {      private String str1;      private String str2;      private int num1;      private int num2;      public static final String STATIC_DATA = "hello world";            private void sayHello1(){          System.out.println("this is method1...");      }      private void sayHello2(){          System.out.println("this is method2...");      }      public void sayHello3(){          System.out.println("this is method3...");      }  }

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

    D:\>javap -verbose Demo >> Demo.txt

      Demo.txt:

    Compiled from "Demo.java"  public class Demo extends java.lang.Object    SourceFile: "Demo.java"    minor version: 0    major version: 49         Constant pool:  const #1 = class      #2;   //  Demo  const #2 = Asciz     Demo;  const #3 = class      #4;   //  java/lang/Object  const #4 = Asciz     java/lang/Object;  const #5 = Asciz     str1;  const #6 = Asciz     Ljava/lang/String;;  const #7 = Asciz     str2;  const #8 = Asciz     num1;  const #9 = Asciz     I;  const #10 = Asciz   num2;  const #11 = Asciz   STATIC_DATA;  const #12 = Asciz   ConstantValue;  const #13 = String  #14; //  hello world  const #14 = Asciz   hello world;  const #15 = Asciz   <init>;  const #16 = Asciz   ()V;  const #17 = Asciz   Code;  const #18 = Method       #3.#19;   //  java/lang/Object."<init>":()V  const #19 = NameAndType    #15:#16;//  "<init>":()V  const #20 = Asciz   LineNumberTable;  const #21 = Asciz   LocalVariableTable;  const #22 = Asciz   this;  const #23 = Asciz   LDemo;;  const #24 = Asciz   sayHello1;  const #25 = Field   #26.#28;  //  java/lang/System.out:Ljava/io/PrintStream;  const #26 = class    #27; //  java/lang/System  const #27 = Asciz   java/lang/System;  const #28 = NameAndType    #29:#30;//  out:Ljava/io/PrintStream;  const #29 = Asciz   out;  const #30 = Asciz   Ljava/io/PrintStream;;  const #31 = String  #32; //  this is method1...  const #32 = Asciz   this is method1...;  const #33 = Method       #34.#36;  //  java/io/PrintStream.println:(Ljava/lang/String;)V  const #34 = class    #35; //  java/io/PrintStream  const #35 = Asciz   java/io/PrintStream;  const #36 = NameAndType    #37:#38;//  println:(Ljava/lang/String;)V  const #37 = Asciz   println;  const #38 = Asciz   (Ljava/lang/String;)V;  const #39 = Asciz   sayHello2;  const #40 = String  #41; //  this is method2...  const #41 = Asciz   this is method2...;  const #42 = Asciz   sayHello3;  const #43 = String  #44; //  this is method3...  const #44 = Asciz   this is method3...;  const #45 = Asciz   SourceFile;  const #46 = Asciz   Demo.java;     {  public static final java.lang.String STATIC_DATA;    Constant value: String hello world  public Demo();    Code:     Stack=1, Locals=1, Args_size=1     0:      aload_0     1:      invokespecial  #18; //Method java/lang/Object."<init>":()V     4:      return    LineNumberTable:     line 2: 0    LocalVariableTable:     Start  Length  Slot  Name   Signature     0      5      0    this       LDemo;     public void sayHello3();    Code:     Stack=2, Locals=1, Args_size=1     0:      getstatic   #25; //Field java/lang/System.out:Ljava/io/PrintStream;     3:      ldc   #43; //String this is method3...     5:      invokevirtual  #33; //Method java/io/PrintStream.println:(Ljava/lang/String;)V     8:      return    LineNumberTable:     line 17: 0     line 18: 8    LocalVariableTable:     Start  Length  Slot  Name   Signature     0      9      0    this       LDemo;  }

    解析:

      1、版本号 major version: 49 //java版本 jdk1.6显示的是50, jdk1.5显示的是49,jdk1.4显示的是58 , 高版本能执行低版本的class文件

      2、常量池Constant pool

      Method:方法

      Field:字段

      String:字符串

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

      NameAndType:变量名的类型

      Class:类

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

      三:检测代码的效率问题

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

      例子:一个Java类 TestString.java

    <strong>public class TestString {      public String testString(String str1, String str2){         return str1 + str2;      }      public String testStringBuffer(StringBuffer sb, String str){         return sb.append(str).toString();      }  }   </strong>

      javap –c TestString 后字节码信息:

    Compiled from "TestString.java"  public class TestString extends java.lang.Object{  public TestString();    Code:     0:      aload_0     1:      invokespecial  #8; //Method java/lang/Object."<init>":()V     4:      return     public java.lang.String testString(java.lang.String, java.lang.String);    Code:     0:      new #16; //class java/lang/StringBuilder     3:      dup     4:      aload_1     5:      invokestatic    #18; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;     8:      invokespecial  #24; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V     11:     aload_2     12:    invokevirtual  #27; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;     15:    invokevirtual  #31; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;     18:    areturn     public java.lang.String testStringBuffer(java.lang.StringBuffer, java.lang.String);    Code:     0:      aload_1     1:      aload_2     2:      invokevirtual  #40; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;     5:      invokevirtual  #45; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;     8:      areturn  }

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

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

    本文出自seven的测试人生公众号最新内容请见作者的GitHub页:http://qaseven.github.io/

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)