JVM 虚拟机&&类加载(一)

时间:2020-04-13 10:04:38   收藏:0   阅读:70

虚拟机

虚拟机简介

Java 虚拟机(JVM)是运行java程序的抽象计算机,它是计算机设备的规范,可以采用不同方式进行实现,java 程序通过运行在JVM中实现跨平台,一次编译到处运行,不同的操作系统有不同的JDK版本,通过调用JNI方法去实现调用不同操作系统的方法

graph LR A[java源文件] B[class二进制字节码] C[JVM] D((Java native接口)) F[windows本地方法] H[linux本地方法] I[macOS本地方法] A-.编译.->B B-.加载.->C C-.打开文件.->D D-.windows-JDK.->F D-.linux-JDK.->H D-.macOS-JDK.->I

Java虚拟机不和包括java的语言绑定,它只和Class文件这种特定的二进制文件格式绑定,class文件中包含了虚拟机指令集和符号表以及若干其他辅信息。

graph LR A[java程序] B[JRuby程序] C[Groovy程序] D[java编译器] E[JRuby编译器] F[Groovy编译器] H[字节码] I[Java虚拟机] A-.*.java文件.->D B-.*.rb文件.->E C-.*.groovy文件.->F D-->H E-->H F-->H H-.*.class文件.->I

虚拟机产品

  1. Sun HotSpot(JVM)
  2. BEA JRocket
  3. IBM J9
  4. Microsoft JVM
  5. Google Android Dalvik

Class 文件

Class文件是以8位字节基础单位的二进制流,各项数据严格按照顺序排列在class文件中,中间没有任何分隔符,如果超过8位,是按照高位在前的方式存储的。

Class文件的组成

  1. java虚拟机指令集
  2. 符号表
  3. 其他辅助信息

常量池

java代码在编译后,Class文件并表不会保存各个方法,字段在内存里面的最终布局,因为在编译期间并不知道引用类的实际地址,因此只能使用符号描述来代替,当虚拟机真正的运行的时候,需要从class文件的常量池获得对应的符号引用,然后在类的创建或者运行时解析称具体的内存地址

也就是说在类被加载的时候,一定是在内存中分配了具体的地址,这样在解析的时候就可以替换符号引用成真正的地址。

Class文件的常量池主要存放2大类型:

虚拟机类加载

类加载到JVM中生命空间

graph LR A[加载Loading] B[验证Verification] C[准备Preparation] D[解析Resolution] E[初始化Initialization] F[使用Using] G[卸载Unloading] A-->B D-->E E-->F F-->G subgraph 连接 B-->C C-->D end

加载

加载的过程做的事情是:

Java中获取二进制字节流的途径有

验证

验证是连接的第一步,要确保Class文件中的字节流包含的信息符合JVM的要求,验证阶段大体分为如下几个阶段

准备

准备阶段是为了给类分配内存并设置类变量的初始化Clint,这些变量使用的内存,都在方法区中进行分配,只是对类变量进行内存分配(Static 修饰的)不包括实例变量,实例变量是随着类实例化后一起在堆中分配的

:类的初始化 父静态变量 父类静态块 子类静态变量 子类的静态块

: 对象的初始化 父类的变量初始化,父类的构造函数。。。。。。

解析

解析的目的是将常量池中的符号引用替换为直接引用

字段的解析

class A extends B implements C{
    private String str; //字段的解析
}
graph TB A{A是否能匹配} B{B是否能匹配} C{C是否能匹配} A-->|是|E[结束] A-->|否|C subgraph 接口 C-->|是|F(结束) end subgraph 父类 C-->|否|B B-->|是|I(结束) B-->|否|J((抛出异常)) end

类方法解析

接口方法解析

类加载器

通过类的全限定名来获取描述类的二进制字节流,把类加载这个阶段中的动作放到虚拟机外部去实现,以便应用程序能够自己决定如果去获取自己需要的类,实现这个动作加做类加载器。
Java中有abstract class ClassLoader 类。
ClassLoader.loadClass类的核心总结是:

加载器类型

从JVM的角度去看 只存在2中类加载器,一种是启动类加载器(Bootstrap ClassLoader)是由C++语言实现的。还有一种是另外的类加载器,是由java语言实现的,独立于JVM,全部是继承了java.lang.ClassLoader

如果从我们开发角度去看,分为3中类加载器:

//Launcher.class文件中
static class ExtClassLoader extends URLClassLoader {
        private static volatile Launcher.ExtClassLoader instance;

        public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
            if (instance == null) {
                Class var0 = Launcher.ExtClassLoader.class;
                synchronized(Launcher.ExtClassLoader.class) {
                    if (instance == null) {
                        instance = createExtClassLoader();
                    }
                }
            }

            return instance;
        }
     ....        
}
 //java/lang/ClassLoader.java 文件中
 public static ClassLoader getSystemClassLoader() {
        initSystemClassLoader();
        if (scl == null) {
            return null;
        }
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkClassLoaderPermission(scl, Reflection.getCallerClass());
        }
        return scl;
    }
graph BT A[Bootstrap加载器] B[ExtClassLoader] C[AppClassLoader] D[UseClassLoader1] F[UseClassLoader2] B --> A subgraph 非JVM C --> B D --> C F --> C end

有兴趣的可以自己执行看下

String appClassLoaderPath = System.getProperty("java.class.path");
System.out.println(appClassLoaderPath);
String extClassLoaderPath = System.getProperty("java.ext.dirs");
System.out.println(extClassLoaderPath);
String bootClassLoaderPath = System.getProperty("sun.boot.class.path");
System.out.println(bootClassLoaderPath);

双亲委派

什么是双亲委派

如何打断

自己写个类加载器 并重写loadClass方法 就可以了!

为什么要使用双亲委派

原文:https://www.cnblogs.com/burg-xun/p/12689079.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!