美文网首页
Java虚拟机总结

Java虚拟机总结

作者: ands999 | 来源:发表于2021-02-20 18:21 被阅读0次

    JVM体系四大块:

    • 类的加载
    • JVM内存结构
    • GC算法 垃圾回收
    • GC分析 性能调优
    • 执行引擎

    JVM架构图


    jvm.arch

    一、类的加载机制

    类的加载
    类的加载,将class文件读入方法区,然后在堆中创建java.lang.Class对象。类的加载最终是生成堆区中的Class对象。Class对象封装类在方法区中的数据结构,并提供访问方法区中的数据结构的接口。

    类的生命周期
    类的生命周期包括:加载、连接、初始化、使用和卸载,其中前三步是类的加载过程。

    class.lifecycle
    • 加载,查找并加载类的二进制数据,在Java堆中创建Class类的对象。
    • 链接,
      • 验证,文件格式、元数据、字节码、符号引用的验证。
      • 准备,为类的静态变量分配内存,并初始化为默认值。
      • 解析,把类中的符号引用转换为直接引用。
    • 初始化,为类的静态变量赋予手动设置的初始值。
    • 使用,new出对象,在程序中使用。
    • 卸载,执行垃圾回收。

    几个小问题

    1、JVM初始化步骤 ? 2、类初始化时机 ?3、哪几种情况下,Java虚拟机将结束生命周期?

    答案参考 jvm系列(一):java类的加载机制

    类加载器

    启动类加载器:Bootstrap ClassLoader扩展类加载器:Extension ClassLoader应用程序类加载器:Application ClassLoader

    类加载机制
    全盘负责,当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也由该类加载器负责载入,除非显示使用另外一个类加载器来载入。
    父类委托,先让父类加载器尝试加载该类,只有在父类加载器无法加载时才从自己的类路径中加载该类。
    缓存机制,缓存机制保证所有加载过的Class都会被缓存。当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区中不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效。

    二、JVM内存结构

    jvm内存结构

    • Java堆(Heap),只存放对象实例,几乎所有的对象实例都在这里。
    • 方法区(Method Area),存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码。
    • 程序计数器(Program Counter Register),当前线程所执行的字节码的行号指示器。
    • JVM栈(JVM Stacks),其生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:方法被执行的时候同时会创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。方法从被调用到执行完成的过程,就对应着栈帧在栈中从入栈到出栈的过程。
    • 本地方法栈(Native Method Stacks),与虚拟机栈类似,虚拟机栈为虚拟机执行Java方法(字节码)服务,而本地方法栈为虚拟机使用到的Native方法服务。

    对象分配规则

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

    三、GC算法 垃圾回收

    1、对象存活判断方式

    • 引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时减1,计数为0时可以回收。此方法简单,但无法解决对象循环引用的问题。
    • 可达性分析:从GC roots向下搜索,搜索所走过的路径称为引用链。当对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,不可达对象。
      GC roots包括:虚拟机栈中引用的对象。本地方法栈中JNI引用的对象。 方法区中类静态属性、常量引用的对象。

    2、GC算法

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

    3、垃圾回收器

    • Serial回收器,是最古老,最稳定以及效率高的收集器,可能会产生较长的停顿,只能用一个线程回收。
    • ParNew回收器,ParNew收集器是Serial收集器的多线程版本。
    • Parallel Scavenge回收器,类似ParNew收集器,Parallel收集器更关注系统的吞吐量
    • CMS(Concurrent Mark Sweep)收集器,以获取最短回收停顿时间为目标的收集器。
    • G1(Garbage-First)收集器,是面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器。以极高概率满足GC停顿时间的同时,还具备高吞吐量。

    三、GC分析 性能调优

    1、GC日志分析

    [GC [<collector>: <starting occupancy1> -> <ending occupancy1>, <pause time1> secs] <starting occupancy3> -> <ending occupancy3>, <pause time3> secs] [Times: user=secs sys=secs, real=secs]

    <collector>GC收集器的名称
    <starting occupancy1> 新生代在GC前占用的内存
    <ending occupancy1> 新生代在GC后占用的内存
    <pause time1> 新生代局部收集时jvm暂停处理的时间
    <starting occupancy3> JVM Heap 在GC前占用的内存
    <ending occupancy3> JVM Heap 在GC后占用的内存
    <pause time3> GC过程中jvm暂停处理的总时间
    <user>用户时间
    <sys>系统时间
    <real>实际时间

    4.231: [GC 4.231: [DefNew: 4928K->512K(4928K), 0.0044047 secs] 6835K->3468K(15872K), 0.0045291secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

    4.445: [Full GC (System) 4.445: [Tenured: 2956K->3043K(10944K), 0.1869806 secs] 4034K->3043K(15872K), [Perm : 3400K->3400K(12288K)], 0.1870847secs] [Times: user=0.05 sys=0.00, real=0.19 secs]

    最前面的数字 4.231 和 4.445 代表虚拟机启动以来的秒数。
    接下来的 [DefNew , [Tenured , [Perm 表示 GC 发生的区域,区域的名称与使用的 GC 收集器相关。 Serial 收集器中新生代名为 "Default New Generation",显示的名字为 "[DefNew"。ParNew收集器显示的是 "[ParNew", “Parallel New Generation”。 Parallel Scavenge 收集器新生代名为 "PSYoungGen"。年老代和永久代名称也都由收集器决定。
    方括号内部显示的 “4928K->512K(4928K)” 表示 “GC 前该区域已使用容量 -> 垃圾回收后该区域已使用容量 (该区域内存总容量) ”。
    再往后的 “0.0044047 secs” 表示该区域GC所用时间,单位是秒。
    再往后的 “6835K->3468K(15872K)” 表示 “GC 前Java堆已使用容量 -> GC后Java堆已使用容量 (Java堆总容量)”。
    再往后的 “0.0045291 secs” 是Java堆GC所用的总时间。
    最后的 “[Times: user=0.00 sys=0.00, real=0.00 secs]” 分别代表用户态消耗的CPU时间、内核态消耗的CPU时间和操作从开始到结束所经过的墙钟时间。
    墙钟时间包括各种非运算的等待耗时,如IO等待、线程阻塞。CPU时间不包括等待时间,当系统有多核时,多线程操作会叠加这些CPU时间,所以user或sys时间会超过real时间。

    2、调优命令

    JDK监控和故障处理命令

    • jps,显示指定系统内所有的HotSpot虚拟机进程。
    • jstat,用于监视虚拟机运行时状态信息,可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
    • jmap,生成heap dump文件。
    • jhat,与jmap搭配使用,分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看。
    • jstack,生成java虚拟机当前时刻的线程快照。
    • jinfo,实时查看和调整虚拟机运行参数。

    3、调优工具

    常用调优工具分为两类,jdk自带监控工具:jconsole和jvisualvm;第三方有:MAT(Memory Analyzer Tool)。

    • jconsole,是从java5开始,JDK自带的java监控和管理控制台,用于对JVM中内存,线程和类等的监控。
    • jvisualvm,jdk自带全能工具,可以分析内存快照、线程快照;监控内存变化、GC变化等。
    • MAT,Memory Analyzer Tool,基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。

    四、执行引擎

    分配给运行时数据区的字节码将由执行引擎执行,执行引擎读取字节码并逐个执行。
    (1) 解释器:解释器更快地解释字节码,但执行缓慢。解释器的缺点是当一个方法被调用多次时,每次都需要一个新的解释。

    (2) JIT编译器:JIT编译器消除了解释器的缺点。执行引擎将在转换字节码时使用解释器的帮助,但是当它发现重复的代码时,将使用JIT编译器,编译整个字节码并将其更改为本地代码。这个本地代码将直接用于重复的方法调用,这提高了系统的性能。JIT的构成组件为:

    • 中间代码生成器(Intermediate Code Generator):生成中间代码。
    • 代码优化器(Code Optimizer):优化上面生成的中间代码。
    • 目标代码生成器(Target Code Generator):生成机器代码或本地代码。
    • 分析器(Profiler):查找热点,即该方法是否被多次调用。

    (3) 垃圾收集器(Garbage Collector):收集和删除未引用的对象。可以通过调用“System.gc()”触发垃圾收集,但不保证立即执行。

    Java本机接口(JNI):JNI与本机方法库进行交互,并提供执行引擎所需的本机库。
    本地方法库(Native Method Libraries):它是执行引擎所需的本机库的集合。

    参考

    jvm系列(一):java类的加载机制
    jvm系列(二):JVM内存结构
    jvm系列(三):GC算法 垃圾收集器
    jvm系列(四):jvm调优-命令篇
    jvm系列(七):jvm调优-工具篇
    http://www.ityouknow.com/
    如何合理的规划一次jvm性能调优

    相关文章

      网友评论

          本文标题:Java虚拟机总结

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