JVM那点事

本文深入探讨了Java虚拟机(JVM)的工作原理,包括两种编译方式、热点代码检测、JIT优化如逃逸分析,以及类加载的全过程,如双亲委派模型。还详细介绍了类加载的各个阶段,如验证、准备、解析和初始化。此外,阐述了运行时数据区的结构变化,如方法区和运行时常量池的调整,并对比了类常量池、运行时常量池和字符串常量池的差异。
摘要由CSDN通过智能技术生成

jvm 架构图


java 编译有两种方式
1.动态编译 动态解释执行 、 动态编译执行
2.静态编译 静态解释执行
概念:
热点代码:1. 被多次调用的方法。
2. 被多次执行的循环体
热点检测方式:1 基于采样的热点探测
2 基于计数器的热点探测
Server Compiler和Client Compiler两个编译器的编译过程是不一样的

对Client Compiler来说,它是一个简单快速的编译器,主要关注点在于局部优化,而放弃许多耗时
较长的全局优化手段。
而Server Compiler则是专门面向服务器端的,并为服务端的性能配置特别调整过的编译器,是一个充 分优化过的高级编译器。

JIT优化:1.公共子表达式的消除
变量的替换
2.方法内联
方法内部调用其他方法
3.逃逸分析(最常用)
1)逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所 引用,例如作为调用参数传递到其他地方中,称为方法逃逸

-XX:+DoEscapeAnalysis : 表示开启逃逸分析
-XX:-DoEscapeAnalysis : 表示关闭逃逸分析

2.Class 文件结构
在这里插入图片描述
查看class 文件版本号 javap -v class文件名

class文件中常量池中结构
在这里插入图片描述

int 和 float 类型在class文件中常量池的存储

在这里插入图片描述
在这地方里插入图片描述
double 和float 类似不截图了。。。

String 类型的常量池存储如下:
在这里插入图片描述
总结:
1. 对于某个类或接口而言,其自身、父类和继承或实现的接口的信息会被直接组装成
CONSTANT_Class_info常量池项放置到常量池中;
2. 类中或接口中使用到了其他的类,只有在类中实际使用到了该类时,该类的信息才会在常量池中有
对应的CONSTANT_Class_info常量池项;
3. 类中或接口中仅仅定义某种类型的变量,JDK只会将变量的类型描述信息以UTF-8字符串组成
CONSTANT_Utf8_info常量池项放置到常量池中,上面在类中的private Date date;JDK编译器
只会将表示date的数据类型的“Ljava/util/Date”字符串放置到常量池中。

类加载:
在这里插入图片描述
将本地class 文件或者网络、jsp文件编译后的class 文件转成Class对象的过程
加载 -> 链接 -> 初始化
分为 链接 包括 验证 ------ 准备 ------ 解析
通过类的全限定名,获取二进制流文件(class文件)加载后将二进制字节流存在方法区

验证过程
1.加载开始前,⼆进制字节流还没进⽅法区,⽽加载完成后,⼆进制字节流已经存⼊⽅法区
2.⽽在⽂件格式验证前,⼆进制字节流尚未进⼊⽅法区,⽂件格式验证通过之后才进⼊⽅法区
也就是说,加载开始后,⽴即启动了⽂件格式验证,本阶段验证通过后,⼆进制字节流被转换成特定数
据结构存储⾄⽅法区中,继⽽开始下阶段的验证和创建Class对象等操作

验证包含 格式、元数据、字节码 验证

准备环节:仅仅为类变量(即static修饰的字段变量)分配内存并且设置该类变量的初始值即零值,这⾥不包
含⽤final修饰的static,因为final在编译的时候就会分配了(编译器的优化),同时这⾥也不会为
实例变量分配初始化。类变量(静态变量)会分配在⽅法区中,⽽实例变量是会随着对象⼀起分配
到Java堆中

初始化环境:-调⽤⽅法
其实初始化过程就是调⽤类初始化⽅法的过程,完成对static修饰的类变量的⼿动赋值还有主动调⽤静态
代码块。

类加载器
双亲委派模型:子类交给父类完成加载任务。
采⽤双亲委派的⼀个好处是:
⽐如加载位于rt.jar包中的类java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶 层的启动类加载器进⾏加载,这样就保证了使⽤不同的类加载器最终得到的都是同样⼀个Object对 象
破坏双亲委派模型:父类交给子类完成加载任务

3.总结 class⽂件介绍

  1. class⽂件都存储了哪些内容?有图有真相
  2. class常量池中都是如何存储数据的?cp_info、tag、 1. 字⾯量类型(int、float、long、double、utf8)
  3. 引⽤类型(String、Class类型)
  4. class符号引⽤(#16)和直接引⽤(内存地址)
  5. class特殊字符串(类型&字段描述符、⽅法描述符)
  6. 哪些字⾯量会进⼊到class常量池中?(final修饰的8种基本类型和String类型、⾮final修饰 double、float、long、双引号字符串)
    类加载详解
  7. 类加载的时机(new Student()\调⽤static⽅法或者给static变量赋值)
  8. 类加载的过程(⻅图)
  9. 加载( class字节码—> Class对象 )
  10. 验证(Class对象是否合法)
  11. 准备(为静态变量初始化为默认值)
  12. 解析(符号引⽤–>直接引⽤)
  13. 初始化(为静态变量⼿动赋值)
  14. 类加载器介绍(bootstrap类加载器、扩展类加载器、应⽤程序类加载器、⾃定义类加载器)
  15. 双亲委派模型(⼦类加载器会委派⽗类加载器去完成类的加载)
  16. 破坏双亲委派模型(⽗类加载器委派⼦类加载器去完成类的加载)

运行时数据区
在这里插入图片描述
jdk1.8后将 方法区中的运行时常量池移到了 堆内存中

//这个创建了2个对象 一个new 的堆new出来的对象。一个是字符串对象 “1”
String s3 = new String(“1”);

    //看这个字符串对象 1是否在常量池存在存在直接返回,不存在  将指向堆内存的引用放入stringtable,并将“1” 添加到 string pool
    s3.intern();

    //相当于  String s4=“12”; 最后 但是堆里面 此时有 4个对象 2个new的  2 个字符串对象  1 和 2 。但此时 1 和2 字符串对象  没有到 string -pool
    String s4 = new String("1")+new String("2");

    //将 "12" 字符串对象  添加到字符串常量池中。
    s4.intern();

class常量池(静态常量池)、运⾏时常量池、字符串常量池区别:
class常量池中存储的是符号引⽤,⽽运⾏时常量池存储的是被解析之后的直接引⽤。
class常量池存在于class⽂件中,运⾏时常量池和字符串常量池是存在于JVM内存中。
运⾏时常量池具有动态性,java运⾏期间也可能将新的常量放⼊池中(String#intern())
字符串常量池逻辑上属于运⾏时常量池的⼀部分,但是它和运⾏时常量池的区别在于,字符串常量 池是全局唯⼀的,⽽运⾏时常量池是每个类⼀个。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值