近期我们小组致力于重构,许多业务逻辑因“优化”都做了大幅变动,加上整个项目本身占用内存空间巨大,于是有了再次JVM性能调优的需求(想瘦身,要扛住性能基线的同时,满足Full GC等指标的健康状态,去除java进程启动时多余的内存申请)。借此机会,刚好可以学习总结一番。
1.总图:
Java虚拟机的主要3个组成部分(图中标记的1.类加载器,2.运行时数据区,3.执行引擎),后面会按1,2,3分别详述。
2.JVM虚拟机第一组成部分:类加载器
2.1.类加载过程
加载Loading:
将字节码数据从不同的数据源读取到 JVM 中,包括jar 文件,class 文件,网络数据源,并映射为class对象。
连接Linking:
(1)验证- *.class字节码文件验证符合jvm虚拟机规范,安全性考虑
(2)准备- 给类或接口的静态变量分配内存,赋默认值
(3)解析- 符号引用替换为直接引用。该阶段会把一些静态方法如main方法(符号引用),替换为指向数据所存内存的指针,该过程是类加载期间完成的所谓的静态链接。而动态链接需要运行期间完成的符号引用替换为直接引用。
初始化initialization:
对类的静态变量初始化为指定值,执行静态代码块。
即加载>连接(验证>准备>解析)>初始化>使用(参考https://www.jianshu.com/p/cc66138d72b1)
2.2 类加载器
(1)总共有三种JVM类加载器:启动类加载器BootstrapClassLoader、扩展类加载器ExtensionClassLoader、应用类加载AppClassLoader。(当然还存在一种用户自定义类加载器)
BootstrapClassLoader是嵌在JVM内核中的加载器,该加载器是用C++语言写的,主要负载加载JAVA_HOME/lib下的类库。
ExtensionClassLoader是用JAVA编写,父类加载器是Bootstrap(但打印扩展类加载器的父类加载器会为null),由sun.misc.Launcher$ExtClassLoader实现。java中系统属性java.ext.dirs指定的目录由ExtClassLoader加载器加载,如果程序中没有指定该系统属性(-Djava.ext.dirs=***/lib)那么该加载器默认加载$JAVA_HOME/lib/ext目录下的所有jar文件。
AppClassLoader是应用程序类加载器,负责加载应用程序classpath目录下的所有jar和class文件。它的父加载器为Ext ClassLoader。
(2)测试一下,未特别声明classpath,则默认设置为应用程序当前路径,获取class的加载器,class加载器的父加载器,以及更上层加载器,如下图:
2.3 双亲委派:
下图清晰的展示了双亲委派的机制以及jvm类加载器之间的关系,为防止重复加载的问题,子类加载器会先委托“上级”去找class,直到顶层“父类”未找到目标时,便交由下级去查找,直到应用类加载器。当然像Tomcat这种有能力确保应用之间隔离,是通过实现自定义类加载器的这种用法保证的。
类加载器的源码其实很简单(java部分仅实现了一个双亲委派的机制,本身的class查询,加载等是由C++代码实现的)
2.4 JVM创建到加载一个类的流程
JVM虚拟机第二组成部分:内存模型
JVM虚拟机第三组成部分:执行引擎
To Be Continued...