Java之class常量池与类加载机制

Java之class常量池与类加载机制

Java和JVM

为什么要学习JVM?

1.生产环境调优时可以大展身手
2.搞懂JVM有助于写出好代码
3.面试重灾区-体现你价值的时候到了

JVM是什么

计算机只识别0和1。

Java是⾼级语⾔。⾼级语⾔编写的程序要想被计算机执⾏,需要变成⼆进制形
式的本地机器码。能直接变成机器码的语义是C++,它的缺点是不同操作系统,
需要准备多份。Java需要先变成Java字节码(class⽂件)。然后再变成机器
码。

JVM可以实现Java的⼀次编译,到处运⾏。
这个就是区别于类似于C语⾔的⽅式。

OracleJdk和OpenJdk的区别

Oralcle:不开源-需要钱(部分企业级) 一般互联网公司都是用的这款
OpenJDK:开源 可看源码 C++--免费

JVM架构

整个JVM从架构上可以分为三层以及大体的核心功能
1.类加载系统(.class文件通过类加载技术加载到JVM内存中去 可以理解为静态->动态)
2.运行时数据区(java程序员最关心的部分也是最核心的部分)
3.执行引擎(将我们写的java代码经过解释器 编译器等变成计算机能识别的指令从而调度我们的线程开始工作)

接下来进入今天的主题

class常量池

.class文件都存放了些什么文件?

1.魔数 前四位为 CA FE BA BE 简称咖啡宝贝
2.副版本号
3.版本号
4.常量池计数器(重点) 静态常量池-一个Class对应一个
5.访问标志
6.父类索引
....

存放了描述该类的一切信息

jclasslib插件
这个插件idea自带的 可以直接安装
在这里插入图片描述

class常量池如何存储数据?

分为字面两和符号引用
1.字面量  String str = "java"
2.符号引用 User user = new User()

常量池如何组织的?

1.cp_info(常量池项 每一个常量)
2.constant_pool_count:常量池计算器


这可能大家会懵不要急 先混个眼熟

int数据类型常量如何在class常量池存储?

int:CONSTANT_Integer_info{
    //u1代表1个无符号字节 tag值=3 JVM规定的tag
    u1 tag=3;
    //u4代表4个无符号字节  
    u4 bytes;
}

在这里插入图片描述

long和bouble的数据类型常量如何在class常量池存储?

CONSTANT_Double_info{
    u1 tag=6;
    u4 high_bytes;
    u4 low_bytes;
}

在这里插入图片描述

String类型数据类型常量如何在class常量池存储?(面试重点)

#只表示双引号引起来的值的类型
CONSTANT_String_info{
    u1 tag=8;
    u2 string_index;
}

#真正的值是存储到CONSTANT_Utf8_info
CONSTANT_Utf8_info{
    u1 tag=1;
    u2 length;
    u1 byte[length];
}

#解释:
CONSTANT_String_info中使用两个无字符字节来表示指向某个CONSTANT_Utf8_info结构体。
CONSTANT_Utf8_info才是真正存放数据的结构体

哪些字面量会进入常量池?

加了final关键字的一定会加入
只有[double、float、long]类型的值不加final才会进入常量池
字符串""一定会进入常量池

类加载机制

类加载的时机

1.访问静态成员变量的时候
2.使用java.lang.reflect包对类进行反射调用时 Class c = Class.forname("com.xxx.User")
3.初始化一个类的时候发现其父类还没加载会先加载其父类
4.需要运行一个main主类

类加载的过程

加载->验证->准备->解析->初始化->使用->卸载

加载:从class文件->Class对象的过程

验证:很重要但是不是必要的  可以使用参数关闭 -Xverify:none关闭 在开发阶段测试阶段可以使其缩短类类加载的时间 格式检查、语义检查、字节码检查、符号引用检查...

准备(面试):为静态成员变量分配并初始化0值 除了boolean-false long-0L
1.public static int x =1000
2.public static final int x = 1000
结论:前者在准备阶段=0 后者才是1000

解析:将符号引用替换成直接引用的过程 从上往下搜索

初始化:
1.clinit方法 该类的静态成员变量和静态代码块被执行
注意:虚拟机会保证在多线程环境中一个类的clinit方法被正确地加锁。 静态内部类实现单例的一种方法

类和数组加载的区别

非数组类:是由类加载器来完成的
数组类:是通过java虚拟机直接创建的

***类加载器*** 重中之重

顺序往下

启动类加载器 Bootstrap ClassLoader
	%JAVA_HOME/lib下的jar包
	C++实现的
	需要被虚拟机认可 如 rt.jar

扩展类加载器 Extension ClassLoader
	%JAVA_HOME/lib/ext下的jar包

应用类加载器 Appliacation ClassLoader
	用户classpath指定的类 加载我们写的类

自定义类加载器 UserClassLoader

双亲委派机制

流程:A类(加载请求)->向上委托->自定义加载器->应用类加载器->扩展类加载器->引导类加载器->开始加载->往下回传谁能加载到就返回Class

为什么要这个机制?

小王刚入职 编写了一个Stirng类 并且使用了自己定义的类加载器加载 那么jdk自带的String还可以使用吗?jdk需要确保一些核心的基础类有且仅会被一个类加载器加载,比如String类。

细心的朋友已经发现在使用动态代理的时候需要传入一个类加载器的参数。

在这里插入图片描述
破坏双亲委派模式
第一次破坏

在jdk1.2之前还没有双亲委派机制,不过已经有了ClassLoader抽象类因此有人继承这个抽象类重写loadClass方法来实现用户的自定义类加载器

第二次破坏

描述:java核心类库定义了DriverManager  Mysql厂家实现了该驱动  那么当加载时候由于DriverManager需要被核心类库Boostrap类加载器加载  但是此时需要加载到Mysql厂家实现的类 此时就破坏了双亲委派。自上而下加载确保了java基础核心类统一加载。典型的api被用户代码调用,但是也存在被api调用用户代码的情况。

 解决:main启动时会设置线程上下文类加载器-实际上就是APP类加载器
 
这里需要有SPI的知识基础
java-SPI
Spring-SPI
Dubbo-SPI

第三次破坏

随着需求的不断发展 每次重启非常消耗时间 于是有了热部署

OSGI
OSGI的类加载器既可以在Bundle内部按照JVM的类加载机制去加载,本身也会按照Bundle之间横向委托的方式进行类加载,所以它是一种网状结构的类加载方式。因此也破坏了双亲委派
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值