博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
jvm第8节-解析class文件
阅读量:6539 次
发布时间:2019-06-24

本文共 11747 字,大约阅读时间需要 39 分钟。

  hot3.png

最近看了周志明的深入java虚拟机,看到堆字节码解析这一章节,觉得特别的枯燥无味,于是我花了一段时间解析了一个class文件,由于方法体和属性这两项篇幅太长没做解析,想看这两个解析的略过。

class文件具有与语言无关性的特点,很多语言都可以编译成class文件,让jvm执行,class文件结构如下:

1.魔数

2.版本

3.常量池

4.访问符

5.类、超类、接口

6.字段

7.方法

8.属性

class文件的组成元素如下表:

类型

名称

数量

u4

magic

1

u2

minor_version

1

u2

major_version

1

u2

constant_pool_count

1

cp_info

constant_pool

constant_pool_count - 1

u2

access_flags

1

u2

this_class

1

u2

super_class

1

u2

interfaces_count

1

u2

interfaces

interfaces_count

u2

fields_count

1

field_info

fields

fields_count

u2

methods_count

1

method_info

methods

methods_count

u2

attribute_count

1

attribute_info

attributes

attributes_count

 

1.常量池的结构

constant_pool_count u2

constant_pool  cp_info

CONSTANT_Utf8 1 UTF-8编码的Unicode字符串

CONSTANT_Integer 3 int类型的字面值

CONSTANT_Float 4 float类型的字面值

CONSTANT_Long 5 long类型的字面值

CONSTANT_Double 6 double类型的字面值

CONSTANT_Class 7 对一个类或接口的符号引用

CONSTANT_String 8 String类型字面值的引用

CONSTANT_Fieldref 9 对一个字段的符号引用

CONSTANT_Methodref 10 对一个类中方法的符号引用

CONSTANT_InterfaceMethodref 11 对一个接口中方法的符号引用

CONSTANT_NameAndType 12 对一个字段或方法的部分符号引用

依次是常量池里对应的常量类型,编号,描述

字符常量,如一个类的名称,方法名称

CONSTANT_Utf8

tag 1

length u2

bytes[length]

整数常量

CONSTANT_Integer

tag 3

byte u4

String 类型的字面常量如:

public static final String name="xuehan";

CONSTANT_String

tag 8

string_index  u2 (指向utf8的索引)

 

CONSTANT_NameAndType 对一个字段或方法的部分符号引用

tag 12

name_index u2 (名字,指向utf8)

descriptor_index u2 (描述符类型,指向utf8)

access flag u2:类的标示符

 

Flag NameValueInterpretation

ACC_PUBLIC0x0001public

ACC_FINAL0x0010final,不能被继承.

ACC_SUPER0x0020是否允许使用invokespecial指令,JDK1.2后,该值为true

ACC_INTERFACE0x0200是否是接口

ACC_ABSTRACT0x0400抽象类

ACC_SYNTHETIC0x1000该类不是由用户代码生成,运行时生成的,没有源码

ACC_ANNOTATION0x2000是否为注解

ACC_ENUM0x4000是否是枚举

this_class u2

指向常量池的Class

super_class u2

指向常量池的Class

 

interface_count u2

接口数量

interfaces

interface_count 个 interface u2

每个interface是指向CONSTANT_Class的索引

。。。。。以上还有很多我就不一一列出来了,下面解析一个class文件

需要解析的类

public class User {		private String name;	public String getName() {		return name;	}	public void setName(String name) {		this.name = name;	}		 	}

 

解析的代码:

package com.jvm.day7;import java.io.RandomAccessFile;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;import util.BytesUtils;/** * 分析clas文件 * @Author:xuehan * @Date:2016年3月27日上午10:26:54 */public class ClassAnalyze {		public void analyze(String filePath) throws Exception{		ByteBuffer buf = getBuffer(filePath);	 		System.out.println("魔数==>" + BytesUtils.toHexString(getbytes(buf, 4)));		buf = clearRead(buf, 4);		System.out.println("版本号==>" + BytesUtils.toHexString(getbytes(buf, 4)));		buf = clearRead(buf, 4);		System.out.println("常量池大小==>" + BytesUtils.toHexString(getbytes(buf, 2)));		buf = clearRead(buf, 2);		System.out.println("第一个常量的tag==>" + BytesUtils.toHexString(getbytes(buf, 1) ));		// 读出第一个常量第一个index		buf = clearRead(buf, 1);		System.out.println("Methodref_info第一个index是==>" + BytesUtils.toHexString(getbytes(buf, 2)));		// 读出第一个常量第二个index		buf = clearRead(buf, 2);		System.out.println("Methodref_info第二个index是==>" + BytesUtils.toHexString(getbytes(buf, 2)));		buf = clearRead(buf, 2);		// 读第二个常量		System.out.println("第二个常量的tag==>" + BytesUtils.toHexString(getbytes(buf, 1) ));		buf = clearRead(buf, 1);				System.out.println("fieldref_info第一个index是==>" + BytesUtils.toHexString(getbytes(buf, 2)));		// 读出第一个常量第二个index		buf = clearRead(buf, 2);		System.out.println("fieldref_info第二个index是==>" + BytesUtils.toHexString(getbytes(buf, 2)));		buf = clearRead(buf, 2);		// 读第三个常量		System.out.println("第三个常量的tag==>" + BytesUtils.toHexString(getbytes(buf, 1) ));		buf = clearRead(buf,1);		System.out.println("class_info第一个index是==>" + BytesUtils.toHexString(getbytes(buf, 2))); 		buf = clearRead(buf, 2); 		// 读第四个常量		System.out.println("第四个常量的tag==>" + BytesUtils.toHexString(getbytes(buf, 1) ));		buf = clearRead(buf,1);		System.out.println("class_info第一个index是==>" + BytesUtils.toHexString(getbytes(buf, 2))); 		buf = clearRead(buf, 2);    	// 开始读取字符串常量 		for(int i = 1; i < 13 ; i ++){ 			System.out.println("第" + i + "个常量的tag==>" + BytesUtils.toHexString(getbytes(buf, 1) )); 			buf = clearRead(buf,1); 			String hexStr = BytesUtils.toHexString(getbytes(buf, 2)); 			System.out.println("第" + i +" utf8_info length十六进制是==>" +hexStr); 			int len = HexToInteger(hexStr); 			System.out.println("第" + i +" utf8_info length十进制是==>" +len);  	 		buf = clearRead(buf, 2); 			System.out.println("第" + i + "utf8_info bytes是==>" + BytesUtils.toHexString(getbytes(buf, len)) + "字面常量是==>" + new String((getbytes(buf, len)))); 	 		buf = clearRead(buf, len); 		}		System.out.println("已经读取16个常量,下面开始读取第17个常量#########################################");		// 读取第17个常量		System.out.println("第17个常量的tag==>" + BytesUtils.toHexString(getbytes(buf, 1) ));		buf = clearRead(buf,1);		System.out.println("class_info第1个index是==>" + BytesUtils.toHexString(getbytes(buf, 2))); 		buf = clearRead(buf, 2); 		System.out.println("class_info第2个index是==>" + BytesUtils.toHexString(getbytes(buf, 2))); 		buf = clearRead(buf, 2); 		// 读取第18个常量		System.out.println("第18个常量的tag==>" + BytesUtils.toHexString(getbytes(buf, 1) ));		buf = clearRead(buf,1);		System.out.println("class_info第1个index是==>" + BytesUtils.toHexString(getbytes(buf, 2))); 		buf = clearRead(buf, 2); 		System.out.println("class_info第2个index是==>" + BytesUtils.toHexString(getbytes(buf, 2))); 		buf = clearRead(buf, 2); 		// 读取第19和第20个常量 		for(int i = 19; i < 21 ; i ++){ 			System.out.println("第" + i + "个常量的tag==>" + BytesUtils.toHexString(getbytes(buf, 1) )); 			buf = clearRead(buf,1); 			String hexStr = BytesUtils.toHexString(getbytes(buf, 2)); 			System.out.println("第" + i +" utf8_info length十六进制是==>" +hexStr); 			int len = HexToInteger(hexStr); 			System.out.println("第" + i +" utf8_info length十进制是==>" +len);  	 		buf = clearRead(buf, 2); 			System.out.println("第" + i + "utf8_info bytes是==>" + BytesUtils.toHexString(getbytes(buf, len)) + "字面常量是==>" + new String((getbytes(buf, len)))); 	 		buf = clearRead(buf, len); 		} 		// 常量池读取完毕读取acces_flags 		System.out.println("acces_flags==>" + BytesUtils.toHexString(getbytes(buf, 2) ));		buf = clearRead(buf,2);		// 读取this_class 		System.out.println("this_class==>" + BytesUtils.toHexString(getbytes(buf, 2) ));		buf = clearRead(buf,2);		// 读取super_class		System.out.println("super_class==>" + BytesUtils.toHexString(getbytes(buf, 2) ));		buf = clearRead(buf,2);		// 读取interfaces_cout,如果cout大于0就要继续读取interfaces		System.out.println("interfaces_cout==>" + BytesUtils.toHexString(getbytes(buf, 2) ));		buf = clearRead(buf,2);		// 读取fileds_count		System.out.println("fileds_count==>" + BytesUtils.toHexString(getbytes(buf, 2) ));		buf = clearRead(buf,2);		// 读取fileds acces_flags		System.out.println("fileds acces_flags==>" + BytesUtils.toHexString(getbytes(buf, 2) ));		buf = clearRead(buf,2);		// 读取fileds  index		System.out.println("fileds  index==>" + BytesUtils.toHexString(getbytes(buf, 2) ));		buf = clearRead(buf,2);		// 读取fileds  descriptor index		System.out.println("fileds  descriptor index==>" + BytesUtils.toHexString(getbytes(buf, 2) ));		buf = clearRead(buf,2);		// 读取attribute_count		System.out.println("attribute_count==>" + BytesUtils.toHexString(getbytes(buf, 2) ));		buf = clearRead(buf,2);		// 读取methods_count		System.out.println("methods_count==>" + BytesUtils.toHexString(getbytes(buf, 2) ));		buf = clearRead(buf,2); 	    		 	}	 	public static void main(String[] args) throws Exception {		ClassAnalyze ca = new ClassAnalyze(); 		ca.analyze("D:\\BaiduYunDownload\\data\\jvmtest\\User.class"); 		//ca.getBuffer("D:\\BaiduYunDownload\\data\\jvmtest\\User.class");	}	public ByteBuffer getBuffer(String filePath) throws Exception{		RandomAccessFile  raf = new RandomAccessFile(filePath, "rw");		FileChannel channel = raf.getChannel();		ByteBuffer bb = ByteBuffer.allocate(2048);		channel.read(bb);		raf.close();		byte[] bbs = new byte[4];		bb.get(bbs); 		return bb;	}	public byte[] getbytes(ByteBuffer bb, int len){				byte[] bytes =new byte[len];		for(int i =0; i < bytes.length; i ++){			bytes[i] = bb.get(i);		}		return bytes;	}	public int HexToInteger(String hexStr){		char[] chars = hexStr.toCharArray();		int secondInt = 0;		int thirdInt = 0;		if(chars[2] == 'a'){			secondInt = 10;		}else if(chars[2] == 'b'){			secondInt = 11;		}else if(chars[2] == 'c'){			secondInt = 12;		}else if(chars[2] == 'd'){			secondInt = 13;		}else if(chars[2] == 'e'){			secondInt = 14;		}else if(chars[2] == 'f'){			secondInt = 15;		}else{			secondInt =  Integer.parseInt("" + chars[2]);		}		secondInt = 16 * secondInt;				if(chars[3] == 'a'){			thirdInt = 10;		}else if(chars[3] == 'b'){			thirdInt = 11;		}else if(chars[3] == 'c'){			thirdInt = 12;		}else if(chars[3] == 'd'){			thirdInt = 13;		}else if(chars[3] == 'e'){			thirdInt = 14;		}else if(chars[3] == 'f'){			thirdInt = 15;		}else{			thirdInt = Integer.parseInt("" + chars[3]);		}		 		return thirdInt +   secondInt;	}	public ByteBuffer clearRead(ByteBuffer bb, int len){		System.out.println("清理之前==>" + BytesUtils.toHexString(bb.array()));		ByteBuffer bbnew = ByteBuffer.allocate(bb.capacity());		for(int i = len ; i < bb.capacity(); i ++){			bbnew.put(bb.get(i));		}		System.out.println("清理完毕==>" + BytesUtils.toHexString(bbnew.array()));		return bbnew;	}}

 

魔数==>cafebabe版本号==>00000033常量池大小==>0015第一个常量的tag==>0aMethodref_info第一个index是==>0004Methodref_info第二个index是==>0011第二个常量的tag==>09fieldref_info第一个index是==>0003fieldref_info第二个index是==>0012第三个常量的tag==>07class_info第一个index是==>0013第四个常量的tag==>07class_info第一个index是==>0014第1个常量的tag==>01第1 utf8_info length十六进制是==>0004第1 utf8_info length十进制是==>4第1utf8_info bytes是==>6e616d65字面常量是==>name第2个常量的tag==>01第2 utf8_info length十六进制是==>0012第2 utf8_info length十进制是==>18第2utf8_info bytes是==>4c6a6176612f6c616e672f537472696e673b字面常量是==>Ljava/lang/String;第3个常量的tag==>01第3 utf8_info length十六进制是==>0006第3 utf8_info length十进制是==>6第3utf8_info bytes是==>3c696e69743e字面常量是==>
第4个常量的tag==>01第4 utf8_info length十六进制是==>0003第4 utf8_info length十进制是==>3第4utf8_info bytes是==>282956字面常量是==>()V第5个常量的tag==>01第5 utf8_info length十六进制是==>0004第5 utf8_info length十进制是==>4第5utf8_info bytes是==>436f6465字面常量是==>Code第6个常量的tag==>01第6 utf8_info length十六进制是==>000f第6 utf8_info length十进制是==>15第6utf8_info bytes是==>4c696e654e756d6265725461626c65字面常量是==>LineNumberTable第7个常量的tag==>01第7 utf8_info length十六进制是==>0007第7 utf8_info length十进制是==>7第7utf8_info bytes是==>6765744e616d65字面常量是==>getName第8个常量的tag==>01第8 utf8_info length十六进制是==>0014第8 utf8_info length十进制是==>20第8utf8_info bytes是==>28294c6a6176612f6c616e672f537472696e673b字面常量是==>()Ljava/lang/String;第9个常量的tag==>01第9 utf8_info length十六进制是==>0007第9 utf8_info length十进制是==>7第9utf8_info bytes是==>7365744e616d65字面常量是==>setName第10个常量的tag==>01第10 utf8_info length十六进制是==>0015第10 utf8_info length十进制是==>21第10utf8_info bytes是==>284c6a6176612f6c616e672f537472696e673b2956字面常量是==>(Ljava/lang/String;)V第11个常量的tag==>01第11 utf8_info length十六进制是==>000a第11 utf8_info length十进制是==>10第11utf8_info bytes是==>536f7572636546696c65字面常量是==>SourceFile第12个常量的tag==>01第12 utf8_info length十六进制是==>0009第12 utf8_info length十进制是==>9第12utf8_info bytes是==>557365722e6a617661字面常量是==>User.java已经读取16个常量,下面开始读取第17个常量#########################################第17个常量的tag==>0cclass_info第1个index是==>0007class_info第2个index是==>0008第18个常量的tag==>0cclass_info第1个index是==>0005class_info第2个index是==>0006第19个常量的tag==>01第19 utf8_info length十六进制是==>0004第19 utf8_info length十进制是==>4第19utf8_info bytes是==>55736572字面常量是==>User第20个常量的tag==>01第20 utf8_info length十六进制是==>0010第20 utf8_info length十进制是==>16第20utf8_info bytes是==>6a6176612f6c616e672f4f626a656374字面常量是==>java/lang/Objectacces_flags==>0021this_class==>0003super_class==>0004interfaces_cout==>0000fileds_count==>0001fileds acces_flags==>0002fileds index==>0005fileds descriptor index==>0006attribute_count==>0000methods_count==>0003

 

完成的二进制编码:

 

 

 

转载于:https://my.oschina.net/zaxb/blog/1544126

你可能感兴趣的文章
I.MX6 Android USB Touch eGTouchA.ini文件存放
查看>>
4-5-创建索引表-串-第4章-《数据结构》课本源码-严蔚敏吴伟民版
查看>>
java 操作 RabbitMQ 发送、接受消息
查看>>
go run main.go undefined? golang main包那点事
查看>>
数据挖掘(二):认识数据
查看>>
从零开始写一个npm包,一键生成react组件(偷懒==提高效率)
查看>>
Volley(二)—— 基本Request对象 & RequestQueue&请求取消
查看>>
2017中国系统架构师大会“盛装”来袭
查看>>
Google插件switchysharp的用法
查看>>
中国最强的人工智能学术会议来了
查看>>
Metasploit的射频收发器功能 | Metasploit’s RF Transceiver Capabilities
查看>>
主库 归档 删除策略
查看>>
Chrome 更新策略大变:优先安装 64 位版本
查看>>
《Linux从入门到精通(第2版)》——导读
查看>>
路过下载攻击利用旧版 Android 漏洞安装勒索软件
查看>>
ThinkSNS 六大子版本体验及源码下载
查看>>
《算法基础》——1.5实际因素
查看>>
《Java数字图像处理:编程技巧与应用实践》——第3章 基本Swing UI组件与图像显示 3.1 JPanel组件与BufferedImage对象的显示...
查看>>
为什么有人讨厌 Google 的新 Logo?
查看>>
2022 年 AI 会发展成什么样子,IBM 做出了 5 大预测
查看>>