您的位置 首页 java

「JVM虚拟机」系列——深入理解JVM-Class中常量池

一、概念

1、 jvm 生命周期

启动 :当启动一个 java 程序时,一个jvm实例就诞生了,任何一个拥有 main 方法的class都可以作为 JVM 实例运行的起点。

运行 :main()函数作为程序初始线程起点,其它线程由该线程启动,包括守护线程(daemon)和non-daemon(普通线程)。守护线程是JVM自己使用的线程比如GC线程就是个守护线程,只要这个jvm实例还有普通线程执行,就不会停止,但是可以用exit()强制终止程序。

消亡 :所有非守护线程退出时,JVM实例结束生命,若安全管理器允许,程序也可以使用java.lang.Runtime类或者System.exit(0)来退出。实际上exit也是用到Runtime类来退出,Runtime是个神奇的类,它还可以用于启动和关闭非java进程。

2、JVM与Class文件

我们一直说 java虚拟机 实现的与 语言是无关的 ,java虚拟机不和包含 Java 在内的任何语言绑定,它只和与 Class文件 这种特殊的 二进制 文件格式所关联,class文件中包含了java虚拟机 指令集 符号表 以及若干其他辅助信息。基于安全方面的考虑, Java 虑拟机规范要求在 Class 文件中使用许多强制性的语法和结构化约束 ,但任一门功能性语言都可以表示为一个能被 Java 虚拟机 所接受的有效的 Class 文件。作为一个通用的、机器无关的执行平台,任何其他语言的实现者都可以将 Java 虚拟机作为语言的产品交付媒介。例如,使用 Java 编译器 可以把 Java 代码编译为存储字节码的 Class 文件,使用 JRuby 等其他语言的编译器同样可以把程序代码编译成 Class 文件,虚拟机并不关心Class 的来源是何种语言,如图。

3、什么是Class文件

Java字节码类文件(.class)是Java编译器编译Java源文件(.java)产生的“目标文件”。它是一种8位字节的二进制流文件, 各个 数据项 按顺序紧密地从前向后排列, 相邻的项之间没有间隙, 这样可以使得 class文件 非常紧凑, 体积轻巧, 可以被JVM快速地加载至内存, 并且占据较少的内存空间(方便于网络的传输)。

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

class文件中的信息是一项一项排列的, 每项数据都有它的固定长度, 有的占一个字节, 有的占两个字节, 还有的占四个字节或8个字节, 数据项的不同长度分别用u1, u2, u4, u8表示, 分别表示一种数据项在class文件中占据一个字节, 两个字节, 4个字节和8个字节。

4、什么是魔数

当我们把class文件转成16进制,我们可以看到文件的头四个字节是 cafe babe ,这个就称为 魔数 。,它唯一作用就告诉虚拟机当前的文件就是class文件。
使用魔数而不是用扩展名来进行识别主要是基于安全考虑,因为扩展名我们可以随意通过重命名等方式改动。而通过魔数就算你把结尾改成.clss。但它同样还能在JVM运行,因为它的头部还是 cafe babe 没变。
很多文件存储标准中都用魔数进行身份标识,如图片gif,jpeg都在文件头部中存储着魔数。

5、jvm常量池

我先讲下概念,接下来我会将class文件转为16进制流后,再举例说明。

常量池中每一项常量都是一个表, JDK 1.8有14种结构不同的表结构,这14个表有个共同特点,就是表开始的第一位都是一个u1类型的标志位,JVM根据这个标志位[tag]来确定某个常量池项表示什么类型的字面量,比如tag为1就是指CONSTANT_utf8_info

再看常量池类型表

这14种常量项结构还有一个特点是, 其中13表占用的字节固定,只有CONSTANT_Utf8_info占用字节不固定 ,其大小由length决定。为什么呢?因为从常量池存放的内容可知,其存放的是字面量和符号引用,最终这些内容都会是一个字符串,
这些字符串的大小是在编写程序时才确定,比如你定义一个类,类名可以取长取短,所以在没编译前,无法确定大小不固定,编译后,通过utf-8编码,就可以知道其长度。

再看每一项常量表对应的说明 :


二、16进制class文件解析

先看java代码

 package com.jincou.demo.domain;
public class XiaoXiao {
     private  String father;
    public String fatherName() {
        return "小小她爹";
    }
}

  

通过命令自动生成class文件(会在同一目录生成)

  javac  XiaoXiao.java
  

在将class文件拖入文本编辑器里,显示自然就是16进制流了,如下:

上面的表其实可以划分为以下七个部分,.class 字节码 文件包括:

  • 魔数与class文件版本
  • 常量池
  • 访问标志
  • 类索引、父类索引、接口索引
  • 字段表集合
  • 方法表集合
  • 属性表集合

这篇博客只讲到常量池,其它的下篇讲,接下来我们一行一行解释,首先是:

cafe babe :上面说过了这个是魔数,告诉JVM虚拟机我就是class文件。

0000 0034 :次版本号组成u2+主版本号u2。共占4个字节。0034转10进制为52,代表当前JDK版本为1.8。

0013 :说明有19-1即18个常量。

上面这些位置是固定的。接下来就是说明每一个常量:

0a :这就是tag代表一个标志,0a代表10,去找常量池列表。

得知它是一个接口中方法的符号引用,然后去找 CONSTANT_Methodref_info 对应常量列表描述:

从常量列表我们可以知道该类型一共占了5u,即 0a00 0400 0f ,那么下一个tag就是 08 代表字符串类型常量,以此类推就可以知道一共18个常量的信息。

三、class反编译

通过上面看16进制的确太麻烦了,现在我们可以通过JDK自带反编译工具查看会更加清晰。

 javap -verbose 文件名
  

通过 反编译 看去就很直观,比如第一个字符常量很明显告诉你是 CONSTANT_Methodref_info ,而且对于的就是4和15和上面完美对应。

最后思考,到底哪些会放到常量池?

 1.常量池可以理解为class文件中的资源仓库,有很多种类型,主要存放两大常量
①.字面量 
字面量就是通俗理解的java常量,如文本字符串,8大基本数据类型,final修饰的常量值等
②.符号引用
符号引用属于 编译原理 的概念,主要包含以下三种
1)类和接口的全限定名
2)字段的名称和描述符
3)方法的名称和描述符  

文章来源:智云一二三科技

文章标题:「JVM虚拟机」系列——深入理解JVM-Class中常量池

文章地址:https://www.zhihuclub.com/200317.shtml

关于作者: 智云科技

热门文章

网站地图