「JAVA」Java基础之堆、栈、方法区、类加载器——JVM内存模型分析
Java 进阶
相信正经,或者不正经的程序员小伙伴们,亦或者非开发小伙伴,多多少少都听说过JVM(Java虚拟机),Java程序的运行支持;同时,也是高级程序员的必须掌握的底层知识,更是中高级Java程序员的分水岭;那么,让我们一起来进阶吧:
JVM 内存模型
首先,先来看下面这张图:
程序计数器:当前线程所执行的字节码的行号指示器。
本地方法栈:为虚拟机使用的native方法服务,方法通常是使用C/C++编写,然后编译成.dll或者.so文件,再由JNI(Java Native Interface)调用执行。
Java虚拟机栈:描述Java方法执行的内存模型,每个方法被执行的时候都会同时创建一个栈帧用于存储局部变量表、操作栈、动态链接、方法出口等信息;注意是每执行一个方法就创建一个栈帧,栈帧存放了当前方法的数据信息(局部变量),方法执行完毕,该方法的栈帧就会被销毁。
Java堆:是在虚拟机启动时创建的一块内存区域,是被所有线程共享的,因为要通过其中的存储的对象调用方法和属性。所有的对象实例(直接或者间接使用new关键字创建的对象)以及数组都要在堆上分配(使用new关键字,就表示在堆中开辟一块新的存储空间)。
方法区:线程共享的内存区域,存储已被虚拟机加载的类信息、常量、静态变量,静态代码块、即时编译器(JIT Compiler)编译后的代码数据等,这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。
GC(Garbage Collection):垃圾回收器。
类加载器
1. 加载:到指定或者默认的路径下查找和导入.class文件。
2. 校验和解析:
检查加载进来的class的正确性;
给类的静态变量分配存储空间;
将符号引用转化成直接引用;
3.初始化:对静态变量,静态代码根据其数据类型块执行初始化操作;
BootstrapClassLoader 是主加载器,也有称为根加载器的,是被嵌入到JVM中的,是用C++编写的,主要负责加载JAVA_HOME/lib目录下的类库,如rt.jar、charset.jar等,该加载器无法被应用程序使用,它是由JVM调用的,java.lang.String就是由BootstrapClassLoader 加载的;
ExtensionClassLoader 是扩展类加载器,使用Java编写的,父类是BootstrapClassLoader,主要负责加载JRE扩展目录(默认情况下是JAVA_HOME/lib/ext)下的类;在Java中,系统属性的java.ext.dirs指定的目录就是ExtensionClassLoader 加载的,当然了,这个目录也是可以自定义的(通过:-Djava.ext.dirs=myExtDir);
AppClassLoader是应用类加载器,使用Java编写的,父类是ExtensionClassLoader,主要负责加载Casspath下的类,默认情况下,都是使用AppClassLoader来加载开发项目中的类的。
除了BootstrapClassLoader ,ExtensionClassLoader ,AppClassLoader以及其他的类加载器都是ClassLoader的子类,当一个类加载器加载一个类时,除非显式的使用另一个类加载器,否则该类引入(或者依赖)的其他类也使用的是该类加载器;该加载体系并不是继承体系,而是委派体系,类加载会先从父类中寻找目标类,找不到时才会从自己本地查找;从而就引出了“双亲委派模型”:
双亲委派模型
当一个类加载器接到一个类加载请求时,会先检查是否加载过,若没有它会把请求转给父类加载器的loadClass()去完成,层层皆是如此,所以所有的加载请求都会传到最顶层的类加载器中;
若是父加载器不存在,会使用根加载器,也就是BootstrapClassLoader 来加载;
若是父类加载器加载失败,再调用自己的findClass()方法进行加载。
loadClass 方法源码如下图所示:
以上的就是今天的分享了,完结。老夫虽不正经,但老夫一身的才华