字节码文件解剖

· 浏览次数 : 69

小编点评

前言: 本文将简要介绍Java字节码文件生成、JclassLib插件使用、Java版本选择及相关问题。 1. Java文件生成.class文件 在Java中,通过`java -c`命令将.java文件编译成.class文件。这部分过程并非JVM处理的关键部分,而是由编译器完成。JVM处理的是基于生成的.class文件。 2. JclassLib插件使用 要使用JclassLib插件,请按照以下步骤操作: a. 运行代码:每次更新代码后,或在进行构建时。 b. 基础信息介绍:介绍了魔数(magic number)的概念,它是文件格式的一部分,用于识别文件是否为Java文件。 3. 版本号问题 关于Java版本的选择,提高Java版本可能会降低依赖库的工作量,但同时也可能导致性能下降。例如,在JDK 8时,要转换为HTTP/2,可能需要引入其他依赖,而在JDK 9中则可以直接转换。 4. 常量池介绍 常量池是字节码文件中用于节省空间并加快JVM解析class文件的一种池化技术。它主要针对final和static修饰的字段。每个以cp开头的字段最终都指向常量池中的一个记录。 5. 字段中的constant_value 使用final修饰符时,需要一个指向constantValue的引用。constantValue属性的存在是为了实现final修饰不可改变的效果。constantValue属性的值实际上是一个UTF-8编码的字符串。 6. 字符串常量池 在String类中,字符串常量池用于保存字符串及其对应的UTF-8编码信息。这样可以节省空间并简化字符串的操作。 7. 局部变量和操作数栈 局部变量最大槽数是指方法中使用的局部变量(包括参数)的数量。操作数栈深度是指字节码运行时用于操作数栈的最大深度。 8. 总结 本文总结了Java字节码文件生成、JclassLib插件使用、Java版本选择和相关问题的关键点。通过深入了解这些概念和技术,可以更好地理解和应用Java编程语言。

正文

#### 前提提要:

.java文件通过java -c 生成.class文件,这部分并非是JVM需要处理的部分,JVM处理的部分是基于生成的class文件,生成的部分是由编译器来负责

一个字节码文件的主要组成部分

image-20240705134632708

使用工具说明

idea的JclassLib插件

使用步骤:

  1. 运行代码(只要你更新了代码就需要,或者build)
  2. image-20240705135213385

基础信息介绍

image-20240705134952556

魔数

魔数并非在这里展示,魔数其实有点类似一个文件的格式中的一部分,所有的软件开发中关于打开文件都会这么设计,用于识别说我们能否成功打开

java中是开头是cafebabe

其他文件如下:

image-20240705135756018

主版本号

1.2后大版本对应的公式是主版本号-44

image-20240705135921638

关于版本号可能遇到的problem及解法
  1. 版本不匹配
    1. 提高java版本
    2. 降低依赖版本

image-20240705140132316

在这之中可能还会遇到的问题:jdk版本存在bug,虽然很多人都说什么永远jdk8,但是实际使用上jdk8在性能上以及开发上可能会比jdk17多不少工作量,例如我之前尝试转为http2,而在jdk8需要引入其他依赖,而jdk9则可以直接转

常量池介绍以及属性介绍

此常量池非JVM中的字符串常量池,仅仅是当前class文件中为了降低一定的空间占用和加快JVM解析class文件而使用的常量池而已

下列讨论也仅局限于字节码文件中的常量池,不会涉及JVM部分

在此先给常量池定论:为了节省空间而使用的池化技术

首先从字段入手:

image-20240705141818983

image-20240705142237700

Problem1:为什么aaa变量可以打开?而其他的不行?

解答:是因为他使用的是static和final同时修饰

所有以cp开头的都最终指向了常量池中的一个记录,例如aaa这个变量名,以及对应的aaa的类型string,具体内容如下:

image-20240705142003501

image-20240705142022100

我们再看看ConstantValue的信息:

image-20240705142358192

problem2:这个属性名索引为何存在且为什么要是constantValue?

首先回答一下为什么要有constantvalue,这是由于我们使用final修饰了,所以最终的效果就是有了一个指向constantValue的引用,也就是我们java,字节码层面实现我们final修饰不可改变的效果,如下图所示,而这个属性值为什么存在也不言而喻了

image-20240705144543867

最后回到正题:我们可以发现当String aaa和Stirng ccc都="aaa"时,他的常量值索引都指向7

image-20240705144724267

那么7的内容是什么?7的类型是string_info,而之中又指向了8?

image-20240705144925308

8才是真正的aaa

image-20240705145000278

那么问题来了?

为什么要这么设计?

关于字段中的constant_value为什么要指向string?然后string再指向其中的一个具体的utf8_info呢?

因为最终jvm会有一个string常量池,我们要保存string和utf8_info中保存的值的关系,好方便之后我们将其存储到常量池中

追问:那为什么不直接保存在对应的string中,也即我们string不存符号引用而是直接存对应的常量?

这样的话如果有其他变量也纸箱这里应该也没问题吧?

没错,但是例如string abc="abc",对应的变量名也要存储,而java中采用的变量名的引用就指向对应的utf_8的"abc"

那么abc直接指向这个string不行吗?

可以,不过这样的话,能够更节省一些空间复杂度,但是会消耗更多的时间,尤其对于变量之后运行中的处理

总结

常量池是把对应的我们会使用到的常量抽出来,无论是各个参数或者变量名类名等等,他的核心之一就是要复用

而字段在目前阶段只会对于final static修饰的,因为目前能够处理的也只有些final和static修饰的值,其他是不行的,也就是编译阶段能处理的也就这一些了

其他字段的赋值需要之后类的生命周期在各个阶段再进行赋值

方法介绍

image-20240705152339655

其中字节码部分就是对应方法的执行流程

而异常表主要是trycatch才会有的

而杂项对应的操作数栈的深度是字节码运行之中对于操作数栈使用的时候会使用到的栈的深度(与数据结构无异,而最大深度是因为在这之中便可以计算出来)

image-20240705152458362

局部变量最大槽数是这里:也就是方法中使用的局部变量,包括参数等等

image-20240705152926361

关于属性就不赘述了,意义不大

与字节码文件解剖相似的内容:

字节码文件解剖

#### 前提提要: .java文件通过java -c 生成.class文件,这部分并非是JVM需要处理的部分,JVM处理的部分是基于生成的class文件,生成的部分是由编译器来负责 一个字节码文件的主要组成部分 使用工具说明 idea的JclassLib插件 使用步骤: 运行代码(只要你更新了代码

[转帖]【JVM】字节码执行引擎

引入 class文件就是字节码文件,是由虚拟机执行的文件。也就是java语言和C & C++语言的区别就是,整个编译执行过程多了一个虚拟机这一步。这个在 类文件结构 中已经解释。上一节讲了虚拟机是如何加载一个class的,这一节就讲解虚拟机是如何执行class文件的。 运行时栈帧结构 1.定义 栈是

为什么反射慢?

反射机制就是通过字节码文件对象获取成员变量、成员方法和构造方法,然后进一步获取它们的具体信息,如名字、修饰符、类型等。 反射机制的性能较低有很多原因,这里详细总结以下4点原因: (1)JIT优化受限: JIT 编译器的优化是基于静态分析和预测的。反射是一种在运行时动态解析类型信息的机制,在编译时无法

[转帖]JVM 运行数据区深度解析

https://my.oschina.net/jiagoushi/blog/5597878 运行数据区 字节码只是一个二进制文件存放在那里。要想在 jvm 里跑起来,先得有个运行的内存环境。 也就是我们所说的 jvm 运行时数据区。 1)运行时数据区的位置 运行时数据区是 jvm 中最为重要的部分,

什么是 Java 字节码?采用字节码的好处是什么?

在 Java 中,JVM 可以理解的代码就叫做字节码(即扩展名为 .class 的文件),它不面向任何特定的处理器,只面向虚拟机。Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以, Java 程序运行时相对来说还是高效的(不过

2.2 PE结构:文件头详细解析

PE结构是`Windows`系统下最常用的可执行文件格式,理解PE文件格式不仅可以理解操作系统的加载流程,还可以更好的理解操作系统对进程和内存相关的管理知识,DOS头是PE文件开头的一个固定长度的结构体,这个结构体的大小为64字节(0x40)。DOS头包含了很多有用的信息,该信息可以让Windows操作系统使用正确的方式加载可执行文件。从DOS文件头`IMAGE_DOS_HEADER`的`e_lf

驱动开发:内核实现进程汇编与反汇编

在笔者上一篇文章`《驱动开发:内核MDL读写进程内存》`简单介绍了如何通过MDL映射的方式实现进程读写操作,本章将通过如上案例实现远程进程反汇编功能,此类功能也是ARK工具中最常见的功能之一,通常此类功能的实现分为两部分,内核部分只负责读写字节集,应用层部分则配合反汇编引擎对字节集进行解码,此处我们将运用`capstone`引擎实现这个功能。

反射快速入门

反射就是通过字节码文件获取类的成员变量、构造方法和成员方法的所有信息。 利用反射,我们可以获取成员变量的修饰符、名字、类型、取值。我们可以获取构造方法的名字、形参,并利用通过反射获取的构造方法创建对象。我们可以获取成员方法的修饰符、名字、形参、返回值、抛出的异常、注解,并运行通过反射获取的方法。 比

synchronized原理-字节码分析、对象内存结构、锁升级过程、Monitor

本文分析的问题: synchronized 字节码文件分析之 monitorenter、monitorexit 指令 为什么任何一个Java对象都可以成为一把锁? 对象的内存结构 锁升级过程 Monitor 是什么、源码查看 字节码分析 synchronized的3种使用方式 作用于实例方法,对对象

【转帖】3.JVM内存结构概述

目录 1.JVM内存结构 1.JVM内存结构 在JVM系列的第一篇文章中已经给出了JVM内存结构的简图,下面是JVM内存结构更加详细的图。 同样,JVM的内存结构可以分为上中下3层。 上层主要是类加载子系统,负责将字节码文件加载到内存中。 类加载又分为具体的三个环节,加载(loading)、链接(l