这个其实也很正常,其实很多程序员在工作中用到JVM性能调优的情况很少,可以说大部分情况用不到,但用不掉是不是意味着可以不用学习了呢?个人认为还是很有必要学习的,学习JVM其实就像你学习古诗词一样,你说学习古诗词有什么用?至少能提高你的审美以及文化水平吧,那么了解JVM,起码能让你更加了解底层代码的运行原理,提高对技术的认知水平,而且你在简历上加上了解JVM性能调优,那么你的简历就跟别人区分开来了。
以下是我综合整理的JVM基础知识,希望看完能让你对JVM基础知识有更深的理解。JVM如何加载class
加载阶段(Loading)、
连接阶段(Linking)、
初始化阶段(Initializing)
加载阶段(Loading)
加载阶段主要做3件事情:- 通过类全限定名获取定义此类的二进制字节流;
- 将字节流代表的静态存储结构转换为方法区的运行时数据结构;
- 在内存中生成此类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
连接阶段(Linking)
Linking的过程分为三小步:- Verification是校验装进来的class文件是不是符合class文件的标准,假如你装进来的不是这个CAFEBABE,在这个步骤就被拒绝掉了。
- Preparation是把class文件静态变量赋默认值,不是赋初始值,比如你statici=8,注意这个步骤8并不是把i值赋成8,而是先赋0。
- Resolution是把class文件常量池里面用到的符号引用转换为直接内存地址,可以直接访问到的内容。
初始化阶段(Initializing)
Initlalizing意思是把静态变量赋初始值。双亲委派机制
双亲委派是一个孩子向父亲方向,然后父亲向孩子方向的双亲委派过程。
一个class文件需要被load内存的时候是这样的任何一个class,假如你定义了ClassLoader,这时候就先尝试去自定义的ClassLoader里面找,他内部维护着缓存,先问你有没有帮我把这个class加载进来了?如果加载进来了就不需要加载第二遍了,如果没有加载进来,那就赶紧把我加载进来。他如果在自定义的ClassLoader缓存中没有找到的话,他并不是直接加载这个类,他会先去他的父亲Application父加载器,说爸爸你有没有把这个类加载进来了呀?这个时候Application也会去他的缓存里面找有没有这个类,如果有则返回,如果没有则委托给他的父亲Bootstrap,同理Bootstrap也会去他的缓存中找看有没有这个类,有则返回,没有则继续委托给他的父亲Extendsion去加载,这时候Extendsion说我只负责加载扩展jar包里的类,你的类我找不到,麻烦Application去加载,Application又说我只是负责加载classpath路径下的类,其他的我也找不到,然后再委托给自定义的ClassLoader去加载。整个过程经过了一圈转了一圈,才真正把这个类加载进来,当我们能够把这个类加载进来的时候叫做成功,如果加载不进来,抛出异常ClassNotfoundException。这就叫双亲委派。CPU的内存结构以及JavaMemoryModel
CPU计算核心与内存之间有三级缓存,缓存离CPU计算核心越远容量越大、速度越慢,反之则容量越小、速度越快。CPU计算核心与内存之间存在着三级缓存,那么这个时候就会带来数据一致性的问题,如果在单线程的环境数据一致性是没有问题的,但是在多线程环境中就很难保证数据的一致性了。为了解决这个问题,CPU引入了一个数据一致性的协议,这个协议叫MESI。关于MESI协议更详细的描述,大家可以查看这篇文章【并发编程】MESI--CPU缓存一致性协议Java的内存模型(JavaMemoryModel)其实就是仿照CPU的内存结构来设计和定义的。JMM是一种规范,目的是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。Java内存模型中规定了所有的变量都存储在主内存中,每个线程有自己的工作内存(类比缓存理解),线程的工作内存中保存了该线程使用到主内存中的变量拷贝,线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同线程之间无法直接访问对方工作内存中的变量,线程间变量值的传递(通信)均需要在主内存来完成。Java对象的内存布局
在HotSpot虚拟机中,对象在内存中的存储布局分为三块区域:对象头(Header)实例数据(InstanceData)对齐填充(Padding)一个java对象在内存中占多少个字节呢?- 64位系统(未开启指针压缩):MarkWord占用8个字节+ClassPointer占用8个字节=16个字节(16已经是8的整数倍,所以不需要对齐填充)
对象头的大小:16个字节- 64位系统(开启指针压缩):MarkWord占用8个字节+ClassPointer占用4个字节+对齐填充4个字节=16个字节(空对象,所以实例数据大小为0)
对象头的大小:12个字节以上就是关于JVM的部分基础知识代码实验室的创建已经有一段时间了,建立本号的初衷是为了做技术的分享,通过系统的整理JVM的基础知识让大家更加了解Java底层的运行逻辑,要知其然,更要知其所以然。欢迎订阅我的微信公众号【Seven的代码实验】订阅更多关于JVM、Spring、以及开源项目的分享,希望能让你有所收获。