JVM笔记

类加载机制

加载流程

loading:

将类文件加载到内存,在堆中生成一个Class类对象供外部调用

linking:

验证(verification):验证,确保类信息没安全方面问题

准备(preparation):赋默认值

解析(resolution):符号引用变真实地址值

initializing

执行类构造器、赋初始值、执行静态代码块

如果父类没有初始化,会先初始化父类

对于类:load + 默认值 + 初始值

对于对象:new + 申请内存 + 默认值 + 初始值

编译器

java是混合执行,即解释执行和编译执行。-Xint纯解释,-Xmixed混合执行,-Xcomp纯编译

检测热点代码-XX:CompileThreshold=1000,超过这个值的调用混合模式会编译。JIT:Just In-Time Complier

loading

类加载器分类

Bootstrap(启动类加载器,加载核心类)->Extenstion(扩展类加载器,加载扩展类)->Application(应用程序类加载器,自己写的代码)->Custom(自定义类加载器)

双亲委派机制(代理模式)

从下往上找是否被加载过,再从上往下加载。找不到同时也无法加载则报ClassNotFoundException

是有类缓存

为什么要用双亲委派?

1.为了保证核心类库的安全,避免出现用户自定义java.lang.Object这样的情况。

2.有一个缓存。

代理模式即将指定类的加载交给其他类加载器。双亲委托机制是代理模式的一种

自定义类加载器

可以对class文件加密,然后用自己的类加载器加密后加载

java.class.ClassLoader

加载指定类,找到或生成对应的字节代码。即java.lang.Class的一个实例。还可以加载一些资源文件

getParent()返回父类加载器

loadClass(String name)加载名为name的类

findClass(String name)查找名name类

findLoadedClass(String name)查找已被加载过的类

defineClass(String name,byte[] b,int off, int len)把字节数组b中的内容转换成java类,返回类实例

resolveClass连接指定的java类

自定义

继承ClassLoader,重写findClass()方法

1.首先检查请求类型是否已经装载,如果装载则返回

2.委派给父类加载器,如果父类能够找到或装载完成则返回

3.调用本类加载器的findClass方法,获取到则defineClass导入到方法区

打破双亲委派机制

重写loadClass即可打破

线程上下文类加载器

当前线程类加载器就是为了抛弃双亲委派加载链模式

ThreadContextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定。Thread.currentThead().getContextClassLoader()

热部署,热启动:tomcat先加载再查找,为了保证安全不查找核心库。

package at.guigu.study.jvm;

import sun.misc.PerfCounter;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.InvocationTargetException;

public class CustomClassLoaderTest extends ClassLoader {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        CustomClassLoaderTest loaderTest = new CustomClassLoaderTest();
        Class<?> aClass = loaderTest.loadClass("at.guigu.study.jvm.CustomClassLoaderTest");
        aClass.getMethod("hello").invoke(aClass.newInstance(), new Object[]{});
        // 这里可以看出,新加载的类,并不会影响之前的。
        loaderTest.hello();
    }

    public String hello(){
        // class文件里是输出jvm,上面运行中先输出了jvm,再输出了hello
        System.out.println("hello");
        return "hello";
    }

    @Override
    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 {
                    // 重写了loadClass方法,意味着java.lang包中类也会通过这个加载,所以要用extClassLoader加载。这里用app的父类加载器
                    Class<?> aClass = Thread.currentThread().getContextClassLoader().getParent().loadClass(name);
                    if (aClass != null) {
                        return aClass;
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

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

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        File file = new File("D:\\project\\java\\study_java\\file\\CustomClassLoaderTest.class");
        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int b = 0;
            while ((b = fileInputStream.read()) != -1) {
                byteArrayOutputStream.write(b);
            }
            byte[] bytes = byteArrayOutputStream.toByteArray();
            byteArrayOutputStream.close();
            fileInputStream.close();

            return defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return super.findClass(name);
    }
}
// class文件
package at.guigu.study.jvm;

public class CustomClassLoaderTest {
    public CustomClassLoaderTest() {
    }

    public String hello() {
        System.out.println("jvm");
        return "hello";
    }
}

linking

verification验证是否符合jvm规范和安全

perparation静态成员变量赋默认值

resolution

将类、方法、属性等符号引用解析为直接引用

常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用

你可能感兴趣的:(#,java,jvm,java,开发语言)