JVM包含哪几个部分?

  • 类加载子系统
  • 运行时数据区(程序计数器、虚拟机栈、本地方法栈、堆、方法区)
  • 执行引擎


类加载子系统

类的加载过程

加载 -> 连接(验证、准备、解析) -> 初始化
  • 加载:{
            1、将类转化为字节流,并将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;
            2、在堆中生成一个代表该类的class对象,作为方法区这个类的各种数据的入口;
}
  • 验证:保证class文件的字节流中包含的信息是否符合当前虚拟机的要求,包含:文件格式验证、元数据验证、字节码验证、符号引用验证;
  • 准备:{
            1、为类变量(只包括静态变量、实例变量会在对象实例化的时候分配在java堆中)分配内存并设置类变量初始值置零的阶段;
            2、增添final之后会将其赋值为所给的数(完成了类构造器<clinit>方法之中);
}
  • 解析:将常量池内的符号引用替换为直接引用的过程。主要针对(接口、字段、类方法、接口方法、方法类型、方法句柄、调用限定符);
               (系统需要明确知道这个方法所在的位置。Java 虚拟机为每个类都准备了一张方法表来存放类中所有的方法。当需要调用一个类的方法的时候,只要知道这个方法在方法表中的偏移量就可以直接调用该方法了。通过解析操作符号引用就可以直接转变为目标方法在类中方法表的位置,从而使得方法可以被调用。)
  • 初始化:执行类构造器<clinit>()方法;


类加载器

四种类加载器:

类加载器之间的父子关系一般不会以继承的关系来实现,而是使用组合关系来复用父加载器的代码
  1. Bootstrap ClassLoader 启动类加载器;
  2. Extention ClassLoader 标准拓展类加载器;
  3. Application ClassLoader 应用类加载器;
  4. User ClassLoader 用户自定义类加载器。

双亲委派加载机制:

  • 保证了java程序的稳定运行,可以避免类的重复加载(JVM区分不同类的方式不仅仅根据类型,相同的类文件被不同的类加载器加载产生的是两个不同的类);
  • 保证了java的核心API不被篡改


破坏双亲委派加载机制:

  • SPI机制:获取 META/INF 路径下的所有实现类,这些类通过第三方提供的,根据双亲委派原则,由JDK内部加载的 Class 默认应该归属于 BootStrap 类加载器;但是原生SPI机制通过ServiceLoader.load方法由外部指定类加载器,或者默认使用ThreadContextClassLoader()线程上下文加载器,从而避免了Class 被载入BootStrap加载器,导致破坏了双亲委派加载机制;
  • Tomcat容器:会优先加载Web应用自己定义的类,通过WebAPPClassLoader加载本身目录下的class文件,加载不到再给CommonClassLoader加载。为了实现不同应用依赖不同版本使用同一个类名的问题,需要加载多个相同的、不同版本的类;
  • 参考:https://github.com/HSshuo/SPI


Java内存区域/运行时数据区

大体分为:堆、栈、本地方法栈、方法区、程序计数器

  • 线程私有:程序计数器、虚拟机栈、本地方法栈;
  • 线程共享:堆、方法区(元空间)

介绍:

  • 程序计数器:是一块内存区域、用来记录线程当前要执行的指令地址。确保线程切换后能恢复到正确的执行位置;
  • 虚拟机栈:包含局部变量表(基本数据类型+对象的引用)、方法出口信息操作数栈、动态连接;为虚拟机执行java方法(字节码)服务;
  • 本地方法栈:使用本地方法会在本地方法栈也创建栈帧;为虚拟机使用到的native方法服务;
  • 堆:
  1. 字符串常量池(原本在方法区的运行时常量池中);
  2. 存放对象实例;并不是绝对的,通过逃逸分析,如果某些方法中的对象引用没有被返回或者被外面使用,对象可以直接在栈上分配内存;
  3. Java堆是垃圾收集器管理的主要区域;(引申垃圾回收算法、常见的垃圾回收器、参考链接:https://blog.nowcoder.net/n/a16d0a274890498891aee25aea4ba3a7
  • 方法区:运行时常量池,用于存储以被虚拟机加载的类信息(类的版本、字段、方法、接口等信息)、静态变量等数据;

方法区的具体实现:
  1. 方法区也被称为永久代,是HotSpot虚拟机对虚拟机规范中方法区的一种实现方式;
  2. 元空间(替代永久代):使用直接内存;通过NIO类使用native函数库直接分配堆外内存,通过存储在java堆中的DirectByteBuffer对象对这个内存进行引用操作;

元空间、永久代可以被回收嘛:可以回收
  1. 当该类的实例都被回收
  2. 加载该类的ClassLoader已经被回收
  3. 该类不能通过反射访问到其他方法、同时该类没有被引用

思维脑图: