JVM的体系结构图
简介
- 从图上可以看出JVM启动时按照其配置要求,申请一块内存,并根据JVM规范和实现将内存划分为几个区域。class二进制文件信息将会被放入“方法区”,对象实例将会被放入“java堆”等
类加载器的作用
通过一个类的全限定名来获取描述此类的二进制字节流,并将此类相关信息加载到JVM的方法区,并创建一个 java.lang.Class 对象作为此类的访问接口, class 对象的引用也保存在方法区内,每一个类加载器都有独立的类名称空间。比较两个类是否相等的前提是两个类是由同一个类加载器加载的,否则两个类比不相等。
-
类加载器分类
从JVM角度来讲,只有两种类加载器:启动类加载器、其他的类加载器。因为前者是JVM虚拟机的一部分,后者是独立于JVM实现的 ,跟细致的分类有以下几种 - 1.启动类加载器(Bootstrap ClassLoader)
作用:启动类加载器是使用C++语言实现的(HotSpot),负责加载JVM虚拟机运行时所需的基本系统级别的类,如java.lang.String, java.lang.Object等等。启动类加载器(Bootstrap Classloader)会读取 {JRE_HOME}/lib 下的jar包(如 rt.jar)和配置,然后将这些系统类加载到方法区内。 由于类加载器是使用平台相关的底层C/C++语言实现的, 所以该加载器不能被Java代码访问到。但是,我们可以查询某个类是否被引导类加载器加载过 - 2.扩展类加载器(Extension ClassLoader)
作用:此加载器由 sun.misc.Launcher$ExtClassLoader 实现,它负责加载 {JAVA_HOME}\lib\ext 目录下的类库, 开发者可以直接获取此加载器, 拓展类加载器是是整个JVM加载器的Java代码可以访问到的类加载器的最顶端,即是超级父加载器,拓展类加载器是没有父类加载器的 - 3.应用程序类加载器(Application ClassLoader)
作用:此加载器负责加载用户类路径上指定的类库,若没有指定自定义加载器,则此加载器一般是程序中默认的加载器。应用类加载器将拓展类加载器当成自己的父类加载器。 - 4.用户自定义加载器(Customized Class Loader)
作用:用户可以自己定义类加载器来加载类。所有的类加载器都要继承 java.lang.ClassLoader 类并重写 findClass(String name) 方法。用户自定义类加载器默认父加载器是 应用程序加载器
双亲委派模型
双亲委派模型工作过程就是一个类加载器收到类加载的请求,它首先会把这个请求委派给父类加载器去完成,层层上升,只有当父类加载器无法完成此加载请求时,子加载器才会尝试自己去加载。
代码测试
public class Test {
public static void main(String args[]) {
try {
Object obj = new Object();
System.out.println("Test-Test-19->" + obj.getClass().getClassLoader()); //null
Test test=new Test();
System.out.println("Test-Test-22->"+test.getClass().getClassLoader()); //sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println("Test-Test-23->"+test.getClass().getClassLoader().getParent()); //sun.misc.Launcher$ExtClassLoader@421faab1
System.out.println("Test-Test-24->"+test.getClass().getClassLoader().getParent().getParent()); //null
StringBuffer sb=new StringBuffer();
System.out.println("Test-Test-22->"+sb.getClass().getClassLoader()); //null
} catch (Exception e) {
e.printStackTrace();
}
}
}
PC寄存器
- PC寄存器( PC register ):每个线程启动的时候,都会创建一个PC(Program Counter,程序计数器)寄存器。PC寄存器里保存有当前正在执行的JVM指令的地址。 每一个线程都有它自己的PC寄存器,也是该线程启动时创建的。保存下一条将要执行的指令地址的寄存器是 PC寄存器。PC寄存器的内容总是指向下一条将被执行指令的地址,这里的地址可以是一个本地指针,也可以是在方法区中相对应于该方法起始指令的偏移量。
- 每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,也即将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不记。
- 这块内存区域很小,它是当前线程所执行的字节码的行号指示器,字节码解释器通过改变这个计数器的值来选取下一条需要执行的字节码指令。
- 如果执行的是一个Native方法,那这个计数器是空的
方法区
- 方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
- 方法区是一种规范,不同的虚拟机里面的方法区的实现不一样,最典型的就是永久代(PerGem)和元空间(Metaspace),java7的时候叫做永久代,java8开始叫做元空间
堆和栈
- 栈管运行,堆管存储
- jvm栈也叫做栈内存,主管Java程序的运行,是在线程创建的时创建,它的生命周期是跟随线程的生命周期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收的访问,只要线程一结束改栈就会释放,生命周期和线程的生命周期同步,是线程私有的东西。
- 8中基本数据类型 + 对象的引用变量+实例方法都是在函数的栈内存中分配的
来源:CSDN
作者:多哥仁慈的骆驼
链接:https://blog.csdn.net/qq_36381640/article/details/103665397