JVM_Class文件格式示例

合集下载

JavaClass类解析

JavaClass类解析

1.Class对象Class对象包含了与类相关的信息。

事实上,Class对象就是用来创建类的所有的“普通”对象的。

类是程序的一部分,每个类都有一个Class对象。

换言之,每当编写并且编译了一个新类,就会产生一个Class对象(恰当地说,是被保存在一个同名的.class文件中)。

在运行时,当我们想生成这个类的对象时,运行这个程序的Java虚拟机(JVM)首先检查这个类的Class对象是否已经加载。

如果尚未加载,JVM就会根据类名查找.class文件,并将其载入。

一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。

看下面示例。

SweetShop.javapackage com.zj.sample;class Candy {static {System.out.println("Loading Candy");}}class Gum {static {System.out.println("Loading Gum");}}class Cookie {static {System.out.println("Loading Cookie");}}public class SweetShop {public static void main(String[] args) {System.out.println("inside main");new Candy();System.out.println("After creating Candy");try {Class.forName("com.zj.sample.Gum");} catch (ClassNotFoundException e) {System.out.println("Couldn't find Gum");}System.out.println("After Class.forName(\"Gum\")");new Cookie();System.out.println("After creating Cookie");}}结果:Loading CandyAfter creating CandyLoading GumAfter Class.forName("Gum")Loading CookieAfter creating Cookie2.获取Class实例的三种方式1)利用对象调用getClass()方法获取该对象的Class实例。

jvm 参数格式

jvm 参数格式

jvm 参数格式(实用版)目录1.JVM 参数简介2.JVM 参数格式3.常见 JVM 参数及其作用4.JVM 参数配置方法5.总结正文1.JVM 参数简介JVM(Java 虚拟机)是 Java 语言的核心组件,负责管理 Java 程序的运行。

在 Java 程序运行过程中,JVM 会根据程序的需求和系统资源进行必要的调整,以保证程序的稳定运行。

这些调整通常通过 JVM 参数来实现。

2.JVM 参数格式JVM 参数采用键值对(key-value)的形式进行配置,其中键表示参数的名称,值表示参数的值。

参数名和值的格式通常为:```参数名=参数值```多个参数之间用空格分隔,如:```-Xmx2g -Xms1g```3.常见 JVM 参数及其作用以下是一些常见的 JVM 参数及其作用:-Xms:初始堆内存大小。

指定 Java 程序启动时堆内存的初始大小。

-Xmx:最大堆内存大小。

指定 Java 程序运行过程中堆内存允许达到的最大值。

-Xmn:年轻代堆内存大小。

指定年轻代堆内存的大小。

-Xss:栈空间大小。

指定每个线程的栈空间大小。

-XX:+UseComcSpin:使用自旋锁。

开启此参数可以提高多线程程序的性能。

-XX:+UseConcMarkSweepGC:使用并发标记清除垃圾收集器。

开启此参数可以提高垃圾回收的效率。

4.JVM 参数配置方法JVM 参数可以通过以下几种方法进行配置:-在启动 Java 程序时,通过命令行参数的形式指定。

如:```java -Xmx2g -Xms1g -XX:+UseComcSpin -XX:+UseConcMarkSweepGC YourMainClass```-在 Java 程序代码中,通过`System.getProperty()`方法获取参数值。

如:```javaString xmx = System.getProperty("Xmx");```-在 Java 程序的 JVM 启动参数配置文件(如:jvm.config)中指定。

class文件直接修改_反编译修改class文件变量

class文件直接修改_反编译修改class文件变量

class⽂件直接修改_反编译修改class⽂件变量今天笔者同事遇到⼀个问题,客户同事的数据库连接信息直接写在代码中,连接的密码改了,但是⼜没有源代码,所以只能直接修改Java class⽂件。

记录⼀下修改步骤:1.下载JClassLib_windows(后⾯⽤到),下载jd-gui-1.4.0.jar,⽤来反编译class⽂件的,IDEA也可以反编译class⽂件;找到需要修改⽂件的变量,我这⾥是Admin。

2.第⼆步把class⽂件,备份待⽤,把⼯程⽂件存⼀份,⽅法如下图所⽰:3.安装JClassLib,安装完成之后⽤JClassLib打开需要修改的class⽂件,我这⾥是Client.class,找到需要修改的函数⽅法位置,我这⾥是main函数,然后点菜单的code,查看右侧的Bytecode,找到需要修改的变量Admin,然后点击#173,会跳到变量区,如下⾯⼆张图说明: 图1图2 4.⿏标点击cp_info#174会跳转到174这⾏变量,如下图所⽰:4.替换变量写⼀个Java程序,替换指定变量的值,filePath变量存在需要替换的class⽂件路径,174是需要替换的位置,Admin123是希望替换的变量,如果替换成中⽂需要注意⽬标编码(不然会⽤JVM默认编码),如下图所⽰:图1图2 中⽂编码设置4.上⾯的Java程序完整代码如下,特别需要注意的是:程序依赖JClasslib包下的jclasslib-library.jar包,所以可以进到JClasslib安装路径下拷贝⼀个jclasslib-library.jar包⽂件放到build path内,我的路径和jar包⽂件如下:完整代码,你们替换三处,⽂件路径、变量位置、修改后⽬标变量import java.io.DataInput;import java.io.DataInputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import org.gjt.jclasslib.io.ClassFileWriter;import org.gjt.jclasslib.structures.CPInfo;import org.gjt.jclasslib.structures.ClassFile;import org.gjt.jclasslib.structures.InvalidByteCodeException;import org.gjt.jclasslib.structures.constants.ConstantUtf8Info; public class Client{@SuppressWarnings("deprecation")public static void main(String[] args){String filePath = "C:\\Users\\zw\\Desktop\\Client.class";FileInputStream fis = null;try {fis = new FileInputStream(filePath);} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}DataInput di = new DataInputStream(fis);ClassFile cf = new ClassFile();try {cf.read(di);} catch (InvalidByteCodeException e1) {// TODO Auto-generated catch blocke1.printStackTrace();} catch (IOException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}CPInfo[] infos = cf.getConstantPool();int count = infos.length;for (int i = 0; i < count; i++) {if (infos[i] != null) {System.out.print(i);System.out.print(" = ");try {System.out.print(infos[i].getVerbose());} catch (InvalidByteCodeException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.print(" = ");System.out.println(infos[i].getTagVerbose());if(i == 174){ConstantUtf8Info uInfo = (ConstantUtf8Info)infos[i]; uInfo.setBytes("Admin123".getBytes());infos[i]=uInfo;}}}cf.setConstantPool(infos);try {fis.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}File f = new File(filePath);try {ClassFileWriter.writeToFile(f, cf);} catch (InvalidByteCodeException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}另外我把⼯程。

class,classloder,dex详解

class,classloder,dex详解

class,classloder,dex详解class与dex⽂件什么是class⽂件class⽂件是⼀种能够被JVM识别,加载并且执⾏的⽂件格式。

class⽂件的作⽤class⽂件的作⽤是记录⼀个类⽂件的所有信息。

例如记住了当前类的引⽤this、⽗类super等等。

class⽂件记录的信息往往⽐java⽂件多。

class⽂件的结构8位字节的⼆进制流⽂件各个数据紧密排列,⽆间隙,减少了⽂件体积,加快加载速度每个类或者接⼝单独占据⼀个class⽂件,每个类单独管理,没有交叉class⽂件的弊端内存占⽤⼤,不适合于移动端堆栈的加载模式导致加载速度慢⽂件IO操作多,类查找慢什么是dex⽂件能够被DVM或者Art虚拟机执⾏并且加载的⽂件格式。

dex⽂件的作⽤dex⽂件的作⽤是记录整个⼯程(通常是⼀个Android⼯程)的所有类⽂件的信息。

dex⽂件的结构8位字节的⼆进制流⽂件各个数据紧密排列,⽆间隙,减少了⽂件体积,加快加载速度整个⼯程的类信息都存放在⼀个dex⽂件中(不考虑dex分包的情况下)class⽂件与dex⽂件的⽐较本质上都是⼀样的,都是⼆进制流⽂件格式,dex⽂件是从class⽂件演变⽽来的class⽂件存在冗余信息,dex⽂件则去掉了冗余,并且整合了整个⼯程的类信息。

结构对⽐图如下:类加载机制是做热修复以及插件化的很重要的理论基础。

Java中的ClassLoader回顾如下图所⽰:ClassLoader的特性ClassLoader的主要特性是双亲委托机制,即加载⼀个类的时候,先判断已经存在的类是否被加载过,如果没有,先去委托⽗亲去加载。

如果⽗亲都没有加载成功的话,那么最终由⾃⼰加载。

最终这个类最终没有合适的CLassLoader加载,那么就会抛出异常,相关的ClassLoader源码如下:protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{// 先判断这个类是否已经被加载过Class c = findLoadedClass(name);if (c == null) {// 如果没有被加载过,那么委托⽗亲去加载long t0 = System.nanoTime();try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}// 如果⽗亲没有加载,那么最终由⾃⼰(实现类)负责加载if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the stats}}return c;}其中CLassLoader的findClass⽅法是空实现,需要⾃⼰继承然后实现:protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);}这种双亲委托机制有两种好处:1. 实现类加载的共享功能,提升类加载的效率。

JVM 之 Class文件资料结构

JVM 之 Class文件资料结构

JVM 之Class文件结构本文写作目的:1)为了加深自己学习的理解,2)帮助正在学习研究JVM的,3)与任何热爱技术的达人交流经验,提升自己以此为本,文章会尽量写的简洁,尽量保证理解的正确性,如有任何理解不到位或错误的地方,希望朋友们及时指出,严厉拍砖。

开始之前我们需要先了解一些基本的概念,这些概念是学习整个JVM原理的基础。

1)JVM虚拟机规主要规了Class文件结构,虚拟机存结构,虚拟机加载,解析,执行Class文件的行为方式,以及一系列的字节码指令集。

2)Class文件理论上说是一种数据结构,该数据结构有着严格的格式规,该规在字节粒度上规定了组成该数据结构的格式标准。

3)Class文件本质上是一组二进制字节流,是被JVM解析执行的数据源,每个字节都有着不同的含义,可能表示字符,数字,也可能表示执行某种操作的一个字节码指令。

4)JVM (Java 虚拟机)是解析执行Class文件的核心引擎,是整个Java系统的运行时环境,是跨平台的基石。

5)我们的Java代码需要被编译器编译成完整,正确的Class文件才能被JVM正确的执行。

6)编译器并非JVM的一部分,不同的语言可以提供不同的编译器,其作用是将该语言的代码编译为正确的Class文件,如Scala,JRuby等等。

7)JVM是完全开放的跨平台的,只要你有能力你可以按照JVM虚拟机规编写自己的编程语言。

8)JVM 使得Java的跨平台成为可能,那么Class文件结构规则使得更多的编程语言运行在JVM上成为可能。

既然Class文件是一种数据结构,那么到底是什么样的数据结构呢?通常计算机中的文件都包含元数据和实体数据两部分,元数据用来存储该文件的描述信息,实体数据来存放用于表达文件真实容的数据。

当然Class文件也不例外,我们为了便于理解,也将class文件的结构分为两大部分:元数据和实体数据(注:非规定义,只是为了方便理解进行的自定义)。

元数据:包含了Class文件的魔术数(标识符)和版本信息。

运行java的class文件方法详解

运行java的class文件方法详解

运⾏java的class⽂件⽅法详解⼀、运⾏class⽂件执⾏带main⽅法的class⽂件,命令⾏为:java <CLASS⽂件名>注意:CLASS⽂件名不要带⽂件后缀.class例如:复制代码代码如下:java Test如果执⾏的class⽂件是带包的,即在类⽂件中使⽤了:package <包名>那应该在包的基路径下执⾏,命令⾏为:java <包名>.CLASS⽂件名例如:PackageTest.java中,其包名为:com.ee2ee.test,对应的语句为:package com.ee2ee.test;PackageTest.java及编译后的class⽂件PackageTest.class的存放⽬录如下:classes|__com|__ee2ee|__test|__PackageTest.java|__PackageTest.class要运⾏PackageTest.class,应在classes⽬录下执⾏:复制代码代码如下:java com.ee2ee.test.PackageTest⼆、运⾏jar⽂件中的class原理和运⾏class⽂件⼀样,只需加上参数-cp <jar⽂件名>即可。

例如:执⾏test.jar中的类com.ee2ee.test.PackageTest,命令⾏如下:复制代码代码如下:java -cp test.jar com.ee2ee.test.PackageTest三、显⽰jdk版本信息当⼀台机器上有多个jdk版本时,需要知道当前使⽤的是那个版本的jdk,使⽤参数-version即可知道其版本,命令⾏为:复制代码代码如下:java -version四、增加虚拟机可以使⽤的最⼤内存java虚拟机可使⽤的最⼤内存是有限制的,缺省值通常为64MB或128MB。

如果⼀个应⽤程序为了提⾼性能⽽把数据加载内存中⽽占⽤较⼤的内存,⽐如超过了默认的最⼤值128MB,需要加⼤java虚拟机可使⽤的最⼤内存,否则会出现Out of Memory(系统内存不⾜)的异常。

java程序来解析class文件,实现反编译

java程序来解析class文件,实现反编译

[B01][java程序解析class文件]Type:[B01][Solution]Submit:[abc@][2012.11.06][V1.00]From:/jtf975/blog/static/18135128720112612517429/我们都知道,Java编译器负责将.java文件编译成.class文件,class文件存储的是java字节码,与.java 文件无关(只要你愿意写一个编译器,也可以将别的语言写的源代码编译成.class文件),本文预备具体解剖class文件的内部结构,并且把class文件结构读取并显示出来。

Class文件的格式由JVM规范规定,一共有以下部分:1. magic number,必须是0xCAFEBABE,用于快速识别是否是一个class文件。

2. version,包括major和minor,假如版本号超过了JVM的识别范围,JVM将拒尽执行。

3. constant pool,常量池,存放所有用到的常量。

4. access flag,定义类的访问权限。

5. this class和super class,指示如何找到this class和super class。

6. inte***ces,存放所有inte***ces。

7. fields,存放所有fields。

8. methods,存放所有methods。

9. attributes,存放所有attributes。

先写一个Test.java:package example.test;public final class TestClass {public int id = ;public void test() {}}公司地址:北京海淀区彩和坊路10号1+1大厦3层100080然后编译,放在C:\Documents and Settings\Administrator\桌面\解析class文件\TestClass.java。

描述一下JVM加载class文件的原理机制

描述一下JVM加载class文件的原理机制

描述⼀下JVM加载class⽂件的原理机制Java中的所有类,都需要由类加载器装载到JVM中才能运⾏。

类加载器本⾝也是⼀个类,⽽它的⼯作就是把class⽂件从硬盘读取到内存中。

在写程序的时候,我们⼏乎不需要关⼼类的加载,因为这些都是隐式装载的,除⾮我们有特殊的⽤法,像是反射,就需要显式的加载所需要的类。

类装载⽅式,有两种:1.隐式装载,程序在运⾏过程中当碰到通过new 等⽅式⽣成对象时,隐式调⽤类装载器加载对应的类到jvm中,2.显式装载,通过class.forname()等⽅法,显式加载需要的类Java类的加载是动态的,它并不会⼀次性将所有类全部加载后再运⾏,⽽是保证程序运⾏的基础类(像是基类)完全加载到jvm 中,⾄于其他类,则在需要的时候才加载。

这当然就是为了节省内存开销。

Java的类加载器有三个,对应Java的三种类:Bootstrap Loader :启动类加载器,是虚拟机⾃⾝的⼀部分。

负责将存放在\lib⽬录中的类库加载到虚拟机中。

其⽆法被Java程序直接引⽤。

负责加载系统类 (指的是内置类,像是String,对应于C#中的System类和C/C++标准库中的类) ExtClassLoader :负责加载扩展类(就是继承类和实现类)AppClassLoader :负责加载⽤户类路径(ClassPath)上所指定的类库(程序员⾃定义的类)JVM中类的加载是由类加载器(ClassLoader)和它的⼦类来实现的,Java中的类加载器是⼀个重要的Java运⾏时系统组件,它负责在运⾏时查找和装⼊类⽂件中的类。

由于Java的跨平台性,经过编译的Java源程序并不是⼀个可执⾏程序,⽽是⼀个或多个类⽂件。

当Java程序需要使⽤某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。

类的加载是指把类的.class⽂件中的数据读⼊到内存中,通常是创建⼀个字节数组读⼊.class⽂件,然后产⽣与所加载类对应的Class对象。

JVM之用Java解析class文件

JVM之用Java解析class文件

JVM之用Java解析class文件前言:身为一个Java程序员,怎么能不了解JVM呢,倘若想学习JVM,那就又必须要了解Class 文件,Class之于虚拟机,就如鱼之于水,虚拟机因为Class而有了生命。

《深入理解java 虚拟机》中花了一整个章节来讲解Class文件,可是看完后,一直都还是迷迷糊糊,似懂非懂。

正好前段时间看见一本书很不错:《自己动手写Java虚拟机》,作者利用go语言实现了一个简单的JVM,虽然没有完整实现JVM的所有功能,但是对于一些对JVM稍感兴趣的人来说,可读性还是很高的。

作者讲解的很详细,每个过程都分为了一章,其中一部分就是讲解如何解析Class文件。

这本书不太厚,很快就读完了,读完后,收获颇丰。

但是纸上得来终觉浅,绝知此事要躬行,我便尝试着自己解析Class文件。

go语言虽然很优秀,但是终究不熟练,尤其是不太习惯其把类型放在变量之后的语法,还是老老实实用java吧。

话不多说,先贴出项目地址:https:///HalfStackDeveloper/ClassReader Class文件什么是Class文件?java之所以能够实现跨平台,便在于其编译阶段不是将代码直接编译为平台相关的机器语言,而是先编译成二进制形式的java字节码,放在Class文件之中,虚拟机再加载Class 文件,解析出程序运行所需的内容。

每个类都会被编译成一个单独的class文件,内部类也会作为一个独立的类,生成自己的class。

基本结构随便找到一个class文件,用Sublime Text打开是这样的:是不是一脸懵逼,不过java虚拟机规范中给出了class文件的基本格式,只要按照这个格式去解析就可以了:ClassFile {u4 magic;u2 minor_version;u2 major_version;u2 constant_pool_count;cp_info constant_pool[constant_pool_count-1];u2 access_flags;u2 this_class;u2 super_class;u2 interfaces_count;u2 interfaces[interfaces_count];u2 fields_count;field_info fields[fields_count];u2 methods_count;method_info methods[methods_count];u2 attributes_count;attribute_info attributes[attributes_count];}ClassFile中的字段类型有u1、u2、u4,这是什么类型呢?其实很简单,就是分别表示1个字节,2个字节和4个字节。

class的格式

class的格式

class的格式
Class文件是一组以8位字节为基础的二进制流。

根据Java虚拟机规范的规定,Class文件格式采用一种类似于C语言结构体的伪结构来存储数据,这种结构只用两种数据类型:无符号数和表。

无符号数以u1、u2、u4、u8分别来代表1、2、4、8个字节的无符号数。

表是由多个无符号数或者其他表为数据项组成的复合数据类型,习惯以“_info”结尾。

Class文件格式包含以下数据项:
1. 魔数(魔数用于判断是否可以被虚拟机支持)和版本。

2. 常量池(常量池是最大的数据项,也是第一个使用表类型数据项)。

常量池主要存放两个类常量:字面量和符号引用。

3. 访问标示(用于识别一些类或者接口层次的访问信息,例如public、private、abstract、final等)。

4. 类、父类、接口、字段、方法、属性等。

Class文件中的字节码指令由一个操作码和操作数组成,代表某些特殊的操作,例如对象创建new、检查类实列类型指令instanceof、类型转换指令等。

此外,还包括控制指令如if,异常指令等。

如需了解更多关于class的格式,建议阅读《深入理解Java虚拟机JVM高级特性与最佳实践》等书籍或请教专业人士。

JVM之Class文件资料结构

JVM之Class文件资料结构

JVM之Class⽂件资料结构JVM 之Class⽂件结构本⽂写作⽬的:1)为了加深⾃⼰学习的理解,2)帮助正在学习研究JVM的,3)与任何热爱技术的达⼈交流经验,提升⾃⼰以此为本,⽂章会尽量写的简洁,尽量保证理解的正确性,如有任何理解不到位或错误的地⽅,希望朋友们及时指出,严厉拍砖。

开始之前我们需要先了解⼀些基本的概念,这些概念是学习整个JVM原理的基础。

1)JVM虚拟机规主要规了Class⽂件结构,虚拟机存结构,虚拟机加载,解析,执⾏Class⽂件的⾏为⽅式,以及⼀系列的字节码指令集。

2)Class⽂件理论上说是⼀种数据结构,该数据结构有着严格的格式规,该规在字节粒度上规定了组成该数据结构的格式标准。

3)Class⽂件本质上是⼀组⼆进制字节流,是被JVM解析执⾏的数据源,每个字节都有着不同的含义,可能表⽰字符,数字,也可能表⽰执⾏某种操作的⼀个字节码指令。

4)JVM (Java 虚拟机)是解析执⾏Class⽂件的核⼼引擎,是整个Java系统的运⾏时环境,是跨平台的基⽯。

5)我们的Java代码需要被编译器编译成完整,正确的Class⽂件才能被JVM正确的执⾏。

6)编译器并⾮JVM的⼀部分,不同的语⾔可以提供不同的编译器,其作⽤是将该语⾔的代码编译为正确的Class⽂件,如Scala,JRuby等等。

7)JVM是完全开放的跨平台的,只要你有能⼒你可以按照JVM虚拟机规编写⾃⼰的编程语⾔。

8)JVM 使得Java的跨平台成为可能,那么Class⽂件结构规则使得更多的编程语⾔运⾏在JVM上成为可能。

既然Class⽂件是⼀种数据结构,那么到底是什么样的数据结构呢?通常计算机中的⽂件都包含元数据和实体数据两部分,元数据⽤来存储该⽂件的描述信息,实体数据来存放⽤于表达⽂件真实容的数据。

当然Class⽂件也不例外,我们为了便于理解,也将class⽂件的结构分为两⼤部分:元数据和实体数据(注:⾮规定义,只是为了⽅便理解进⾏的⾃定义)。

JavaClass文件格式详解

JavaClass文件格式详解

JavaClass⽂件格式详解magic[4字节] 魔数,⽤来判断是否可以被虚拟机使⽤。

固定值为0xCAFEBABE(咖啡宝贝)minor_version[2字节] 次版本号major_version[2字节] 主版本号,低版本的jdk⽆法执⾏⾼版本的class⽂件。

constant_pool_count[2字节] 常量池⾥的项⽬个数constant_pool 常量池⾥每⼀个项⽬类型都⽤⼀个tag标⽰。

从1开始取值,⽐如取值为1时,表⽰info⾥存放的是utf8的字符串 tag[1字节] 不同的取值,决定了其下info的结构不同 infoaccess_flags[2字节] 类的访问标识位,⽤来标识类是否具有pulbic/abstract/interface/final等修饰符。

⽤其中的bit位标识是否存在。

例如,如果是public的class,其值为0x0001this_class[2字节] 两个字节的数值,指向常量池⾥的某⼀个项⽬。

这⾥指向的是当前类的全名称super_class[2字节] 指向常量池⾥的当前类的⽗类全名称interfaces_count[2字节] 当前类实现的接⼝个数interfaces 每⼀个指向常量池⾥的接⼝的全名称fields_count[2字节] 当前类的成员变量个数fields 成员变量信息 access_flags[2字节] 成员变量的访问标识,与上边access_flags相似 name_index[2字节] 指向常量池⾥当前字段的名字 desc_index[2字节] 指向常量池⾥当前字段的描述。

例如字符串类型对应的描述是'ng.String;' attribute_count[4字节] 字段的属性表个数,跟类的属性表类似。

在下⾯介绍 attributes 存放字段的属性信息methods_count[2字节] 当前类的成员⽅法个数mehtods 成员⽅法信息 access_flags[2字节] 成员⽅法的访问标识,与上边access_flags相似 name_index[2字节] 指向常量池⾥当前⽅法的名字 desc_index[2字节] 指向常量池⾥当前⽅法的签名。

class文件解读

class文件解读

class文件解读第一部分 class文件结构及解析1、Class文件结构class文件是java虚拟机指定的二进制文件,扩展名为.class,一个类在编译后被编译器编译为一个class文件。

class文件是一种独立于平台的文件,class文件的格式由Java虚拟机规范指定,class 文件结构由字节码、常量池、类型描述符、字段、方法等构成。

Class文件的结构如下:1)magic number:Class文件的前4个字节,它的值是0xCAFEBABE,表示当前文件是一个有效的Java class文件。

2)次版本号:Class文件共有两个版本号,major_version和minor_version,表示Class文件的Java虚拟机支持的版本。

3)常量池:常量池是Class文件中最大的构件,用于保存常量,比如类名、方法名、字段名和字符串值等,在类加载期间,类的相关信息都是从常量池中提取的。

4)修饰符:Class文件的修饰符,用于描述类的访问权限、是否为抽象类等信息。

5)类名:Class文件中类的名称,在常量池中由一个索引指向它的名字。

6)超类:Class文件中超类的名称,在常量池中由一个索引指向它的名字。

7)接口:Class文件中实现的接口列表,在常量池中由一个索引指向每个接口的名字。

8)字段:Class文件中所定义的字段,它由字段名、类型和访问修饰符等信息构成。

9)方法:Class文件中定义的方法,它由方法名、参数列表、返回类型和字节码等信息构成。

10)属性:Class文件中提供的其他附加信息,包括代码版本、类的注释和类的异常处理器等信息2、Class文件解析Class文件解析就是读取class文件,将各个部分的字节码解码成可读的java源码,可以用来查看源代码,调试程序,反编译class 文件等,重要的工具包括:jad、Javap等。

3、Jad反编译器Jad是一个著名的反编译器,它可以把class文件反编译成源代码,即把class文件的字节码解码为可读的java源码,可以用来查看源代码,调试程序,反编译class文件等,jad反编译类文件稍显复杂,但是能够得到准确的源代码,是一款非常有用的反编译工具,Jad反编译器还支持Windows、Linux等多个平台,具体使用方法如下:1)安装jad,下载文件并解压。

【JVM】class文件内部结构

【JVM】class文件内部结构

【JVM】class⽂件内部结构问题:1.如何将.java⽂件转化为.class⽂件?2.class⽂件的内部结构是怎样的?如何查看它的内部结构内容?3.字节码是什么?⼀、先介绍两个命令1)javac将java⽂件编译成class⽂件⽐如:HelloWorld.java ==> HelloWorld.classjava -g HelloWorld.java 可⽣成更多的调试信息HelloWorld.java如下:public class HelloWorld {public static void main(String[] args) {System.out.println("Hello world!");}}2)javap反编译命令,解析class⽂件内容,通过该命令可以查看class内部结构➜ MyGithub javapUsage: javap <options> <classes>where possible options include:-help --help -? Print this usage message-version Version information-v -verbose Print additional information-l Print line number and local variable tables-public Show only public classes and members-protected Show protected/public classes and members-package Show package/protected/public classesand members (default)-p -private Show all classes and members-c Disassemble the code-s Print internal type signatures-sysinfo Show system info (path, size, date, MD5 hash)of class being processed-constants Show final constants-classpath <path> Specify where to find user class files-cp <path> Specify where to find user class files-bootclasspath <path> Override location of bootstrap class filesjavap 有⽐较多的参数选项,其中-c -v -l -p -s是最常⽤的。

class文件概述

class文件概述

class⽂件概述将java代码编译后会产⽣class⽂件,并且⼀个clas⽂件会对应唯⼀⼀个java类或者接⼝。

下⾯对⼀个通过⼀个简单的例⼦来简述⼀下class⽂件的结构。

java代码public class JavaMethodAreaOOM{String str = "abc";int i = 1;static String str1 = "123";public static void main(String[] args) {int b = 1;}public void test(){System.out.println(str1);}}在java代码中我们定义了⼀些属性和⽅法。

下⾯是编译⽣成的字节码⽂件字节码⽂件我们看到,源代码不长,但是⽣成的字节码⽂件内容却很多,下⾯就针对上⾯的例⼦进⾏分析。

字节码分析 在字节码⽂件中只存储了两种类型的数据,⼀种是⽆符号数(u1,u2,u4,u8,u1表⽰⼀个字节,u2表⽰两个字节...),⼀种是表(表其实就是⽆符号的集合,不过表中的数据之间是有联系的,⽽且表中还可以存放表,表⼀般以_info结尾)。

字节码⽂件中数据存放顺序是按照下⾯表中的顺序存放的,⽐如⽂件的开始位置会存放4个字节的数据,我们称之为magic。

所以,在进⾏字节码分析时,我们可以参考下⾯的表进⾏对⽐分析。

magic(u4)的翻译是魔术,但是这⾥是魔数。

这个魔数的作⽤就是⽂件标识,⽤来标识这是⼀个字节码⽂件可以被JVM加载运⾏。

查看字节码开始的4个字节,我们会发现是fecababe。

所有的字节码⽂件开始都是这样的。

minor_version(u2)紧跟着魔数有两个字节(0000)表⽰次版本号,次版本⼀般是JDK的某个分⽀如JDK 1.1有个分⽀为JDK1.1.8,那么此版本号就为0x0003。

这个次版本号不太重要。

major_version(u2)接下来的两个字节(0033)表⽰主版本号,这个主版本号很重要。

JVM-class文件完全解析-字段表集合

JVM-class文件完全解析-字段表集合

JVM-class⽂件完全解析-字段表集合字段表集合 这个class⽂件的解析,分析得有点太久了.前⾯介绍类魔数,次版本号,主板本号,常量池⼊⼝,常量池,访问标志,类索引,⽗类索引和接⼝索引集合.下⾯就应该到字段表集合了. 紧接着接⼝索引集合的就是字段表的集合了. 字段表(field_info)⽤于描述接⼝或者类中声明的变量.字段包括类级变量以及实例级变量,但是不包括在⽅法内部声明的局部变量. 字段表的结构:类型 名称数量u2access_flags1u2name_index1u2descriptor_index1u2attributes_count1attribute_info attributes attributes_count 字段修饰符放在access_flags项⽬中,它与类中的access_flags项⽬是⾮常相似的,都是⼀个u2的数据类型. 字段访问标志:标志名称标志值含义ACC_PUBLIC0x00 01字段是否为publicACC_PRIVATE0x00 02字段是否为privateACC_PROTECTED0x00 04字段是否为protectedACC_STATIC0x00 08字段是否为staticACC_FINAL0x00 10字段是否为finalACC_VOLATILE0x00 40字段是否为volatileACC_TRANSTENT0x00 80字段是否为transientACC_SYNCHETIC0x10 00字段是否为由编译器⾃动产⽣ACC_ENUM0x40 00字段是否为enum 跟随access_flags标志的是两项索引值:name_index和descriptor_index,它们都是对常量池的引⽤,分别代表着字段的简单名称以及字段⽅法和⽅法的描述符. 描述符的作⽤是⽤来描述字段的数据类型,⽅法的参数列表(包括数量,类型以及顺序)和返回值.根据描述符规则,基本数据类型以及代表⽆返回值的void类型都⽤⼀个⼤写字符来表⽰,⽽对象类型则⽤字符加L加对象名的全限定名来表⽰. 描述符标志含义:标志符含义B基本数据类型byteC基本数据类型charD基本数据类型doubleF基本数据类型floatI基本数据类型intJ基本数据类型longS基本数据类型shortS基本数据类型shortZ基本数据类型booleanV基本数据类型voidL对象类型 对于数组类型,每⼀维度将使⽤⼀个前置的"["字符来描述.如⼀个定义为"ng.Stirng[ ]"类型的⼆维数组,将被记录为:"[[Ljava/lang/Stirng",⼀个整型数组"int[]"将被记录为"[I". ⽤描述符来描述⽅法时,按照先参数列表,后返回值的顺序来描述,参数列表按照参数的严格顺序放在⼀组⼩括号"()"之内. 字段表集合中不会列出从⽗类或者⽗接⼝中继承⽽来的字段,但有可能列出原来Java代码中不存在的字段,譬如在内部类中为了保持对外部类的访问性,会⾃动添加指向外部类实例的字段.另外,在Java语⾔中字段是⽆法重载的,两个字段的数据类型,修饰符不管是否相同,都必须使⽤不⼀样的名称,但是对于字节码来讲,如果连个字段的描述符不⼀致,那字段重名就是合法的. 下⾯继续前⾯分析的class⽂件: 源⽂件: javap分析出来的常量池: 分析: 从上图中分析可以看到0x00 01表⽰字段表数据的个数,只有⼀个.0x00 02表⽰字段表的private修饰符,从上⾯的字段访问标志表可以看到.0x00 05表⽰字段对应着第5个常量池,从javap分析出来的常量池,可以看到第5个常量池对应着m,再看源代码我们定义的字段确实就是m.0x00 06表⽰描述符标识,对应着第6个常量池,为I,那么在对应我们的访问标识符含义表,I 对应着int数据类型,再看源代码m的数据类型确实就是int.。

通过ASM库生成和修改class文件

通过ASM库生成和修改class文件

通过ASM库⽣成和修改class⽂件在主要详细讲解了Class⽂件的格式,并且在上⼀篇⽂章中做了总结。

众所周知,JVM 在运⾏时,加载并执⾏class⽂件,这个class⽂件基本上都是由我们所写的java源⽂件通过 javac 编译⽽得到的。

但是,我们有时候会遇到这种情况:在前期(编写程序时)不知道要写什么类,只有到运⾏时,才能根据当时的程序执⾏状态知道要使⽤什么类。

举⼀个常见的例⼦就是 JDK 中的动态代理。

这个代理能够使⽤⼀套API代理所有的符合要求的类,那么这个代理就不可能在 JDK 编写的时候写出来,因为当时还不知道⽤户要代理什么类。

当遇到上述情况时,就要考虑这种机制:在运⾏时动态⽣成class⽂件。

也就是说,这个 class ⽂件已经不是由你的 Java 源码编译⽽来,⽽是由程序动态⽣成。

能够做这件事的,有JDK中的动态代理API,还有⼀个叫做 cglib 的开源库。

这两个库都是偏重于动态代理的,也就是以动态⽣成 class 的⽅式来⽀持代理的动态创建。

除此之外,还有⼀个叫做 ASM 的库,能够直接⽣成class⽂件,它的 api 对于动态代理的 API 来说更加原⽣,每个api都和 class ⽂件格式中的特定部分相吻合,也就是说,如果对 class ⽂件的格式⽐较熟练,使⽤这套 API 就会相对简单。

下⾯我们通过⼀个实例来讲解 ASM 的使⽤,并且在使⽤的过程中,会对应 class ⽂件中的各个部分来说明。

ASM 库的介绍和使⽤ASM 库是⼀款基于 Java 字节码层⾯的代码分析和修改⼯具,那 ASM 和访问者模式有什么关系呢?访问者模式主要⽤于修改和操作⼀些数据结构⽐较稳定的数据,通过前⾯的学习,我们知道 .class ⽂件的结构是固定的,主要有常量池、字段表、⽅法表、属性表等内容,通过使⽤访问者模式在扫描 .class ⽂件中各个表的内容时,就可以修改这些内容了。

在学习 ASM 之前,可以通过这篇⽂章学习⼀下访问者模式。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

1.环境JDK 1.6.0_10-rc22.源代码/*** 验证java class类格式* 以下代码不要修改:* @author: zhanglixin* @version: 1.0 2013-1-10*/public class ClassFormat1 implements Serializable{final int C=8;final String S="Hello!";private double instaneDouble=1.01;static double staticDouble = 2.02;public static void main(String[] args){boolean localBln = true;}}3.编译后字节码通过javap -version 查看E:\testspace\jvm>javap -verbose ClassFormat1Compiled from "ClassFormat1.java"public class ClassFormat1 extends ng.Object implements java.io.Serializable SourceFile: "ClassFormat1.java"minor version: 0major version: 50Constant pool:const #1 = Method #12.#32; // java/lang/Object."<init>":()V const #2 = Field #11.#33; // ClassFormat1.C:Iconst #3 = String #34; // Hello!const #4 = Field #11.#35; // ClassFormat1.S:Ljava/lang/String; const #5 = double 1.01d;const #7 = Field #11.#36; // ClassFormat1.instaneDouble:D const #8 = double 2.02d;const #10 = Field #11.#37; // ClassFormat1.staticDouble:D const #11 = class #38; // ClassFormat1const #12 = class #39; // java/lang/Objectconst #13 = class #40; // java/io/Serializableconst #14 = Asciz C;const #15 = Asciz I;const #16 = Asciz ConstantValue;const #17 = int 8;const #18 = Asciz S;const #19 = Asciz Ljava/lang/String;;const #20 = Asciz instaneDouble;const #21 = Asciz D;const #22 = Asciz staticDouble;const #23 = Asciz <init>;const #24 = Asciz ()V;const #25 = Asciz Code;const #26 = Asciz LineNumberTable;const #27 = Asciz main;const #28 = Asciz ([Ljava/lang/String;)V;const #29 = Asciz <clinit>;const #30 = Asciz SourceFile;const #31 = Asciz ClassFormat1.java;const #32 = NameAndType #23:#24;// "<init>":()Vconst #33 = NameAndType #14:#15;// C:Iconst #34 = Asciz Hello!;const #35 = NameAndType #18:#19;// S:Ljava/lang/String;const #36 = NameAndType #20:#21;// instaneDouble:Dconst #37 = NameAndType #22:#21;// staticDouble:Dconst #38 = Asciz ClassFormat1;const #39 = Asciz java/lang/Object;const #40 = Asciz java/io/Serializable;{final int C;Constant value: int 8final ng.String S;Constant value: String Hello!static double staticDouble;public ClassFormat1();Code:Stack=3, Locals=1, Args_size=10: aload_01: invokespecial #1; //Method java/lang/Object."<init>":()V 4: aload_05: bipush 87: putfield #2; //Field C:I10: aload_011: ldc #3; //String Hello!13: putfield #4; //Field S:Ljava/lang/String;16: aload_017: ldc2_w #5; //double 1.01d20: putfield #7; //Field instaneDouble:D 23: returnLineNumberTable:line 37: 0line 38: 4line 39: 10line 40: 16public static void main(ng.String[]);Code:Stack=1, Locals=2, Args_size=10: iconst_11: istore_12: returnLineNumberTable:line 44: 0line 45: 2static {};Code:Stack=2, Locals=0, Args_size=00: ldc2_w #8; //double 2.02d3: putstatic #10; //Field staticDouble:D 6: returnLineNumberTable:line 41: 0}4.class文件分析0X00000000h 偏移0-3:魔法数字。

0X00000000h 偏移4-5:次版本号。

0X00000000h 偏移5-6:主版本号。

0X00000000h 偏移8-9:常量池个数。

算法:16进制转换为10进制-1。

0X29:41-1个常量。

0X00000000h 偏移A 至0X00000160h 偏移8:常量池。

0X00000160h 偏移9-A:Access flag类访问修饰符0X00000160h 偏移B-C:当前类名字索引0X00000160h 偏移D-E:父类名字索引0X00000160h 偏移F- 0X00000170h 偏移0 :该类实现的接口数量。

值00 001,1个。

0X00000170h 偏移1-2:接口名字索引。

值00 0D,索引值13.即对应常量池索引号13的项,即该接口名字。

0X00000170h 偏移3- 至0X00000200 偏移4:为域Field的信息0X00000170h 偏移3-4 :域的数量。

值00 04,即4个域。

0X00000170h 偏移5-6 :第一个域的访问修饰(Access Flag),值:00 10,即ACC_FINAL,final方法,不可覆盖。

0X00000170h 偏移7-8 :域名字索引。

值00 0E,即对应常量池第14项,即“C”,域的名字为C 。

0X00000170h 偏移9-a :域名字的描述符。

值00 0F,即对应常量池第15项,即“I”,域的类型为Integter 整型。

0X00000170h 偏移b-c :该域所含属性的数量。

值00 01,含有一个属性。

0X00000170h 偏移d-e :该属性的名字索引。

值00 10,即对应常量池16项,即:ConstantValue.该属性值为常量值。

0X00000170h 偏移f- 0X00000180h 偏移2 :属性的长度,值00 00 00 02,即4个字节长。

0X00000180h 偏移3-4:属性值00 11,即对应常量池的索引是17,为:“int 8”。

以下各个域相同。

0X00000180h 偏移5 至0X00000190 偏移4 :第二个域final String S="Hello!"的域。

0X00000190h 偏移5 至0X00000190 偏移c :第三个域private double instaneDouble=1.01 的域。

注意该域没有属性集合。

0X00000190h 偏移d 至0X000001a0 偏移4 :第四个域static double staticDouble = 2.02 的域。

注意该域没有属性集合。

0X000001a0 偏移5 - 0X00000240h 偏移0 :为方法method的信息。

0X000001a0h 偏移5-6 :方法的数量。

值00 03,即3个方法。

0X000001a0h 偏移7-8 :第一个方法的访问修饰(Access Flag),值:00 01,即ACC_PUBLIC,public方法。

0X000001a0h 偏移9-a :方法名字索引。

值00 17,即对应常量池第23项,即“<init>”,对象实例初始化方法。

0X000001a0h 偏移b-c :方法名字的描述符。

值00 18,即对应常量池第24项,即“()V",方法的参数为空,返回值void。

0X000001a0h 偏移d-e :该方法所含属性的数量。

值00 01,含有一个属性。

0X000001a0h 偏移f- 0X000001b0h 偏移0:该属性的名字索引。

值00 19,即对应常量池25项,即:"Code".该属性值为代码。

0X000001b0h 偏移1-4 :属性的长度,值00 00 00 3c,4个字节长,该属性的长度,3c,即60字节(单位u1),后续60个字节为属性值。

相关文档
最新文档