JAVA虚拟机JVM粗浅理解

JVM的概念

JVM(Java Virtual Machine)顾名思义就是java虚拟机,他是在不同计算机平台中构建出来的一个虚拟计算机来实现各种功能,所以他有个很重要的特点就是平台的无关性。只要你所在的系统无论是linux,windows亦或是macOs,只要你装了jvm就可以运行java程序。举个不恰当的例子,比亚迪是汽车车厂,格力是空调厂,但是他们都改装了口罩生产线,都可以生产口罩,jvm就相当于口罩生产线,生产口罩就是运行java程序,生产场地、配置、资质相当于你计算机的配置,工厂规模的大小类似于服务器的配置能够决定口罩的产量。

结构组成

JVM主要由三个子系统构成,分别为类加载子系统运行时数据区执行引擎

类加载子系统

我们在创建一个java类的时候会产生一个.java的文件,通过javac编译成.class的字节码文件,这个过程就是将给我们看的文件转换成给机器看的文件,只有.class文件才能被加载到。类加载子系统包含了三个步骤(加载,连接,初始化)。下图是一个类完整的生命周期,前三个步骤是加载步骤。

加载

加载的意思就是讲.calss文件加载到内存

连接

连接阶段是由三部分组成,分别是验证、准备和解析

验证阶段是验证字节码文件的准确性,是否符合java类文件的固定格式,语意是否符合jvm规范,字节码是否可以被jvm安全的执行。

准备阶段是类的静态变量分配内存,然后赋予默认值,值得一提的是静态常量则是在这一步就赋值。如下图所示,age作为静态变量最初讲赋予0的默认值,性别作为静态常量将在这一步赋值male。

解析

解析阶段,jvm会把类的二级制数据中的符号引用替换为直接引用。这是什么意思呢?我们看到User类里有个play的方法,ClassRoom类里有一个User类的变量,并且有个userPlay方法引用了User里的play方法,在字节码文件中这只是一个描述性的符号引用。我们知道创建User类的时候会在方法区开辟一个内存存放play的方法并且对应一个内存地址,通过解析ClassRoom在引用User类里的方法时会直接通过一个指针指向play的内存地址。符号引用就像是你想要去一个地方只是一个愿望,直接引用就是将你的愿望转化成了一张机票带你去实现愿望。

类加载器

加载一个类需要类加载器,一个类只会被加载一次,加载器通过路径来加载相应的类,如果访问不到该类,jvm怎么抛出classNotFound的异常。理解类加载机制对后序一些设计模式的理解,源码分析都有帮助。JVM预定义有三种类加载器:根类加载器(bootstrap class loader)、扩展类加载器(extensions class loader)、系统类加载器(system class loader)。用户也可以自己定义加载器。

根类加载器(bootstrap class loader)

用来加载 Java 的核心类(JAVA_HOME/jre/lib/rt.jar下的所有类),开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。

扩展类加载器(extensions class loader)

用来加载JAVA_HOME/jre/lib/ext文件夹下的所有类或者由系统变量-Djava.ext.dir指定位置中的类,开发者可以直接用它来加载自己导入的jar包

系统类加载器(system class loader)

用来加载用户自定义的类。

如上图所示,User类,Map类,JsonObject分别使用了不同的加载器加载。

类加载机制

双亲委派:先让父类加载器试图加载该Class,只有父加载器无法完成此加载任务时,才自己去加载,这样做的目的是为了防止重复加载。以下是加载类方法的源码:

 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先,查看这个类之前有没有加载到内存,如果有直接返回
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {

                    //先检查是否有父类加载器,如果有则用父类加载器加载
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                //如果父类加载器无法加载,则自己加载
                if (c == null) {                    
                    long t1 = System.nanoTime();
                    c = findClass(name);
                    
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

下图是四种加载器的关系图

全盘负责:全盘负责是指当一个ClassLoader加载一个类时,除非显示地使用另一个ClassLoader,则该类所依赖与引用的类也由这个ClassLoader加载。

运行时数据区(内存结构)

运行时数据区分为五部分:方法区、堆、JAVA虚拟机栈、本地方法栈和程序计数器。其中方法区和堆是共享线程,JAVA虚拟机栈、本地方法栈和程序计数器为私有线程。共享线程是所有线程在运行时可以访问这一部分内存,比如A线程和B线程都可以在堆内存创建一个实例对象,并且可以调用方法区的方法,这一部分数据的生命周期并不会随着线程的终止而终止,他是和垃圾收集机制(GC)有关。私有线程的数据和线程的生命周期一致。

方法区

类的所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在这里定义。简单来说,所有定义的方法的 信息都保存在该区域,静态变量+常量+类信息(构造方法/接口定义)+运行时常量池都存在方法区中。

堆是用来存放对象实例的,也是占内存最大的一块区域。如果对象过大过多会出现内存溢出的情况,他也是GC最主要的管理区域。它分为新生代、老年代和元空间(jdk1.8以后没有持久代)。

GC机制

首先在伊甸区存在四个活着的对象abcd,其他区域均为空

此时在堆内存中又创建了ef两个对象,此时伊甸区内存占满,这是发生第一次minor GC,通过算法判断a对象已经消亡,则将他回收,bcdef放入from区,并且清空伊甸区

对内存又创建了g h对象,并且又把伊甸区内存占满,则进行上一步骤,如果此时from内存也满了,则将from区消亡对象清空(bcd),并将存活对象复制到to区 ,然后清空伊甸区和from区,并且将复制到to区的对象计数+1,from和to互换

当minor GC到达一定次数 from区对象(efgh)的标记次数到达15次(默认),这部分存活对象将被放入老年代

当老年代内存满了会进行一次major GC,清空老年代,进行major GC是会造成线程停顿较长时间,所以major GC的次数尽量避免,这也是JVM调优的重要指标

未完待续

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值