JVM调优 知识体系学习 一

学习方法3问

是什么?有什么?能干什么?

什么是调优?

首先学习JVM等知识是需要有目标的,而不是盲目不带问题去学习,只有带着问题去学习,才能有收获,那么什么是JVM调优呢?

其实jvm调优,调的是稳定,并不能带给你性能的大幅提升,所谓调优就是调的保证服务的稳定,服务稳定的重要性就不用多说了。那么影响服务的稳定gc永远会是需要考虑的不稳定因素之一。复杂和高并发下的服务,必须保证每次gc不会出现性能下降,各种性能指标不会出现波动,gc回收规律而且干净,找到合适的jvm设置,保证服务的稳定,这就是调优。

JVM描述

  • 所谓虚拟机是指 通过软件模拟的具有完整硬件系统功能的,运行在一个完全隔离环境中的计算机系统。
  • JVM是通过软件来模拟java字节码的指令集,是java程序的运行环境.

JVM主要功能

  • 通过ClassLoader寻找和装载class文件
  • 解释字节码成为指令并执行,提供class文件的运行环境
  • 进行运行期间的内存分配和垃圾回收
  • 提供与硬件交换的平台

Java跨平台图

在这里插入图片描述

类加载

在这里插入图片描述

概述

  • 加载 查找并加载类文件的二进制数据
  • 连接 就是将已经读入内存的类的二进制数据合并到JVM运行环境中去,包含如下几个步骤:
    1.验证 确保被加载类的正确性
    2.准备 为类的 静态变量 分配内存,并初始化它们
    3.解析 把常量池中的符号引用转换成直接引用
  • 初始化 为类的静态变量赋初始值

类加要载完成的功能

  • 通过类的全限定名来获取该类的二进制字节流
  • 把二进制字节流转化为方法区的运行时数据结构
  • 在堆上创建一个java.lang.Class对象,用来封装类在方法区内的数据结构,并向外提供了访问方法区内数据结构的接口

类加载方式

  • 最常见的方式: 本地文件系统中加载、从jar等归档文件中加载
  • 动态的方式 : 将java源文件动态编译成class
  • 其他方式:网络下载、从专有数据库中加载等等

类加载器

java虚拟机自带的加载器包括如下几种

  • 启动类加载器(BootstrapClassLoader)
  • 平台类加载器(PlatformClassLoader) [JDK8:扩展类加载器 Extension ClassLoader]
  • 应用程序类加载器(AppClassLoader)
  • 用户自定义的加载器,是java.lang.ClassLoader的子类,用户可以定制类的加载方式;只不过自定义类加载器其加载的顺序是在所有系统类加载器的最后

类加载器的关系

在这里插入图片描述

  • 启动类加载器:用于加载启动的基础模块类,比如java.base、java.managment、java.xml等等。
  • 平台类加载器:用于加载一些平台相关的模块,比如java.scripting、java.compiler*、java.corba*等等。
  • 应用程序类加载器:用于加载应用级别的模块,比如jdk.compiler、jdk.jartool、jdk.jshell等等;还加载classpath路径中的所有类库。
  • JDK8启动类加载器: 负责将<JAVA_HOME>/lib,或者 -Xbootclasspath参数指定的路径中的,且是虚拟机识别的类库加载到内存中(按照名字识别,比如rt.jar,对于不能识别的文件不予加载)。
  • JDK8扩展类加载器:负责加载<JRE_HOME>/lib/ext,或者java.ext.dirs系统变量所指定路径中的所有类库。
  • JDK8应用程序类加载器:负责加载classpath路径中的所有类库。
  • java程序不能直接引用启动类加载器,直接设置classLoader为null,默认就使用启动类加载器。
  • 类加载器并不需要等到某个类 “首次主动使用” 的时候才加载它,JVM规范允许类加载器在预料到某个类将要被使用的时候它就预先加载它。
  • 如果在加载的时候.class文件缺失,会在该类首次主动使用时报告LinkageError错误,如果一直没有被使用,就不会报错。

加载器小例子

public class ClassLoaderStudy {
    public static void main(String[] args) throws ClassNotFoundException {
        // java程序不能直接引用启动类加载器,直接设置classLoader为null,默认就使用启动类加载器
        String str = "str ClassLoader";
        System.out.println("str Class Loader = "+str.getClass().getClassLoader());

        ClassLoaderStudy study = new ClassLoaderStudy();
        System.out.println("study class loader = "+study.getClass().getClassLoader());

        System.out.println("study class loader Parent = "+study.getClass().getClassLoader().getParent());
    }
}

运行结果
在这里插入图片描述

双亲委派模型

  • JVM中的ClassLoader通常采用双亲委派模型,要求除了启动类加载器外,其余的类加载器都应该有自己的父级加载器。这里的父子关系是组合而不是继承,工作过程如下:
    1.一个类加载器接收到类加载请求后,首先搜索它的内建加载器定义的所有“具名模块”。
    2.如果找到了合适的模块定义,将会使用该加载器来加载。
    3.如果Class没有在这些加载器定义的具名模块中找到,那么将会委托给父级加载器,直到启动类加载器。
    4.如果父级加载器反馈它不能完成加载请求,比如在它的搜索路径下找不到这个类,那子的类加载器才能自己来加载。
    5.在类路径下找到的类将成为这些加载器的无名模块
    其实就是只有当父加载器在自己搜索范围内找不到特定的类时(即ClassNotFoundException), 子加载器才会尝试自己去加载。
双亲委派模型描述
  • 双亲委派模型对于保证java程序的稳定运作很重要。
  • 实现双亲委派的代码在java.lang.ClassLoader的loadClass()方法中,如果自定义类加载器的话,推荐覆盖实现findClass()方式。
  • 如果有一个类加载器能加载某个类,称为 定义类加载器,所有能成功返回该类的Class的类加载器都被称为 初始类加载器.
  • 如果没有指定父加载器,默认就是启动加载器。
  • 每个类加载器都有自己的命名空间,命名空间由该加载器及其所有父加载器所加载的类构成,不同的命名空间,可以出现类的全路径名 相同的情况。
  • 运行时包由同一个类加载器的类构成,觉定两个类是否属于同一个运行时包,不仅要看全路径名是否一样,还要看定义类加载器是否相同。只有属于同一个运行时包的类才能实现相互包内可见。

破坏双亲委派模型

  • 双亲模型有个问题:父加载器无法向下识别子加载器加载的资源。为了解决这个问题,引入了线程上下文类加载器,可以通过Thread的setContextClassLoader()进行设置。另外一种典型情况就是实现热替代,比如OSGI的模块化热部署,它的类加载器就不再是严格按照双亲委派模型,很多可能就在平级的类加载器中执行了。

类连接主要验证的内容

  • 类文件结构检测:按照JVM规范规定的类文件结构进行。
  • 元数据验证:对字节码描述的信息进行语义分析,保证其符合java语言规范要求。
  • 字节码验证:通过对数据流和控制流进行分析,确保程序语义是合法和符合逻辑的,这里主要对方法体进行效验。
  • 符合引用验证:对类自身以外的信息,也就是常量池中的各种符合引用,进行匹配效验。

类连接中的解析

  • 所谓解析就是把常量池中的符合引用转换成直接引用的过程,包括 符合引用 以一组无歧义的符号来描述所引用的目标,与虚拟机的实现无关。
  • 直接引用:直接指向目标的指针、相对偏移量、或是能间接定位到目标的句柄,是和虚拟机实现相关的。主要针对:类、接口、字段、类方法、接口方法、方法类型、方法句柄、调用点限定符。

类的初始化

  • 类的初始化就是为类的静态变量赋初始值,或者说是执行类构造器 < clinit > 方法的过程。
  • 如果类还没有加载和连接,就先加载和连接。
  • 如果类存在父类,且父类没有初始化,就先初始化父类。
  • 如果类中存在初始化语句,就依次执行这些初始化语句。
  • 如果是接口的话:
    a.初始化一个类的时候,并不会先初始化它实现的接口。
    b.初始化一个接口时,并不会初始化它的父接口。
    c.只有当程序首次使用接口里面的变量或者是调用接口方法的时候,才会导致接口初始化。
  • 调用Classloader类的loadClass方法来装载一个类,并不会初始化这个类,不是对类的主动使用。

类的初始化时机

  • Java程序对类的使用方式分成 主动使用和被动使用,JVM必须在每个类或接口“首次主动使用”时才初始化它们;被动使用类不会导致类的初始化,主动使用的情况:
    1.创建类实例
    2.访问某个类或接口的静态变量
    3.调用类的静态方法
    4.反射某个类
    5.初始化某个类的子类,而父类还没有初始化
    6.JVM启动的时候运行的主类
    7.定义了default方法的接口,当接口实现类初始化时。

例子根据上面学习请回答a和b输出是什么?

public class ClassATest {
    private static ClassATest classATest = new ClassATest();
    private static int a = 0;
    private static int b;
    private ClassATest(){
        a++;
        b++;
    }
    public static ClassATest getInstance(){
        return classATest;
    }
    public int getA(){
        return a;
    }
    public int getB(){
        return b;
    }
}

public class ClassLoaderStudy {
    public static void main(String[] args) throws ClassNotFoundException {
       ClassATest classATest = ClassATest.getInstance();
        System.out.println("classATest.a = "+classATest.getA());
        System.out.println("classATest.b = "+classATest.getB());
    }
}

运行结果,这里需要想想a为什么是0,如果没想出来在看一遍上面的类加载顺序或自行百度类加载顺序
在这里插入图片描述

类的卸载

  • 当代表一个类的Class对象不再被引用,那么Class对象的生命周期就结束了,对应的在方法区中的数据也会被卸载。
  • JVM自带的类加载器装载的类,是不会卸载的,由用户自定义的类加载器加载的类是可以卸载的。

JVM调优 知识体系学习 二

下一章开始学习java内存分配

https://blog.csdn.net/baidu_37313657/article/details/107683190
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值