这些不可不知的JVM知识,我都用思维导图整理好了

小伙伴们不管是通过 APP还是其他招聘软件获得面试时,面试官总会安排一些常见面试题去提问 。小编再网上搜集了Java的一部分常见面试题,希望对Java小伙伴的面试有一定帮助,也希望各位多多支持,推荐更多小伙伴一起加入 APP
JVM是面试中必问的部分,本文通过思维导图以面向面试的角度整理JVM中不可不知的知识 。
先上图:
1、JVM基本概念
1.1、JVM是什么
JVM 的全称是 「Java」,也就是我们耳熟能详的 Java 虚拟机 。
JVM具备着计算机的基本运算方式,它主要负责把 Java 程序生成的字节码文件,解释成具体系统平台上的机器指令jvm内存模型及垃圾收集策略解析,让其在各个平台运行 。
JVM是运行在操作系统上的 , 它与硬件没有直接的交互 。
当然 , 严格来说JVM也是虚拟机规范,有很多不同的实现,Sun/和中的默认Java虚拟机是虚拟机,是目前使用范围最广的Java虚拟机,一般讲到的JVM默认指的就是虚拟机 。
1.2、Java程序运行过程
我们都知道 Java 源文件,通过编译器 , 能够生产相应的.Class 文件,也就是字节码文件,而字节码文件又通过 Java 虚拟机中的解释器,编译成特定机器上的机器码。
也就是如下:
每一种平台的解释器是不同的,但是实现的虚拟机是相同的,这也就是 Java 为什么能够跨平台的原因了  , 当一个程序从开始运行,这时虚拟机就开始实例化了,多个程序启动就会存在多个虚拟机实例 。程序退出或者关闭,则虚拟机实例消亡,多个虚拟机实例之间数据不能共享 。
1.3、JDK、JRE、JVM
JDK中包含JRE,也包括JDK , 而JRE也包括JDK 。
范围关系:JDK>JRE>JVM 。
2、JVM内存区域
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域 。根据《Java虚拟机规范》的规定 , Java虚拟机所管理的内存将会包括以下几个运行时数据区域:
当然 , 实际上,为了更好的适应 CPU 性能提升,最大限度提升JVM 运行效率 , JDK中各个版本对JVM进行了一些迭代,示意图如下:
JDK1.6、JDK1.7、JDK1.8 JVM 内存模型主要有以下差异:
2.1、程序计数器
一块较小的内存空间, 是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器 , 这类内存也称为“线程私有”的内存 。
正在执行 java 方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址) 。如果还是方法,则为空 。
这个内存区域是唯一一个在虚拟机中没有规定任何情况的区域 。
2.2、Java虚拟机栈
与程序计数器一样,Java虚拟机栈(JavaStack)也是线程私有的,它的生命周期与线程相同 。
虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,Java虚拟机都 会同步创建一个栈?。⊿tack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息 。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程 。
局部变量表存放了编译期可知的各种Java虚拟机基本数据类型、对象引用(类型,它并不等同于对象本身,可能是一个指向对象起始址的引用指针,也可能是指向一个代表对象的句柄或者其他与此对象相关的位置)和 类型(指向了一条字节码指令的地址) 。
2.3、本地方法栈
本地方法栈()与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务 , 而本地方法栈则是为虚拟机使用到的本地() 方法服务 。
Hot-Spot虚拟机直接把本地方法栈和虚拟机栈合二为一 。
与虚拟机栈一样,本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出和异常 。
2.4、Java堆
对于Java应用程序来说 , Java堆(Java Heap)是虚拟机所管理的内存中最大的一块 。Java堆是被所有线程共享的一块内存区域 , 在虚拟机启动时创建 。此内存区域的唯一目的就是存放对象实例,Java 世界里“几乎”所有的对象实例都在这里分配内存 。
Java堆是垃圾收集器管理的内存区域,因此一些资料中它也被称作“GC堆” 。
从回收内存的角度看,
需要注意的是这些区域划分仅仅是一部分垃圾收集器的共同特性或者说设计风格而已,而非某个Java虚拟机具体实现的固有内存布局,里面已经出现了不采用分代设计的新垃圾收集器 。
Java堆既可以被实现成固定大小的,也可以是可扩展的,不过当前主流的Java虚拟机都是按照可扩展来实现的(通过参数-Xmx和-Xms设定) 。如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,Java虚拟机将会抛出异常 。
2.5、方法区(JDK1.8移除)
方法区( Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载 的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据 。
在JDK1.8以前,使用永久代来实现方法区,所以某些场合也认为方法区和永久代是一个概念 。
在JDK 6的 时候开发团队就有放弃永久代,逐步改为采用本地内存( )来实现方法区的计划了,到了JDK 7的,已经把原本放在永久代的字符串常量池、静态变量等移出 , 而到了 JDK 8 , 终于完全废弃了永久代的概念,改用在本地内存中实现的元空间(Meta- space)来代替 , 把JDK 7中永久代还剩余的内容(主要是类型信息)全部移到元空间中 。
如果方法区无法满足新的内存分配需求时,将抛出异常 。
2.6、运行时常量池
运行时常量池(Pool)是方法区的一部分——在JDK1.8已经被移到了元空间 。
运行时常量池相对于Class文件常量池的一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是说,并非预置入Class文件中常量池的内容才能进入运行时常量池 , 运行期间也可以将新的常量放入池中 , 这种特性被开发人员利用得比较多的便是类的 ()方法 。
2.7、直接内存
直接内存( )并不是虚拟机运行时数据区的一部分 。
显然 , 本机直接内存的分配不会受到Java堆大小的限制,但是 , 既然是内存,则肯定还是会受到本机总内存(包括物理内存、SWAP分区或者分页文件)大小以及处理器寻址空间的限制 。
元空间从虚拟机 Java 堆中转移到本地内存,默认情况下,元空间的大小仅受本地内存的限制 。jdk1.8 以前版本的 class 和 JAR 包数据存储在下面,大小是固定的,而且项目之间无法共用,公有的 class,所以比较容易出现 OOM 异常 。
升级 JDK 1.8 后,元空间配置参数,-XX:=512M XX:=1024M 。
3、JVM中的对象
上面已经了解Java虚拟机的运行时数据区域,我们接下来更进一步了解这些虚拟机内存中数据的其他细节,譬如它们是如何创建、如何布局以及如何访问的 。以最常用的虚拟机和最常用的内存区域Java堆为例,了解一下虚拟机在Java堆中对象分配、布局和访问的全过程 。
3.1、对象的创建
Java对象创建的大概过程如下:
内存分配的两种?式:选择以上两种?式中的哪?种 , 取决于 Java 堆内存是否规整 。? Java 堆内存是否规整,取决于 GC收集器的算法是”标记-清除” , 还是”标记-整理”(也称作”标记-压缩”),值得注意的是,复制算法内存也是规整的 。
3.2、对象的内存布局
在虚拟机里 , 对象在堆内存中的存储布局可以划分为三个部分:对象头()、实例数据( Data)和对齐填充() 。
虚拟机对象的对象头部分包括两类信息 。第一类是用于存储对象自身的运行时数据,如哈希码()、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32个比特和64个比特 , 官方称它为“Mark Word” 。
3.3、对象的访问定位
建?对象就是为了使?对象,我们的Java程序通过栈上的数据来操作堆上的具体对象 。对象的访问?式有虚拟机实现?定,?前主流的访问?式有①使?句柄和②直接指针两种:
4、GC垃圾回收
对于垃圾回收,主要考虑的就是完成三件事:
4.1、如何判断对象需要回收?
4.1.1、引用计数法
引用计数法的算法:
客观地说,引用计数算法( )虽然占用了一些额外的内存空间来进行计数 , 但它的原理简单,判定效率也很高,在大多数情况下它都是一个不错的算法 。也有一些比较著名的应用案例 , 例如微软COM(Model)技术、使用 3的、语言以及在游戏脚本领域得到许多应用的中都使用了引用计数算法进行内存管理 。
但是,在Java 领域,至少主流的Java虚拟机里面都没有选用引用计数算法来管理内存,主要原因是,这个看似简单的算法有很多例外情况要考虑,例如在处理处理一些相互依赖、循环引用时非常复杂 。
4.1.2、可达性分析算法
当前主流的商用程序语言(Java、C#,上溯至前面提到的古老的Lisp)的内存管理子系统,都是通过可达性分析( )算法来判定对象是否存活的 。这个算法的基本思路就是通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始 , 根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”( Chain),如果某个对象到GC Roots间没有任何引用链相连 ,  或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的 。
GC Roots 包括;
4.1.3、引用
无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否引用链可达 , 判定对象是否存活都和“引用”离不开关系 。
Java的引用分为四种:强引用( Re-)、软引用(Soft )、弱引用(Weak )和虚引用( ) 。
4.2、垃圾收集算法
4.2.1、标记-清除算法
最早出现也是最基础的垃圾收集算法是“标记-清除”(Mark-Sweep)算法,
算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记存活的对象 , 统一回
收所有未被标记的对象 。标记过程就是对象是否属于垃圾的判定过程 。
后续的收集算法大多都是以标记-清除算法为基础jvm内存模型及垃圾收集策略解析,对其缺点进行改进而得到的 。
它的主要缺点有两个:
程的执行效率都随对象数量增长而降低;
标记-清除算法的执行过程如图:
4.2.2、标记-复制算法
标记-复制算法常被简称为复制算法 。为了解决标记-清除算法面对大量可回收对象时执行效率低的问题 。
它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块 。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉 。
这样实现简单 , 运行高效,不过其缺陷也显而易见,这种复制回收算法的代价是将可用内存缩小为了原来的一半,空间浪费较多 。
标记-复制算法的执行过程如图所示 。
4.2.3、标记-整理算法
标记-整理算法的标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存 。
“标记-整理”算法的示意图如图:
4.3、分代收集理论
当前商业虚拟机的垃圾收集器,大多数都遵循了“分代收集”( )的理论进行设计,分代收集名为理论 , 实质是一套符合大多数程序运行实际情况的经验法则,它建立在两个分代假说之上:
基于这两个假说,收集器应该将Java堆划分出不同的区域,然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储 。
设计者一般至少会把Java堆划分为新生代(Young )和老年代(Old )两个区域 。顾名思义 , 在新生代中,每次垃圾收集
时都发现有大批对象死去,而每次回收后存活的少量对象,将会逐步晋升到老年代中存放 。
基于这种分代 , 老年代和新生代具备不同的特点,可以采用不同的垃圾收集算法 。
因为有了分代收集理论,所以就有了了“Minor GC(新?代GC)”、“Major GC(?年代GC)”、“Full GC(全局GC)”这样的回收类型的划分
4.4、垃圾收集器
4.4.1、收集器
收集器是最基础、历史最悠久的收集器,曾经(在JDK 1.3.1之前)是虚拟机新生代收集器的唯一选择 。这个收集器是一个单线程工作的收集器,但它的“单线 程”的意义并不仅仅是说明它只会使用一个处理器或一条收集线程去完成垃圾收集工作,更重要的是强调在它进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束 。
/ Old收 集器的运行过程如下:
4.4.2、收集器
收集器实质上是收集器的多线程并行版本,除了同时使用多条线程进行垃圾收集之外,其余的行为包括收集器可用的所有控制参数(例如:-XX:、-XX:ld、-XX:re等)、收集算法、Stop The World、对象分配规则、回收策略等都与收集器完全一致 , 在实现上这两种收集器也共用了相当多的代码 。
收集器的工作过程如图所示:
4.4.3、 收集器
收集器也是一款新生代收集器,它同样是基于标记-复制算法实现的收集器,也是能够并行收集的多线程收集器
收集器的目标则是达到一个可控制的吞吐量() 。由于与吞吐量关系密切 ,  收集器也经常被称作“吞吐量优先收集器” 。
收集器提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集停顿时间的-XX:参数以及直接设置吞吐量大小的-XX:参数 。
4.4.4、 Old收集器
Old是收集器的老年代版本,它同样是一个单线程收集器 , 使用标记-整理算法 。这个收集器的主要意义也是供客户端模式下的虚拟机使用 。如果在服务端模式下,它也可能有两种用途:一种是在JDK 5以及之前的版本中与 收集器搭配使用,另外一种就是作为CMS 收集器发生失败时的后备预案,在并发收集发生 Mode 时使用 。这两点都将在后面的内容中继续讲解 。
Old收集器的工作过程如图所示 。
4.4.5、 Old收集器
Old是 收集器的老年代版本 , 支持多线程并发收集,基于标记-整理算法实现 。这个收集器是直到JDK 6时才开始提供的 。Old收集器的工作过程如图所示 。
4.4.6、CMS收集器
CMS( Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器 。目前很大一部分的Java应用集中在互联网网站或者基于浏览器的B/S系统的服务端上,这类应用通常都会较为服务的响应速度,希望系统停顿时间尽可能短,以给用户带来良好的交互体验 。CMS收集器就非常符合这类应用的需求 。
Mark Sweep收集器运行过程如图:
4.4.7、 First收集器
G1是一款主要面向服务端应用的垃圾收集器,是目前垃圾回收器的前沿成果 。开发团队最初赋予它的期望是(在比较长期的)未来可以替换掉JDK 5中发布的CMS收集器 。现在这个期望目标已经实现过半了 , JDK 9发布之日 , G1宣告取代 加 Old组合 , 成为服务端模式下的默认垃圾收集器 。
G1收集器运行过程如图:
5、JVM类加载
Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制 。
5.1、类加载过程
一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载 ()、验证()、准备()、解析()、初始化 ()、使用(Using)和卸载()七个阶段,其中验证、准备、解析三个部分统称为连接() 。
过程如下图:
加载 :
“加载”()阶段是整个“类加载”(Class )过程中的一个阶段,在加载阶段,Java虚拟机需要完成以下三件事情:
验证:
验证是连接阶段的第一步,这一阶段的目的是确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机自身的安全 。
验证阶段大致上会完成四个阶段的检验动作:文件格式验证、元数据验证、字节码验证和符号引用验证 。
准备:
准备阶段是正式为类中定义的变量(即静态变量,被修饰的变量)分配内存并设置类变量初始值的阶段 。
解析:
解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程 。
5.2、类加载器
【这些不可不知的JVM知识,我都用思维导图整理好了】Java虚拟机设计团队有意把类加载阶段中的“通过一个类的全限定名来获取描述该类的二进制字节流”这个动作放到Java虚拟机外部去实现 , 以便让应用程序自己决定如何去获取所需的类 。实现这个动作的代码被称为“类加载器”(Class ) 。
5.2.1、类与类加载器
类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远超类加载阶段 。对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间 。这句话可以表达得更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义 , 否则,即使这两个类来源于同一个Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同 , 那这两个类就必定不相等 。
5.2.2、双亲委派模型
JVM 中内置了三个重要的  , 启动类加载器( ),这个类加载器使用C++语言实现,是虚拟机自身的一部分,其他所有
的类加载器,这些类加载器都由Java语言实现,独立存在于虚拟机外部 , 并且全都继承自抽象类java.lang. 。
双亲委派模型: 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载 。
**为什么要使用双亲委派模型来组织类加载器之间的关系呢?**一个显而易见的好处就是Java中的类随着它的类加载器一起具备了一种带有优先级的层次关系 。例如类java.lang.,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此类在程序的各种类加载器环境中都能够保证是同一个类 。反之,如果没有使用双亲委派模型,都由各个类加载器自行去加载的话,如果用户自己也编写了一个名为java.lang.的类,并放在程序的 中 , 那系统中就会出现多个不同的类,Java类型体系中最基础的行为也就无从保证,应用程序将会变得一片混乱 。
5.2.3、破坏双亲委派模型
过双亲委派模型并不是一个具有强制性约束的模型,而是Java设计者推荐给开发者们的类加载器实现方式 。在Java的世界中大部分的类加载器都遵循这个模型,但也有例外的情况 , 直到Java模块化出现为止,双亲委派模型主要出现过3次较大规模“被破坏”的情况 。
JDK1.2发布之前 , 兼容之前的代码 。
第二次被破坏是这个模型自身的缺陷导致的 。双亲委派模型很好的解决了各个类加载器的基础类的统一问题(越基础的类由越上层的加载器进行加载),基础类之所以称为“基础”,是因为它们总是作为被用户代码调用的API,但没有绝对,如果基础类调用会用户的代码怎么办呢?
这不是没有可能的 。一个典型的例子就是JNDI服务 , JNDI现在已经是Java的标准服务,它的代码由启动类加载器去加载(在JDK1.3时就放进去的rt.jar),但它需要调用由独立厂商实现并部署在应用程序的下的JNDI接口提供者(SPI,)的代码,但启动类加载器不可能“认识“这些代码啊 。因为这些类不在rt.jar中,但是启动类加载器又需要加载 。怎么办呢?
为了解决这个问题,Java设计团队只好引入了一个不太优雅的设计:线程上下文类加载器() 。这个类加载器可以通过java.lang.类的r方法进行设置 。如果创建线程时还未设置,它将会从父线程中继承一个 , 如果在应用程序的全局范围内都没有设置过多的话,那这个类加载器默认即使应用程序类加载器 。
有了线程上下文加载器 , JNDI服务使用这个线程上下文加载器去加载所需要的SPI代码,也就是父类加载器请求子类加载器去完成类加载的动作,这种行为实际上就是打通了双亲委派模型的层次结构来逆向使用类加载器,实际上已经违背了双亲委派模型的一般性原则 。但这无可奈何 , Java中所有涉及SPI的加载动作基本胜都采用这种方式 。例如JNDI,JDBC,JCE,JAXB,JBI等 。
双亲委派模型的第三次“被破坏”是由于用户对程序的动态性的追求导致的 。为了实现热插拔,热部署,模块化,意思是添加一个功能或减去一个功能不用重启,只需要把这模块连同类加载器一起换掉就实现了代码的热替换 。例如OSGi的出现 。在OSGi环境下,类加载器不再是双亲委派模型中的树状结构 , 而是进一步发展为网状结构 。
如果我们自己想定义一个类加载器 , 破坏双亲委派模型,只需要重写重写其中的方法,使其不进行双亲委派即可 。
5.2.4、类加载器架构
是主流的Java Web服务器之一 , 为了实现一些特殊的功能需求,自定义了一些类加载器 。
类加载器如下:
实际上也是破坏了双亲委派模型的 。
是web容器,可能需要部署多个应用程序 。不同的应用程序可能会依赖同一个第三方类库的不同版本,但是不同版本的类库中某一个类的全路径名可能是一样的 。如多个应用都要依赖.jar,但是A应用需要依赖1.0.0版本,但是B应用需要依赖1.0.1版本 。这两个版本中都有一个类是com..Test.class 。如果采用默认的双亲委派类加载机制,那么无法加载多个相同的类 。
所以,破坏双亲委派原则,提供隔离的机制,为每个web容器单独提供一个加载器 。
的类加载机制:为了实现隔离性,优先加载 Web 应用自己定义的类,所以没有遵照双亲委派的约定 , 每一个应用自己的类加载器——负责加载本身的目录下的class文件,加载不到时再交给加载,这和双亲委派刚好相反 。
6、JVM故障处理
6.1、基础故障处理工具
6.1.1、jps:虚拟机进程状况工具
jps(JVMTool),它的功能与 ps 命令类似 , 可以列出正在运行的虚拟机进程,并显示虚拟机执行主类(Main Class,main()函数所在的类)
名称以及这些进程的本地虚拟机唯一 ID ( Local,LVMID) , 类似于 ps -ef | grep java 的功能 。
命令格式
jps [] []
6.1.2、jstat:虚拟机统计信息监视工具
jstat(JVMTool),用于监视虚拟机各种运行状态信息 。它可以查看本地或者远程虚拟机进程中,类加载、内存、垃圾收集、即时编译等运行时数据 。
命令格式
jstat -[-t] [-h] [ []]
[:][//]lvmid[@[:port]/]
选项列表:
6.1.3、jinfo:Java配置信息工具
jinfo( Info for Java),实时查看和调整 JVM 的各项参数 。在上面讲到 jps -v 指令时 , 可以看到它把虚拟机启动时显式的参数列表都打印
出来了,但如果想更加清晰的看具体的一个参数或者想知道未被显式指定的参数时,就可以通过 jinfo -flag 来查询了 。
命令格式
jinfo [] pid
6.1.4、jmap:Java内存映像工具
jmap( Map for Java) , 用于生成堆转储快照( 文件) 。
jmap 的作用除了获取堆转储快照 , 还可以查询执行队列、Java 堆和
方法区的详细信息 。
命令格式
jmap [] pid
6.1.5、jhat:虚拟机堆转储快照分析工具
jhat(JVM HeapTool),与 jmap 配合使用,用于分析 jmap 生成的堆转储快照 。
jhat 内置了一个小型的 http/web 服务器,可以把堆转储快照分析的结果 , 展示在浏览器中查看 。不过用途不大,基本大家都会使用其他第三方工具 。
命令格式
jhat [-stack ] [-refs ] [-port ] [- ] [-
debug ] [-] [-h|-help]
6.1.6、:Java堆栈跟踪工具
(Stack Trace for Java),用于生成虚拟机当前时刻的线程快照(、) 。
线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的目的通常是定位线程出现长时间停顿的原因,如:线程死锁、死循环、请求
外部资源耗时较长导致挂起等 。
线程出现听顿时通过来查看各个线程的调用堆栈,就可以获得没有响应的线程在搞什么鬼 。
命令格式
[] vmid
选项参数:
6.2、可视化故障处理工具
JDK中除了附带大量的命令行工具外,还提供了几个功能集成度更高的可视化工具,用户可以使用这些可视化工具以更加便捷的方式进行进程故障诊断和调试工作 。这类工具主要包括、 JHSDB、和JMC四个 。
6.2.1、JHSDB:基于服务性代理的调试工具
JDK中提供了JCMD和JHSDB两个集成式的多功能工具箱,它们不仅整合了所有 基础工具所能提供的专项功能 , 而且由于有着“后发优势”,能够做得往往比之前的老工具们更好、更强大 。
JHSDB是一款基于服务性代理( Agent,SA)实现的进程外调试工具 。
使用以下命令进入JHSDB的图形化模式,并使其附加进程11180:
jhsdb hsdb --pid 11180
命令打开的JHSDB的界面:
6.2.2、:Java监视与管理控制台
(Javaand)是一款基于JMX(Java -ment )的可视化监视、管理工具 。它的主要功能是通过JMX的MBean( Bean)对系统进 行信息收集和参数动态调整 。
连接页面 :
通过JDK/bin目录下的.exe启动JCon-sole后 , 会自动搜索出本机运行的所有虚拟机进程
6.2.3、:多合-故障处理工具
(All-in-One JavaTool)是功能最强大的运行监视和故障处理程序之一,曾经在很长一段时间内是官方主力发展的虚拟机故障处理工具 。
它除了常规的运行监视、故障处理外,还可以做性能分析等工作 。因为它的通用性很强,对应用程序影响较小,所以可以直接接入到生产环境中 。
的插件可以手工进行安装,在网站上下载nbm包后,点击“工具->插件->已下载”菜单,然后在弹出对话框中指定nbm包路径便可完成安装 。
插件页签:
6.2.4、Java:可持续在线的监控工具
JMC最初是BEA公司的产品,因此并没有像那样一开始就基于自家的Net-Beans平台来开发 , 而是选择了由IBM捐赠的 RCP作为基础框架 , 现在的JMC不仅可以下载到独立程序,更常见的是作为的插件来使用 。JMC与虚拟机之间同样采取JMX协议进行通信,JMC一方面作为 JMX控制台 , 显示来自虚拟机MBean提供的数据;另一方面作为JFR的分析工具,展示来自JFR的数据 。
JMC的主界面如图:
本文到此结束,希望对大家有所帮助 。