0
点赞
收藏
分享

微信扫一扫

【Java进阶营】Java技术专题-进阶分析Java类型信息(Class对象)

前提摘要

JVM编译器.class文件

image

大概分析一下:头4个字节称为魔数(Magic Number) 16进制表中为0xCAFEBABE,它的唯一作用是确定这个文件是否是一个能被虚拟机接收的Class文件,是用来标识Class文件的

第5和第6个字节是次版本号(Minor Version),第7和第8个字节是主版本号(Major Version),Java的版本号是从45开始的,JDK1.1之后的每个JDK大版本发布主版本号向上加1,高版本的JDK能向下兼容以前版本的Class文件,但不能运行以后版本的Class文件。次版本号值为0x0000,主版本号值为0x0034,也就是十进制的52,代表JDK是1.8版本。

紧接着主版本号之后两个字节的是常量池计数值(constant_pool_count),表示常量池中的项目数量,这个容量计数是从1开始的。


JVM的执行模式

上面我们有提到: JVM解释执行Java字节码文件。其实这个说法不够严谨。

目前用的比较主流的是hotspot虚拟机的JIT即时编译器/AOT编译器。

那得先搞懂解释器和编译器的概念:

解释器是一条条的解释执行源语言,比如PHP, JS。

编译器是将源代码先编译成目标代码,执行时直接在支持支持目标代码的平台上运行,这样效率快很多,比如C, C++。(这种方式兼容性和适配性不高)

对于解释执行,不经过JIT直接由解释器解释执行所有字节码,执行效率不高。 而编译执行不加筛选的将全部代码进行编译机器码不论其执行频率是否有编译价值,在程序响应时间的限制下,编译器没法采用编译耗时较高的优化技术(因为JIT的编译是首次运行或启动的时候进行的!),所以,在纯编译执行模式下的java程序执行效率C/C++也是具有较大差距的。

因此,新版本的jvm默认都是采用混合执行模式。

看下面步骤就能明白:

1、源代码经javac编译成字节码,class文件

2、程序字节码经过JIT环境变量进行判断,是否属于“热点代码”(多次调用的方法,或循环等)

3、如是,走JIT编译为具体硬件处理器(如sparc、intel)机器码

4、如否,则直接由解释器解释执行

5、操作系统及类库调用

6、硬件

简单聊聊字节码的加载流程。

  • (1)由类装载器(class loader)负责把类文件(.class文件)加载到Java虚拟机中。在此过程需要检验该类文件是否符合类文件规范。
  • (2)字节码校验器(bytecode verifier)检查该类文件的代码中是否存在着某些非法操作,例如Applet程序中写本地计算机文件系统的操作。 这一步最耗时,JVM会执行大量测试用例去校验。
  • (3)如果字节码校验器检验通过,由Java解释器负责把该类文件解释成为机器码进行执行。

而类加载器可以分为:

1. 启动类加载器(Bootstrap ClassLoader):

2. 扩展类加载器(Extendsion ClassLoader):

3. 应用程序类加载器(Application ClassLoader):

为什么要这么做?

黑客自定义一个java.lang.String类,该String类具有系统的String类一样的功能,只是在某个函数稍作修改。比如equals函数,这个函数经常使用,如果在这这个函数中,黑客加入一些“病毒代码”。并且通过自定义类加载器加入到JVM中。此时,如果没有双亲委派模型,那么JVM就可能误以为黑客自定义的java.lang.String类是系统的String类,导致“病毒代码”被执行。而有了双亲委派模型,黑客自定义的java.lang.String类永远都不会被加载进内存。因为首先是最顶端的类加载器加载系统的java.lang.String类,最终自定义的类加载器无法加载java.lang.String类。

5. JVM的运行期数据区

image

  • (1) 程序计数器:(每个线程有一个,不是线程共享的区域)
  • (2)Java虚拟机栈:(每个线程有一个,不是线程共享的区域)

在Java 虚拟机规范中,对这个区域规定了两种异常状况:

  • 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError 异常;
  • 如果虚拟机栈可以动态扩展(当前大部分的Java 虚拟机都可动态扩展,只不过Java 虚拟机规范中也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。
  • (3)本地方法栈:(每个线程有一个,不是线程共享的区域)
  • (4)Java 堆:(线程共享区域,总共1个,GC管理的区域)
  • (5)方法区:(线程共享区域,总共1个,GC管理的区域)
  • (6)方法区中包含 - 运行时常量池

  • 前面讲了Class 文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中

  • (7)方法区中包含 - Class字节码常量池

  • 运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只能在编译期产生,也就是并非预置入Class 文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern() 方法(还有包含的相关运行时候执行的方法操作(非Literal 字符串值或者Integer常量)而是静态方法的赋值操作)

举报

相关推荐

0 条评论