0

    Java基础之堆、栈、方法区、类加载器——JVM内存模型分析

    2023.07.09 | admin | 125次围观

    美好祝愿

    今天是2020年的第一天,2020如约而至,是美好一年的开始jar找不到或无法加载主类,也带来了新的希望;我呢,也希望在2020能够多多赚钱,能对喜欢的人说2020,同时,把这份祝愿送给所有看到这篇文章的小伙伴们,一起加油吧。在美好的祝愿中,咱们开始今年的第一篇分享:JVM内存模型

    Java 进阶

    相信正经,或者不正经的程序员小伙伴们,亦或者非开发小伙伴,多多少少都听说过JVM(Java虚拟机),Java程序的运行支持;同时,也是高级程序员的必须掌握的底层知识,更是中高级Java程序员的分水岭;那么,让我们一起来进阶吧:

    JVM 内存模型

    首先,先来看下面这张图:

    JVM 内存模型

    看过上图,应该对jvm有了一个大概的了解,如果没看懂也不要紧,慢慢听我来一一解释图中的这些组件:

    JVM内存划分,是人为的根据不同内存空间的存储特点以及存储的数据而划分的不同逻辑内存区域,具体的区域就是上图的这些了;

    程序计数器:当前线程所执行的字节码的行号指示器。

    本地方法栈:为虚拟机使用的native方法服务,方法通常是使用C/C++编写,然后编译成.dll或者.so文件,再由JNI(Java Native Interface)调用执行。

    Java虚拟机栈:描述Java方法执行的内存模型,每个方法被执行的时候都会同时创建一个栈帧用于存储局部变量表、操作栈、动态链接、方法出口等信息;注意是每执行一个方法就创建一个栈帧,栈帧存放了当前方法的数据信息(局部变量),方法执行完毕,该方法的栈帧就会被销毁。

    Java堆:是在虚拟机启动时创建的一块内存区域,是被所有线程共享的,因为要通过其中的存储的对象调用方法和属性。所有的对象实例(直接或者间接使用new关键字创建的对象)以及数组都要在堆上分配(使用new关键字,就表示在堆中开辟一块新的存储空间)。

    方法区:线程共享的内存区域,存储已被虚拟机加载的类信息、常量、静态变量,静态代码块、即时编译器(JIT Compiler)编译后的代码数据等,这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。

    GC(Garbage Collection):垃圾回收器。

    Java的自动垃圾回收机制: 在Java中,程序员就不需要再像C/C++语言中的那样再去手动控制内存的释放,由GC自动的、不定时的去回收垃圾对象,当JVM发觉内存资源紧张的时候,就会自动地去清理无用对象(没有被引用到的对象)所占用的内存空间。

    类加载器

    类加载器是将字节码文件(.class)文件加载进JVM中运行的组件,加载过程如下:

    Java基础之堆、栈、方法区、类加载器——JVM内存模型分析

    加载:到指定或者默认的路径下查找和导入.class文件。校验和解析:

    3.初始化:对静态变量,静态代码根据其数据类型块执行初始化操作;

    一旦将类加载进JVM中,便不会再次加载了,那么是以什么来判断是不是同一个类的的依据呢,那便是类的全限定类名(就是:包名.类名),类加载工作由ClassLoader及其子类负责,ClassLoader负责查找和载入class字节码文件,JVM运行时会创建3个类加载器:BootstrapClassLoader、ExtensionClassLoader 和 AppClassLoader,如下图所示:

    类加载体系

    BootstrapClassLoader 是主加载器,也有称为根加载器的,是被嵌入到JVM中的jar找不到或无法加载主类,是用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);

    ExtensionClassLoader

    AppClassLoader是应用类加载器,使用Java编写的,父类是ExtensionClassLoader,主要负责加载Casspath下的类,默认情况下,都是使用AppClassLoader来加载开发项目中的类的。

    AppClassLoader

    除了BootstrapClassLoader ,ExtensionClassLoader ,AppClassLoader以及其他的类加载器都是ClassLoader的子类,当一个类加载器加载一个类时,除非显式的使用另一个类加载器,否则该类引入(或者依赖)的其他类也使用的是该类加载器;该加载体系并不是继承体系,而是委派体系,类加载会先从父类中寻找目标类,找不到时才会从自己本地查找;从而就引出了“双亲委派模型”:

    双亲委派模型

    双亲委派模型

    当一个类加载器接到一个类加载请求时,会先检查是否加载过,若没有它会把请求转给父类加载器的loadClass()去完成,层层皆是如此,所以所有的加载请求都会传到最顶层的类加载器中;若是父加载器不存在,会使用根加载器,也就是BootstrapClassLoader 来加载;若是父类加载器加载失败,再调用自己的findClass()方法进行加载。源码如下图所示:

    loadClass

    版权声明

    本文仅代表作者观点。
    本文系作者授权发表,未经许可,不得转载。

    发表评论