JVM笔记(二)类加载系统、运行时数据区、执行引擎详解

目录

1.  类加载系统

1.1 类生命周期

1.2 类加载时机

1.3 类加载器

1.3.1 ClassLoader#loadClass

1.3.2 自定义类加载器

1.3.3 java类热加载原理/实现

2. JVM运行时数据区模型

2.1 .Class文件

2.2 程序计数器

2.3 虚拟机栈

2.3.1 局部变量表

2.3.2 操作数栈

2.3.3 动态链接

2.3.4 方法返回地址

2.4 堆

2.4.1 分配过程简述

2.4.2 大小设置

2.5 方法区

2.5.1 永久代(PermGen)和元空间(MetaSpace)

2.5.2 大小设置

2.6 一些面试题

2.6.1 栈和堆的区别

2.6.2 为什么要分新老代

2.6.3 新生代为什么需要2个Survivor

3. 执行引擎

3.1 解释器存在的必要

3.2 JIT即时编译器

3.2.1 热点代码

3.2.2 逃逸分析

3.2.3 逃逸分析测试


图来自腾讯课堂

java文件被编译为字节码文件,由类加载系统将字节码加载到JVM内存,执行引擎负责执行class文件中包含的字节码指令,运行时数据区即程序运行时的内存区域,其又分堆栈方法区PC寄存器,有的区域随虚拟机启动而存在,有的区域依赖用户线程的启动和结束而建立和销毁。

1.  类加载系统

1.1 类生命周期

指一个class从被加载到卸载的全过程。

1)加载,加载一个.class文件到jvm,实例化一个class对象的过程

链接,加载后对class的验证和初始化工作,一般分为三个部分,验证、准备、解析

2)验证:验证字节码文件格式是否符合jvm规范、变量方法是否重复、继承是否合标准,保证jvm可以正确运行。

3)准备:为静态变量分配内存设置初始值,比如int,long等初值设置为0

4)解析:把常量池中的符号引用转换为直接引用,根据符号引用所作的描述,在内存中找到符合描述的目标并把目标指针指针返回。

5)初始化,执行静态变量赋值和静态代码块,从父类往下执行

6)使用,被动引用和主动引用

被动引用:

  • 引用父类的静态字段,只会引起父类的初始化,而不会引起子类的初始化。
  • 定义类数组,不会引起类的初始化。
  • 引用类的常量,不会引起类的初始化,已经放到常量池了

主动引用:1.2 类加载时机

7)卸载,满足下面的情况,类就会被卸载:

  • 该类所有的实例都已经被回收
  • 加载该类的ClassLoader已经被回收
  • 该类对应的java.lang.Class对象没有任何地方被引用

1.2 类加载时机

在运行过程中遇到如下字节码指令时,如果类尚未加载,那就要进行加载:new、getstatic、putstatic、invokestatic。这几个指令对应的场景:

1)通过new创建对象;

2)读取、设置一个类的静态成员变量(不包括final修饰的静态变量,final修饰已在编译阶段把结果放入常量池);

3)调用一个类的静态方法。

4)执行main,初始化主类

还有Class.forName(“”)时触发加载;

1.3 类加载器

java中的类加载器ClassLoader分为四种

  1. 启动类加载器(BootStrap ClassLoader),程序访问不到,C++实现,是最顶层的加载器,用来加载jre/lib目录下的jar包
  2. 扩展类加载器(ExtClassLoader),用来加载jre/lib/ext目录下的jar包
  3. 应用类加载器(AppClassLoader),用来加载classpath中指定的jar包及目录中class,父类加载器为ExtClassLoader
  4. 自定义类加载器,前三种是jvm预定义的类加载器,我们可以自定义classLoader来加载我们指定地方的class文件

类加载机制:全盘负责、双亲委派

全盘负责,就是当一个类加载器负责加载某个Class时,该Class所依赖和引用其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。

双亲委派,试图加载某个class时,首先查询缓存是否之前已加载过,加载过则直接返回class。若没有加载过,则先交给父类加载器加载class,直到顶层的BootStrap ClassLoader,若父类没有加载到class,再调用本加载器的loadClass方法记载class。

1.3.1 ClassLoader#loadClass

重要的三个方法

1)loadClass加载类的主方法

2)findLoadedClass查看本classLoader是否已经加载过这个类

3)defineClass 将字节码文件解析为class对象

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        // 从缓存查询
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                    // 如果存在父类加载器,交给父类加载器加载class
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    // 没有父类加载器,则调用顶层bootstrapclassLoader加载,native方法
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
            // 如果父类没加载到class
            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                // 调用本类的加载方法加载class
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }

        return c;
    }

protected Class<?> findClass(final String name)
    throws ClassNotFoundException
{
    final Class<?> result;
    try {
        result = AccessController.doPrivileged(
            new PrivilegedExceptionAction<Class<?>>() {
                public Class<?> run() throws ClassNotFoundException {
                    // 从完整包路径,加载文件,在调用defineClass将二进制字节码文件解析为class对象
                    String path = name.replace('.', '/').concat(".class");
                    Resource res = ucp.getResource(path, false);
                    if (res != null) {
                        try {
                            return defineClass(name, res);
                        } catch (IOException e) {
                            throw new ClassNotFoundException(name, e);
                        }
                    } else {
                        return null;
                    }
                }
            }, acc);
    } catch (java.security.PrivilegedActionException pae) {
        throw (ClassNotFoundException) pae.getException();
    }
    if (result == null) {
        throw new ClassNotFoundException(name);
    }
    return result;
}

一个类被加载到JVM中,用其类加载器和全限定名作为这个类在jvm中的唯一标识,不同的加载器实例加载的相同class文件,互不兼容(cast强转报错),下节代码示例。

1.3.2 自定义类加载器

测试不同的加载器加载的类与原类不兼容

public class TestClassLoader extends ClassLoader{
    String classpath = "D:/00000000000000/target/test-classes/";
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            name = name.replace(".", "//");
            String path = classpath + name + ".class";
            File file = new File(path);
            FileInputStream is = new FileInputStream(file);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int b = 0;
            while ((b = is.read()) != -1) {
                baos.write(b);
            }
            is.close();
            byte[] bytes = baos.toByteArray();
            return this.defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
public class TestObject {
    public void testPrint() {
        System.out.println("ddd");
    }

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {

        System.out.println("默认加载器:" + TestObject.class.getClassLoader().toString());

        TestClassLoader loader = new TestClassLoader();
        Class<?> testObject = loader.findClass("TestObject");
        Object o1 = testObject.newInstance();

        System.out.println("自定义加载器加载的对象:" + o1.getClass().toString());
        System.out.println("自定义加载器加载的对象classLoader:" + o1.getClass().getClassLoader().toString());

        // 强转报错,因为加载器不同,两个Class也是不同的,不能强转
        TestObject cast = (TestObject) o1;
        cast.testPrint();
    }
}

 

但是如果改改代码,为TestObject加个接口,强转为接口类型则可以。


                
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值