详解JVM

23 阅读3分钟

初识jvm

jvm是什么?

jvm(java虚拟机),主要是解释和运行java字节码文件。

jvm的功能

  1. 解释和运行:将字节码指令解释成机器码供计算机执行。
  2. 即时编译:提前编译热点代码提高代码执行效率,使得java程序的性能可以接近c,c++程序。
  3. 内存管理:jvm可以在java程序运行过程中自动进行内存分配和垃圾回收。

jvm的组成

image.png

类加载器

字节码文件的组成

image.png

  1. 基本信息

image.png 对于以上基本信息中的版本号,jdk的版本号=主版本号-44

  1. 字段

image.png 3. 方法

image.png 4. 属性

image.png 5. 常量池

image.png

如何查看字节码文件

  • 使用javap -v
  • jclasslib
  • arths

类的生命周期

image.png

  • 加载:字节码文件的加载可以是本地,网络或动态生生成,将加载后的字节码信息保存在方法区中,生成一个InstanceKlass对象,保存类的所有信息,同时会在堆中生成类似的一个对象

image.png

  • 链接

1.验证:是否符合jvm规范 2.准备:给静态变量赋值

image.png 3.解析:将符号引用转为直接引用

  • 初始化
  • 使用
  • 卸载

示例代码:


public class Test {

    public static void main(String[] args) {
        System.out.println("A");
        new Test();
        new Test();
    }
    //每次创建对象时执行
    public Test(){
        System.out.println("B");
    }
    //实例代码块,每次创建对象时执行
    {
        System.out.println("C");
    }
    //类加载时执行
    static {
        System.out.println("D");
    }

}
结果:DACBCB

public class Test {
    public static void main(String[] args) {
        new Child(); //如果去掉这行,只访问父类的静态变量不会导致子类初始化,因此结果为1
        System.out.println(Child.a);
    }
}
class Child extends Par1 {
    static {
        a=2;
    }
}
class Par1{
    
    public static int a=0;
    
    static {
        a=1;
    }
}
//执行顺序,(1)new Child()首先父类加载并初始化,a=0,a=1;(2)子类加载a=2并初始化,(3)结果:2

类加载器的分类

启动类加载器

默认加载jre/lib下的类。通过将类放到jre/lib目录下或者使用(-Xbootclasspath/a:jar包目录/jar包名)可以让启动类来加载目标类。

扩展类加载器

默认加载jre/lib/ext下的类。同样可以通过将类放到/jre/lib/ext目录下或者使用(-Djava.ext.dirs=jar包目录)进行扩展。

应用程序类加载器

默认加载classpath下的类。

对于扩展类加载器和应用程序类加载器位于sun.misc.Launcher中,是一个静态内部类。继承自URLClassLoader。具备通过目录或者指定jar包将字节码文件加载到内存中。

image.png

自定义类加载器

打破双亲委派机制的方法

自定义类加载器重写loadClass方法

例如tomact通过自定义类加载器实现应用隔离,不同的应用之间使用不同的类加载器去加载应用类,对于系统类,不同的容器内的系统类是同一个,并不会被重复加载。

使用线程上下文类加载器

SPI机制示例:


public interface DriverManager {

    String getDataBaseUrl();

    String getConnection();

    String getDriverClassName();

    void closeConnection();
}

public class MysqlDriverManager implements DriverManager{
    @Override
    public String getDataBaseUrl() {
        System.out.println("MysqlDriverManager getDataBaseUrl");
        return "jdbc:mysql://localhost:3306/test";
    }

    @Override
    public String getConnection() {
        System.out.println("MysqlDriverManager getConnection");
        return "com.mysql.jdbc.Driver";
    }

    @Override
    public String getDriverClassName() {
        System.out.println("MysqlDriverManager getDriverClassName");
        return "com.mysql.cj.jdbc.Driver";
    }

    @Override
    public void closeConnection() {
        System.out.println("Connection closed");
    }
}


public class PostgreSqlDriver implements DriverManager{
    @Override
    public String getDataBaseUrl() {
        System.out.println("PostgreSqlDriver getDataBaseUrl");
        return "jdbc:postgresql://localhost:5432/test";
    }

    @Override
    public String getConnection() {
        System.out.println("PostgreSqlDriver getConnection");
        return "org.postgresql.Driver";
    }

    @Override
    public String getDriverClassName() {
        System.out.println("PostgreSqlDriver getDriverClassName");
        return "org.postgresql.Driver";
    }

    @Override
    public void closeConnection() {
        System.out.println("Connection closed");
    }
}

//这里就会将在META-INF/com.guolihong.javaLearn.spi.DriverManager 中配置的实现加载。

public class Test {
    public static void main(String[] args) {
        ServiceLoader<DriverManager> load = ServiceLoader.load(DriverManager.class);

        load.forEach(driverManager -> {
            System.out.println("------------------");
            System.out.println(driverManager.getConnection());
            System.out.println(driverManager.getDriverClassName());
            System.out.println(driverManager.getDataBaseUrl());
            driverManager.closeConnection();
        });
    }
}

在META-INF/com.guolihong.javaLearn.spi.DriverManager

com.guolihong.javaLearn.spi.MysqlDriverManager
com.guolihong.javaLearn.spi.PostgreSqlDriver
osgi框架实现的类加载器