JVM体系四大块:
- 类的加载
- JVM内存结构
- GC算法 垃圾回收
- GC分析 性能调优
- 执行引擎
JVM架构图
jvm.arch
一、类的加载机制
类的加载
类的加载,将class文件读入方法区
,然后在堆中创建java.lang.Class对象
。类的加载最终是生成堆区中的Class对象。Class对象封装类在方法区中的数据结构,并提供访问方法区中的数据结构的接口。
类的生命周期
类的生命周期包括:加载、连接、初始化、使用和卸载,其中前三步是类的加载过程。
- 加载,查找并加载类的二进制数据,在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性能调优
网友评论