Java虚拟机概述
了解过java的人大多听说过它的"Write Once,Run Anywhere"口号,java强大的跨平台性让它成为计算机界的中流砥柱。这么强大的跨平台性很大程度上要归功于java虚拟机,它可以将javac编译器编译的class文件在不同平台上解释出来。并且随着Java虚拟机发展到JDK1.7~1.8时,JVM做到了很大程度上的语言无关性,包括Kotlin、Scala、Clojure、Groovy、Jython等语言都可以在JVM上运行。
Class文件概述
Class文件是一种特殊的二进制文件,其中包含了Java虚拟机指令集、符号表和其他若干辅助信息。它起到的是承前启后的作用,程序语言被编译器编译成Class文件,而这个Class文件可以在任意安装了Java虚拟机的平台被解释成可执行文件然后运行。值得注意的是,Java虚拟机是不与包括java在内的程序语言绑定的,它只和Class文件这种特定的二进制文件格式相关联。
Class文件结构
Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格的按照顺序紧凑排列在Class文件中,中间没有任何分隔符。
整个class文件是由无符号数和表这两种数据类型组成的,本质上可视为一张表。
无符号数属于基本的数据类型,以u1,u2,u4,u8分别代表1、2、4、8个字节的无符号数。
表是由多个无符号数或者其他表作为数据项构成的复合数据类型,习惯性以"info“结尾。
该图参考自《深入java虚拟机》
它由上表中的数据项构成,由于class文件没有分隔符号,因此上表中的数据项,无论是顺序还是数量以及数据存储的字节序,都是被严格限定的,再说一次,这些数据项的顺序数量和数据存储的字节序都是被严格限定的。
接下来我们来对表中的每一数据项进行详细介绍:
(1) magic(魔数):
它是Class文件的头四个字节,是确定该文件是否为class文件的标准。像gif文件的魔数是GIF8,jpeg文件的魔数是0xFFD8FF,class文件的文件头是0xCAFEBABE。如果魔数不是0xCAFEBABE,那么JVM是无法识别的。
(2) minor_version(次版本号)
该数据项占2个字节,是class文件的次版本号。不同版本的javac编译器所编译的class文件版本不同,而jvm可以识别比他版本低的class文件,不能识别比他版本高的class文件。
(3) major_version(主版本号)
该数据项占2个字节,是class文件的主版本号。
(4) constant_pool_version(常量池容量计数值)
该数据项占两个字节,是class的常量池容量计数值。常量池顾名思义就是存放常量的,它是class文件的资源仓库,是Class文件中第一个出项的表类型数据项目。这个容量计数值处于特殊考虑从1开始计数,因此常量的实际数量是该计数值-1.
之后的几项数据项将会在后续的系列文章中详解。
实例讲解
我们用一个简单的Hello.class文件讲解:
Hello.java
package com.ssh.demo;
public class Hello {
public void sayHello(){
System.out.println("hello");
}
public static void main(String[] args){
Hello h = new Hello();
h.sayHello();
}
}
接着我们用winhex打开编译后的Hello.class文件
我们开始对照之前所述的几个数据项是否正确:
首先先介绍下,我们的每个字节都由16进制来表示,每个字节范围是00H~FFH。我们可以看到头四个字节正是CA FE BA BE,表示该文件是一个class文件。第5个-第8个字节表示的是版本号,上图是00 00 00 34,转换为十进制后我们可以知道该class文件版本是52.0。第九个和第十个字节是 00 27,换算为十进制是39,这表示该class文件常量池中共有39-1项常量(该计数值从1开始计数,故减去1).
这个章节里面我们简单介绍了jvm和class文件的作用以及class文件的几个数据项,接下来的一个章节我们将具体介绍class文件的常量池。