jvm相关
package com.xpf.jvm;
/**
- @Author: Xia
- @Date: 2020/4/27 15:10
- @Email:x2358114512@163.com
- 【上:类加载器相关】
- 什么是类加载器(Class Loader):
- ClassLoader:负责加载class文件,class文件在文件开头有特定的文件标示(cafe babe),
- 将class文件字节码内容加载到内存中,并将这些内容转换成方法区中的运行时数据结构并且ClassLoader
- 只负责class文件的加载,至于它是否可以运行,则由Execution Engine(执行引擎)决定。
- ClassLoader的种类? 2. ClassLoader的每一种如何获取?
- 一.虚拟机自带的加载器(ClassLoader)有
- 1.启动类加载器(Bootstrap):java自带的类(Object、String等类)使用此加载器,C++语言编写的
- 因为是c++编写,那么输出此类加载器时是nul值(代码验证),但是并不是代表其不存在
- 2.扩展类加载器(Extension):java后期拓展的一些类(javax开头的包,x就是extension的意思)
- 使用此加载器,Java语言编写的
- 3.应用程序类加载器(AppClassLoader)Java也叫系统类加载器,
- 加载当前应用的classpath的所有类:自己编写的类使用次加载器
- 二.用户自定义加载器
- Java.lang.ClassLoader(抽象类)的子类,用户可以定制类的加载方式
- 3.双亲委派
- 当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类的类加载器去完成,
- 因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候
- (在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。
- 如果都没有就抛出ClassNotFound。
- 采用双亲委派的一个好处是比如加载位于 rt.jar 包中的类 java.lang.Object,不管是哪个加载器加载这个类,
- 最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个 Object对象。
- 也就是我们写的类(代码)不能污染java自带的类(代码),保证了安全性。沙箱安全机制。
- 4.沙箱安全机制
- 【下:Execution Engine(执行引擎)】
- Execution Engine(执行引擎):只要加载验证通过就可以执行,执行引擎负责解释命令,提交操作系统执行。
- 【中:运行时数据区(Running Data Area)】
- 1.Native Method Stack(本地方法栈)
- 它的具体做法是Native Method Stack中登记native方法,在Execution Engine 执行时加载本地方法库。
- native是关键字
- 补充:Native Interface( 本地接口)
- 本地接口的作用是融合不同的编程语言为 Java 所用,它的初衷是融合 C/C++程序,Java 诞生的时候是 C/C++横行的
- 时候,要想立足,必须有调用 C/C++程序,于是就在内存中专门开辟了一块区域处理标记为native的代码,它的具体
- 做法是 Native Method Stack中登记 native方法,在Execution Engine 执行时加载native libraies。
- 目前该方法使用的越来越少了,除非是与硬件有关的应用,比如通过Java程序驱动打印机或者Java系统管理生产设备,
- 在企业级应用中已经比较少见。因为现在的异构领域间的通信很发达,比如可以使用 Socket通信,也可以使用Web
- Service等等,不多做介绍。
- 2.PC寄存器(类似排班值日表,程序运行的顺序)
- 每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条
- 指令的地址,也即将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略
- 不记。 这块内存区域很小,它是当前线程所执行的字节码的行号指示器,字节码解释器通过改变这个计数器的值来选取下一条需要
- 执行的字节码指令。
- 注意:(1)、如果执行的是一个Native方法,那这个计数器是空的。
- (2)、用以完成分支、循环、跳转、异常处理、线程恢复等基础功能。不会发生内存溢出(OutOfMemory=OOM)错误
- 3.Method Area (方法区):最典型的就是永久代(PermGen space,1.7)和元空间(Metaspace,1.8)。
- 供各线程共享的运行时内存区域(线程公有)。它存储了每一个类的结构信息(模板信息),
- 例如运行时常量池(Runtime Constant Pool)、静态变量、虚拟机加载的类信息、即时编译后的代码。
- 上面讲的是规范,在不同虚拟机里头实现是不一样的,最典型的就是永久代(PermGen space,1.7)和元空间(Metaspace,1.8)
- 4.Java Stack(java栈)
- 4.1 栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,
- 线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程一致,
- 是线程私有的。
- 注意: java栈空间中的包括: 8种基本类型的变量+对象的引用变量+实例方法
- 4.2 栈帧中主要保存3类数据:
- (1)本地变量(Local Variables):输入参数和输出参数以及方法内的变量;
- (2)栈操作(Operand Stack):记录出栈、入栈的操作;
- (3)栈帧数据(Frame Data):包括类文件、方法等等。
- 4.3 每个方法执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息,
- 每一个方法从调用直至执行完毕的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程。栈的大小和具体
- JVM的实现有关,通常在256K~756K之间,与等于1Mb左右。
- 4.4 注意递归的结束条件不对:java.lang.StackOverflowError(这是个错误,不是异常)
- 4.5 栈+堆+方法区的交互关系:如图收拾
- HotSpot是使用指针的方式来访问对象:
- Java堆中会存放访问类元数据(元数据:描述数据的数据,其实就是类模板数据)的地址,
- reference存储的就直接是对象的地址
- Heap(堆)
- 5.1 一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,
- 需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行。
- 5.2 堆内存分为三部分:
- (1)堆内存逻辑上分为三部分:新生+养老+永久
- (物理上永久代/元空间[方法区的实例化]不属于堆内存)
- 5.2.1 Young Generation Space (新生区,Young/New,1/3)包括:
- Eden Space、Survivor 0 Space、Survivor 1 Space[8:1:1]
- 5.2.2 Tenure generation space (养老区,Old/ Tenure,2/3)
- 5.2.3 Permanent Space (永久区,Perm)/Metaspace(元空间,Meta)
*/
class MyCode{
}
public class ClassLoaderDemo01 {
public static void main(String[] args) {
Object o = new Object();
System.out.println(o.getClass().getClassLoader()); //null值
// System.out.println(o.getClass().getClassLoader().getParent()); //报错:java.lang.NullPointerException
MyCode myCode = new MyCode(); System.out.println(myCode.getClass().getClassLoader()); System.out.println(myCode.getClass().getClassLoader().getParent()); System.out.println(myCode.getClass().getClassLoader().getParent().getParent());
// h1();
}
public static void h1(){
h1();
}
}