JVM性能优化(一)
一、类的加载机制
- 装载(load)
通过类的全限定名,获取定义此类的二进制字节流,将字节流代表的静态存储结构转化方法区运行时数据结构,并在堆内存中生成一个代表整个类的java.lang.Class。 - 链接
验证:保证加载类的正确性。
准备:为类的静态变量分配内存,并初始化为默认值。
解析:将类的符号引用转化为直接引用。 - 初始化
对类的静态变量,静态代码块进行真正初始化。
二、类的生命周期
三、关于java.lang.Class
Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。Class类对象就是封装了一个类的类型信息,可以通过该对象操作其对应的类,即反射机制。
四、双亲委派机制
类的加载器在收到类的加载请求时,首先不会自己加载这个类,而是将请求委托给父类去加载。在父类无法加载时才会,自己加载这个类。
检查某个类是否已经加载,顺序是自底向上
加载类的,顺序是自上而下。
1、Java类的加载器
- Bootstrap ClassLoader:启动类加载器,负责加载Java核心类,JAVA_HOME中 jre/lib/rt.jar中所有class。
- Extension ClassLoader:扩展加载器,负责加载扩展功能jar,JAVA_HOME中 jre/lib中的所有jar。
- App ClassLoader: 应用类加载器,负责加载classpath中的所有类和jar。
- Custom ClassLoader 自定义加载器,通过java.lang.ClassLoader子类,自定义加载class。
类加载器图解
2、 如何破坏双亲委派原则
- 自定义类加载器,重写loadClass方法;
- 使用线程上下文类加载器;
线程上下文类加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时还未设置,他将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器。
五、运行时数据区
1、内存模型图解
2、内存模型理解
方法区
- 线程共享,在JVM启动时创建
- 存储类信息(版本、字段、方法、接口)、静态变量、常量、即时编译器编译后的代码等数据
- 是堆内存的一个逻辑部分,非堆
- OMM异常
- 运行时常量池(Run-Time Constant Pool):存储class文件的编译时期产生字面量和符号引用
堆
- 线程共享,在JVM启动时创建
- 存储对象的实例,数组
虚拟机栈
- 线程独享,存储线程中方法的调用状态,一个线程对应一个虚拟机栈
- 每一个方法对应该栈中的一个栈帧
- 调用方法,就会向虚拟中压入一个栈帧,执行完一个方法就会从栈中弹出该栈帧
程序计数器
- 线程独享,用于保存线程中方法的执行状态
- 执行Java方法,计数器保存的是该方法对应的虚拟机字节码指令的地址
- 执行Native方法,计数器为空
本地方法栈
- 线程独享
- 保存的是本地方法的执行状态
3、栈和栈帧的理解
每个栈帧,对应一个方法,理解为这个栈帧的运行空间。
栈帧:局部变量表、操作数栈、动态链接、方法返回地址
- 局部变量表:方法的局部变量、参数。局部变量表的变量不可直接使用,需要压至操作数栈中使用
- 操作数栈:压栈 出栈方式操作数
- 动态链接:指向运行时常量池中的该栈帧所属方法的引用
- 方法返回地址:退出方法的两种方式,一遇到返回指令,二遇到异常,且在该方法中未处理。
4、结合内存看对象的创建过程
堆内存模型
- 新对象申请内存空间
- 判断Eden区空间是否充足,充足,直接创建对象。
- Eden区空间不足,触发MinorGC,再次判断Eden区空间是否充足,充足,直接创建对象。
- Eden区空间仍然不足,判断Survivor空间是否充足,充足,复制部分Eden区存活对象至Survivor区,然后创建对象。
- Survivor空间不充足,则判断Old区空间是否充足,充足,复制部分Survivor区存活对象至Old区,然后创建对象。
- Old区空间不充足,则触发一次MajorGC,伴随着触发一次MinorGC,等同于触发一次FullGC。
- 若空间仍不足则抛出OOM异常。