美文网首页jvm
JVM 原理探索

JVM 原理探索

作者: 晏子小七 | 来源:发表于2020-12-02 11:23 被阅读0次

    一.类加载机制

    1.类的加载:类的加载指的是将类的.class 文件中的二进制数据读取到内存中,并放在方法去内,然后堆区创建一个 java.lang.Class对象,用来封装在方法去内的数据结构。类加载的最终产品就是Class 对象,向程序员提供了访问方法的数据结构接口。
    2.类的生命周期:类加载的过程包括了加载、验证、准备、解析(运行时绑定,就是说可能在初始化之后解析)、初始化五个阶段。
    3.类的加载三种方式:
    • 命令行启动应用时候由JVM初始化加载
    • 通过Class.forName()方法动态加载
    • 通过ClassLoader.loadClass()方法动态加载
    package com.neo.classloader;
    public class loaderTest { 
            public static void main(String[] args) throws ClassNotFoundException { 
                    ClassLoader loader = HelloWorld.class.getClassLoader(); 
                    System.out.println(loader); 
                    //使用ClassLoader.loadClass()来加载类,不会执行初始化块 
                    loader.loadClass("Test2"); 
                    //使用Class.forName()来加载类,默认会执行初始化块 
                    //Class.forName("Test2"); 
                    //使用Class.forName()来加载类,并指定ClassLoader,初始化时不执行静态块 
                    //Class.forName("Test2", false, loader); 
            } 
    }
    

    Class.forName()和ClassLoader.loadClass()区别

    Class.forName():将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块;
    ClassLoader.loadClass():只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
    Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。并且只有调用了newInstance()方法采用调用构造函数,创建类的对象 。

    4.双亲委派模型:双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。

    双亲委派机制:

    1、当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
    2、当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader```去完成。
    3、如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;
    4、若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。
    ClassLoader源码分析:

    public Class<?> loadClass(String name)throws ClassNotFoundException {
            return loadClass(name, false);
    }
    
    protected synchronized Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {
            // 首先判断该类型是否已经被加载
            Class c = findLoadedClass(name);
            if (c == null) {
                //如果没有被加载,就委托给父类加载或者委派给启动类加载器加载
                try {
                    if (parent != null) {
                         //如果存在父类加载器,就委派给父类加载器加载
                        c = parent.loadClass(name, false);
                    } else {
                    //如果不存在父类加载器,就检查是否是由启动类加载器加载的类,通过调用本地方法native Class findBootstrapClass(String name)
                        c = findBootstrapClass0(name);
                    }
                } catch (ClassNotFoundException e) {
                 // 如果父类加载器和启动类加载器都不能完成加载任务,才调用自身的加载功能
                    c = findClass(name);
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    
    双亲委派模型意义:

    系统类防止内存中出现多份同样的字节码
    保证Java程序安全稳定运行

    二.JVM 内存结构
    JVM内存结构主要有三大块:堆内存、方法区和栈。堆内存是JVM中最大的一块由++年轻代和老年代++组成,而年轻代内存又被分成++三部分++,++Eden空间、From Survivor空间、To Survivor空间++,默认情况下年轻代按照8:1:1的比例来分配;

    方法区存储类信息、常量、静态变量等数据,是线程共享的区域,为与Java堆区分,方法区还有一个别名Non-Heap(非堆);栈又分为java虚拟机栈和本地方法栈主要用于方法的执行。
    程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。

    控制参数

    -Xms设置堆的最小空间大小。
    -Xmx设置堆的最大空间大小。
    -XX:NewSize设置新生代最小空间大小。
    -XX:MaxNewSize设置新生代最大空间大小。
    -XX:PermSize设置永久代最小空间大小。
    -XX:MaxPermSize设置永久代最大空间大小。
    -Xss设置每个线程的堆栈大小

    对象分配规则

    对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC。
    大对象直接进入老年代(大对象是指需要大量连续内存空间的对象)。这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存拷贝(新生代采用复制算法收集内存)。
    长期存活的对象进入老年代。虚拟机为每个对象定义了一个年龄计数器,如果对象经过了1次Minor GC那么对象会进入Survivor区,同时年龄+1,之后每经过一次Minor GC那么对象的年龄加1,知道达到阀值对象进入老年区。
    动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。
    空间分配担保。每次进行Minor GC时,JVM会计算Survivor区移至老年区的对象的平均大小,如果这个值大于老年区的剩余值大小则进行一次Full GC,如果小于检查HandlePromotionFailure设置,如果true则只进行Monitor GC,如果false则进行Full GC。

    GC 算法:

    判断对象是否存活一般有两种方式:

    引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。
    可达性分析(Reachability Analysis):从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,不可达对象。

    GC最基础的算法有三种:标记 -清除算法、复制算法、标记-压缩算法,我们常用的垃圾回收器一般都采用分代收集算法。

    详细介绍:

    标记 -清除算法,“标记-清除”(Mark-Sweep)算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
    复制算法,“复制”(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
    标记-压缩算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
    分代收集算法,“分代收集”(Generational Collection)算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。
    垃圾回收器了解:

    Serial收集器,串行收集器是最古老,最稳定以及效率高的收集器,可能会产生较长的停顿,只使用一个线程去回收。
    ParNew收集器,ParNew收集器其实就是Serial收集器的多线程版本。
    Parallel收集器,Parallel Scavenge收集器类似ParNew收集器,Parallel收集器更关注系统的吞吐量。
    Parallel Old 收集器,Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法
    CMS收集器,CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。
    G1 收集器,是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器,以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征。

    三.JVM 调优

    jvm监控分析工具一般分为两类,一种是jdk自带的工具,一种是第三方的分析工具。jdk自带工具一般在jdk bin目录下面,jconsole.exe和jvisualvm.exe;第三方的分析工具有很多,各自的侧重点不同,比较有代表性的:MAT(Memory Analyzer Tool)、GChisto等。

    Sun JDK监控和故障处理命令有jps jstat jmap jhat jstack jinfo
    例子:

    JPS:JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。

    $ jps -l -m
      28920 org.apache.catalina.startup.Bootstrap start
      11589 org.apache.catalina.startup.Bootstrap start
      25816 sun.tools.jps.Jps -l -m
    

    jstat(JVM statistics Monitoring)是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。

    $ jstat -class 11589
     Loaded  Bytes  Unloaded  Bytes     Time   
      7035  14506.3     0     0.0       3.67
      
    Loaded : 加载class的数量
    Bytes : class字节大小
    Unloaded : 未加载class的数量
    Bytes : 未加载class的字节大小
    Time : 加载时间
    

    jmap(JVM Memory Map)命令用于生成heap dump文件,如果不使用这个命令,还阔以使用-XX:+HeapDumpOnOutOfMemoryError参数来让虚拟机出现OOM的时候·自动生成dump文件。 jmap不仅能生成dump文件,还阔以查询finalize执行队列、Java堆和永久代的详细信息,如当前使用率、当前使用的是哪种收集器等。

    相关文章

      网友评论

        本文标题:JVM 原理探索

        本文链接:https://www.haomeiwen.com/subject/hymywktx.html