关于jvm:读书笔记之深入理解Java虚拟机JVM高级特性与最佳实践

32次阅读

共计 10561 个字符,预计需要花费 27 分钟才能阅读完成。

💡 学而不思则罔,思而不学则殆。—— 孔子

👉 微信公众号已开启,菜农曰,没关注的同学们记得关注哦!

本篇带来的是周志明老师编写的《深刻了解 Java 虚拟机:JVM 高级个性与最佳实际》,非常硬核!

全书共分为 5 局部,围绕 内存治理 执行子系统 程序编译与优化 高效并发 等外围主题对 JVM 进行了全面而深刻的剖析,粗浅揭示了 JVM 工作原理。

全书整体 5 个局部,十三章,共 358929 字。整体构造相当清晰,以至于写读书笔记的时候无从摘抄(甚至想把全书复述一遍),以下是全书第二局部的内容,望读者细细品味!

一、第一局部 走进 Java

第一局部介绍了对于 Java 的技术体系与发展史,谈及将来。该局部内容不做摘抄,间接进入外围主题。

二、第二局部 主动内存管理机制

Java 与 C++ 之间有一堵由内存动态分配和垃圾收集技术所围成的“高墙”,墙里面的人想进去,墙外面的人却想进去。

第二章 Java 内存区域与内存溢出异样

对于 Java 程序员来说是幸福的也是可悲的,在虚拟机主动内存管理机制的帮忙下不须要为每一个 new 操作去写配对的 delete/ free 代码,不容易呈现 内存泄露 内存溢出 问题,然而在内存治理畛域中,C 或 C++,既是领有最高势力的 “ 皇帝 ” 又是从事最根底工作的 “ 劳动人民 ”。

1)运行时数据区域

Java 虚拟机在执行 Java 程序的过程中会把它所治理的内存划分为若干个不同的数据区域.

  1. 程序计数器

这是一块较小的内存空间,能够看作是以后线程所执行的字节码的行号指示器

为了线程切换后可能复原到正确的执行地位,每条线程都须要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,线程公有

此内存区域是惟一一个在 Java 虚拟机标准中没有规定任何 OutOfMemoryError 状况的区域。

1. 虚拟机栈

与程序计数器一样,Java 虚拟机栈也是 线程公有 的,它的生命周期与线程雷同。虚拟机形容的是 Java 办法执行的内存模型:每个办法在执行的同时都会创立一个栈帧用于存储 局部变量表、操作数栈、动静链接、办法进口等信息。每一个办法从调用直至执行实现的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

2. 本地办法栈

与虚拟机栈十分相似,次要的区别在于虚拟机栈是为虚拟机执行 Java 办法服务,而本地办法栈是为 虚拟机应用到的 Native 办法服务。

与虚拟机栈一样的是:本地办法栈也会抛出 StackOverFlowError 和 OutOfMemoryError 异样

3. Java 堆

Java 堆是 Java 虚拟机所治理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创立。

此内存区域的惟一目标就是寄存对象实例,简直所有的对象实例都会在这里进行内存调配。

Java 堆是垃圾收集器治理的次要区域,因而很多时候也会称为 GC 堆,因而还能够细分为:新生代和老年代,而新生代中又能够粗疏为 Eden 空间 From Survivor 空间To Survivor 空间 等。

4. 办法区

办法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的 类信息、常量、动态变量、即时编译器编译后的代码等数据。

永恒代与办法区,实质上两者并不等价

HotSpot 虚拟机设计团队会利用 永恒代 来实现办法区,这样 HotSpot 的垃圾收集器就能够像 Java 堆那样治理这部分内存,可能省去专门为办法区编写内存治理代码的工作。

Java 虚拟机标准对办法区的限度十分宽松,除了和 Java 堆一样不须要间断的内存和能够抉择固定大小,还能够抉择不实现垃圾收集。

并非数据进入了办法区就如永恒代的名字一样“永恒”存在了。这区域的内存回收指标次要是针对常量池的回收和对类型的卸载,一般来说,这个区域的回收“问题”比拟难以令人满意,尤其是类型的卸载,条件相当刻薄,然而这部分区域的回收的确是必要的

2)扩大阐明

  1. 运行时常量池

运行时常量是办法区的一部分。用于寄存 编译期生成的各种字面量和符号援用,这部分内存将在类加载后进入办法区的运行时常量池中寄存。

运行时常量池绝对于 Class 文件常量池的另外一个重要特色是具备 动态性(并不要求常量肯定只有编译期能力产生,运行期间也能够将新的常量存入池中)

运行时常量池是 办法区 的一部分,当常量池无奈再申请到内存时也会抛出 OutOfMemoryError 异样。

  1. 间接内存

间接内存并不是虚拟机运行时数据区的一部分,也不是 Java 虚拟机标准中定义的内存区域,但这部分也会导致 OutOfMemoryError 异样。

在 Java1.4 引入的 NIO,是一种基于通道(Channel)与缓冲区(Buffer)的 I/O 形式,能够应用 Native 函数库间接调配 堆外内存,防止了在 Java 堆和 Native 堆中来回复制数据,能够在一些场景中显著进步性能。

间接内存的调配不会受到 Java 堆大小的限度,但还是会受到本机总内存大小以及处理器寻址空间的限度。

3)HotSpot 虚拟机对象探秘

  1. 对象的创立

家喻户晓,对象的创立通常是应用 new 关键字生成的

虚拟机遇到一条 new 指令时,首先将去查看这个指令的参数是否能在常量池中定位到一个类的 符号援用 ,并且查看该符号援用是否曾经被 加载 解析 初始化(类加载的过程)。如果没有,则必须先执行相应的类加载过程。

在类加载查看通过后,虚拟机将为新生对象分配内存,对象所需内存的大小在类加载实现后便可齐全确定,为对象调配空间的工作等同于把一块确定大小的内存从 Java 堆中划分进去。

分配内存仅仅是把那个指针向闲暇空间那边移动一段与对象大小相等的间隔,这种调配的形式称为 指针碰撞。(虚拟机必须保护一个列表,记录哪些内存块是可用的,在调配的时候就从列表中找到一块足够大的空间划分给对象实例,这种调配形式称为 闲暇列表

当然,创建对象还须要思考 并发问题,有可能给对象 A 分配内存,指针还没来得及批改,对象 B 又同时应用了原来的指针来分配内存。

解决该问题有两种计划:

  • 对分配内存空间的动作进行同步解决——实际上虚拟机采纳 CAS 配上失败重试的形式保障更新操作的原子性
  • 把内存调配的动作依照线程划分在不同的空间之中,即每个线程在 Java 堆中事后调配一小块内存,称为 本地线程调配缓冲(TLAB)。

内存调配结束后,虚拟机须要将调配到内存空间都初始化为零值(不包含对象头),如果应用 TLAB,这一工作过程也能够提前至 TLAB 调配时进行。

到此,一个新的对象产生了,然而因为还没有执行<init> 办法,这个时候对象的所有字段都是 0。

  1. 对象的内存布局

对象内存中存储的布局能够分为 3 块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)

对象头:

  • 用于存储对象本身的运行时数据,如哈希码、GC 分代年龄、锁状态标记、线程持有的锁、偏差线程 ID、偏差工夫戳等,这部分数据的长度在 32 位和 64 位的虚拟机(未开启压缩指针)中别离为 32 bit 和 64bit,官网称为 Mark Word。

  • 另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

实例数据:

是对象真正存储的无效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类中定义的,都须要记录下来。这部分存储程序会受到虚拟机调配策略参数和字段在 Java 源码中定义程序的影响。

对齐填充:

这部分并不是必然存在的,也没有特地的含意,它仅仅起着占位符的作用。

  1. 对象的拜访定位

建设对象是为了应用对象,咱们的 Java 程序须要通过栈上的 reference 数据来操作堆上的具体对象

目前支流的对象拜访形式有两种

  • 句柄拜访。Java 堆中会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,句柄中蕴含了对象实例数据与类型数据各自的具体地址信息。

  • 间接指针拜访。Java 堆对象的布局中就必须思考如何搁置拜访类型数据的相干信息,而 reference 中存储的就是对象地址。

这两种对象拜访形式各有劣势:

  • 句柄拜访:reference 中存储的最稳固的句柄地址,在对象被挪动(垃圾收集时挪动对象是十分广泛的行为)时只会扭转句柄中的实例数据指针,而 reference 自身不须要扭转。
  • 间接指针拜访:速度更快,节俭了一次指针定位的工夫开销

4)OutOfMemoryError 异样

除了程序计数器之外的其余几个运行时区域都有产生 OOM 异样的可能。

  1. 堆内存溢出

Java 堆用于存储对象实例,只有一直创建对象,并且保障 GC Roots 到对象之间有可达门路来防止垃圾回收机制来革除这些对象,那么在对象数量达到最大堆的容量限度后就会产生 内存溢出异样。

具体情况具体分析,能够借助内存映像剖析工具(如 Eclipse Memory Analyzer)对 Dump 进去的堆转储快照进行剖析,重点是确认内存中的对象是否是必要的,也就是要先分分明到底是呈现了 内存透露 (Memory Leak)还是 内存溢出(Memory Overflow)

如果是内存泄露,那么须要找到泄露对象是通过怎么的门路与 GC Roots 相关联并导致垃圾收集器无奈主动回收它们的。

如果是内存溢出,那就须要查看虚拟机的堆参数(-Xms 和 -Xmx),与机器物理内存比照看看是否还能够调大,从代码上查看是否存在某些对象生命周期过长,持有状态工夫过长的状况,尝试缩小程序运行期的内存耗费。

  1. 虚拟机栈和本地办法栈溢出
  • 如果线程申请的栈深度大于虚拟机所容许的最大深度,将抛出 StackOverflowError 异样
  • 如果虚拟机在扩大栈时无奈申请到足够的内存空间,则抛出 OutOfMemoryError 异样
  1. 办法区和运行时常量池溢出

在运行时常量池溢出,那么在 OutOfMemoryError 前面追随的提示信息是 PermGen space。

办法区溢出也是一种常见的内存溢出异样,一个类要被垃圾收集器回收掉,断定条件是比拟刻薄的。

在常常动静生成大量 Class 的利用中,须要特地留神类的回收情况。

这类场景除了应用了 CGLib 字节码加强和动静语言之外,常见的还有:大量 JSP 或动静产生 JSP 文件的应

用(JSP 第一次运行时须要编译为 Java 类)、基于 OSGi 的利用(即便是同一个类文件,被不同的加载器

加载也会视为不同的类)等。

  1. 本机间接内存溢出

DirectMemory 容量能够通过 -XX: MaxDirectMemorySize 指定。如果不指定,则默认与 Java 堆最大值(-Xmx)一样。

在 DirectByteBuffer 分配内存时也会抛出内存溢出异样,但它抛出异样时并没有真正向操作系统申请分配内存,而是通过计算机得悉内存无奈调配,于是手动抛出异样,真正申请分配内存的办法是:unsafe.allocateMemory()

由 DirectMemory 导致的内存溢出,一个显著的特色是在 Heap Dump 文件中不会看见显著的异样,如果读者发现 OOM 之后 Dump 的文件很小,而程序中又间接或间接应用了 NIO,那就能够思考查看是不是这方面的问题

第三章 垃圾收集器与内存调配策略

1)对象已死吗

在垃圾收集器工作之前须要确定这些对象中哪些是 存活 的,哪些曾经 死去

  1. 援用计数法

定义:给每个对象增加一个援用计数器,每当有一个中央援用它时,计数器值就加 1;当援用生效时,计数器值就减 1;任何时刻计数器为 0 的对象就是不可能再被应用的。

缺点:两个无用对象呈现互相援用而无奈回收。

  1. 可达性分析法

定义:通过一系列称为 GC Roots 的对象作为起始点,从这些节点开始向下搜寻,搜寻所走过的门路称为 援用链,当一个对象到 GC Roots 没有任何援用链相连时,则证实此对象是不可用的。

可作为 GC Roots 对象:

  • 虚拟机栈(栈帧中的本地变量表)中援用的对象
  • 办法区品种动态属性援用的对象
  • 办法区中常量援用的对象
  • 本地办法栈中 JNI(Native 办法)援用的对象
  1. 援用品种

JDK 1.2 之后,Java 对援用的概念进行了裁减,将援用分为 强援用、软援用、弱援用、虚援用 4 种

  • 强援用:普遍存在的援用(通过 new 形式),只有强援用还存在,垃圾收集器就永远不会回收掉被援用的对象
  • 软援用:一些还有用但非必须的对象。在零碎将要产生内存溢出异样之前,将会把这些对象列进回收范畴之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异样
  • 弱援用:强度比软援用更弱一些,被弱援用关联的对象只能生存到下一次垃圾收集产生之前。当垃圾收集器工作时,无论以后内存是否够用,都会回收掉只被弱援用关联的对象
  • 虚援用:一个对象是否有虚援用的存在,齐全不会对其生存工夫形成影响,也无奈通过虚援用来获得一个对象实例。为一个对象设置虚援用关联的惟一目标就是在这个对象被收集器回收时收到一个零碎告诉
  1. 生存还是死亡

即便在可达性剖析算法中不可达的对象,也并非是 非死不可

宣告一个对象死亡,至多要通过两次标记过程:如果对象在进行可达性剖析后发现没有与 GC Roots 相连接的援用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize()办法,当对象没有笼罩 finalize()办法,或曾经被虚拟机调用过,虚拟机将这两种状况都视为没有必要执行。

如果这个对象断定为有必要执行 finalize() 办法,那么这个对象会被搁置在一个叫做 F-Queue 的队列中,并在稍后由一个虚拟机主动建设的、低优先级的 Finalizer 线程去执行它。

finalize()办法是对象逃脱死亡命运的最初一次机会,稍后 GC 将对 F-Queue 中的对象进行第二次小规模的标记,如果对象要在 finalize() 中胜利援救本人——只有从新与援用链上的任何一个对象建设关联即可,譬如把本人(this 关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移除出“行将回收”的汇合。

留神:任何一个对象的 finalize() 办法都只会被零碎主动调用一次,如果对象面临下一次回收,它的 finallize() 办法不会被再次执行。

  1. 回收办法区

Java 虚拟机标准中对办法区的要求比拟宽松,能够不要求虚拟机在办法区中实现垃圾回收,但并不示意办法区没有垃圾回收。

永恒代的垃圾收集次要回收两局部内容:废除常量 和 无用的类。

类须要同时满足上面 3 个条件才算是 无用的类:

  • 该类的所有实例都曾经被回收,也就是 Java 堆中不存在该类的任何实例
  • 加载该类的 ClassLoader 曾经被回收
  • 该类对应的 java.lang.Class 对象没有在任何中央被援用,无论在任何中央通过反射拜访该类的办法

2)垃圾收集算法

  1. 标记 – 革除算法

标记 – 革除算法是最根底的收集算法。算法分为 标记 革除 两个阶段。

不足之处:效率问题 空间问题

  1. 复制算法

该算法将可用内存按容量划分为大小相等的两块,每次只应用其中一块,当一块的内存用完了,就将还存活着的对象复制到另外一块下面,而后再把已应用的内存空间一次清理掉。

不足之处:将内存放大为了原来的一半,代价有点高了

当初商业虚拟机都采纳这种收集算法来回收新生代。将内存分为一块较大的 Eden 空间和两块较小的 Survivor 空间。当回收时,将 Eden 和 Survivor 中还存活着的对象一次性地复制到另外一块 Survivor 空间上,最初清理掉 Eden 和方才用过的 Survivor 空间。(默认 Eden 和 Survivor 的大小比例是 8∶1)

  1. 标记 - 整顿算法

该算法分为 标记 整顿 算法,标记过程依然与 标记 - 革除算法一样,但后续步骤不是间接对可回收对象进行清理,而是让所有存活的对象都向一端挪动,而后间接清理掉端边界以外的内存。

  1. 分代收集算法

在新生代中,每次垃圾收集时都发现有少量对象死去,只有大量存活,那就选用复制算法,只须要付出大量存活对象的复制老本就能够实现收集。而老年代中因为对象存活率高、没有额定空间对它进行调配担保,就必须应用“标记—清理”或者“标记—整顿”算法来进行回收。

3)HotSpot 的算法实现

  1. 枚举根节点

可达性剖析对执行工夫的敏感还体现在 GC 进展上,因为这项剖析工作必须在一个能确保一致性的快照中

进行——这里“一致性”的意思是指在整个剖析期间整个执行零碎看起来就像被解冻在某个工夫点上,不

能够呈现剖析过程中对象援用关系还在一直变动的状况,该点不满足的话剖析后果准确性就无奈失去保

证。这点是导致 GC 进行时必须进展所有 Java 执行线程(Sun 将这件事件称为“Stop The World”)的其

中一个重要起因,即便是在号称(简直)不会产生进展的 CMS 收集器中,枚举根节点时也是必须要进展的。

虚拟机该当是有方法间接得悉哪些地方寄存着对象援用。在 HotSpot 的实现中,是应用一组称 OopMap

的数据结构来达到这个目标。

  1. 平安点

在 OopMap 的帮助下,HotSpot 能够疾速且精确地实现 GC Roots 枚举,但如果为每一条指令都生成对应

的 OopMap,那将会须要大量的额定空间,这样 GC 的空间老本将会变得很高。

因而 HotSpot 会在 特定的地位 记录这些信息,这些地位称为 平安点 程序执行时并非在所有中央都能停顿下来开始 GC,只有在达到平安点能力暂停。

平安点的选定基本上是以是否具备让程序长时间执行的特色选定的。

长时间执行最显著的特色就是指令序列复用(办法调用、循环跳转、异样跳转等)

在 GC 产生时要让所有线程都跑到平安点上再停顿下来有两种形式:

  • 领先式中断:不须要线程的执行代码被动去配合,在 GC 产生时,首先把所有线程都中断,如果发现有线程中断的中央不在平安点上,就复原线程,让它跑到平安点上。
  • 主动式中断:当 GC 须要中断线程的时候,不间接对线程操作,仅仅简略地设置一个标记,各个线程执行时被动去轮询这个标记,发现中断标记为真时就本人中断挂起。
  1. 平安区域

平安点机制保障了程序执行时,在不太长的工夫内就会遇到可进入 GC 的 Safepoint。

然而在程序不执行的时候(线程 Sleep 或 Blocked 状态)这时候线程就无奈响应 JVM 的中断请求,这个时候就须要 平安区域 来解决。平安区域是扩大了的 Safepoint。

在线程执行到 Safe Region 中的代码时,首先标识本人曾经进入了 Safe Region,那样,当这段时间里 JVM 要发动 GC 时,就不必管标识本人为 Safe Region 状态的线程了,在线程要来到 Safe Region 时,它要查看零碎是否曾经实现了根节点枚举(或者整个 GC 过程),如果实现了,那线程就继续执行,否则它就必须期待直到收到能够平安来到 SafeRegion 的信号为止。

4)垃圾收集器

如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。

两个概念:

  • 并行(Parallel):指多条垃圾收集线程并行工作,此时用户线程依然处于 期待 状态
  • 并发(Concurrent):用户线程与垃圾收集线程同时执行(不肯定是并行的,也能够是交替执行),用户程序持续运行,而垃圾收集程序则运行在另一个 CPU 上

在 HotSpot 中大抵存在着作用于不同分代的收集器,如果两个收集器之间存在连线,阐明能够搭配应用。

  1. Serial 收集器

这个收集器是一个单线程的收集器,在进行垃圾收集时,必须暂停其余所有的工作线程,直到它收集完结。

  1. ParNew 收集器

这个收集器就是 Serial 收集器的多线程版本(也就是说除了多条线程一起解决之外,其余行为与 Serial 收集器统一)。除了 Serial 收集器之外,只有 ParNew 能力与 CMS 配合应用

  1. Parallel Scavenge 收集器

该收集器也是应用 复制算法。该收集器与其余收集器的关注点不同 – 吞吐量优先收集器

  • CMS 等收集器的关注点在于 尽可能地缩短垃圾收集时用户线程的进展工夫
  • Parallel Scanvenge 则 尽可能达到一个可管制的吞吐量(吞吐量 = 运行用户代码工夫 /(运行用户代码工夫 + 垃圾收集工夫)

Parallel Scavenge 收集器无奈与 CMS 收集器配合工作,如果新生代抉择了 Parallel Scavenge 收集器,老年代只能抉择 Serial Old 收集器

  1. Serial Old 收集器

Serial Old 是 Serial 收集器的老年代版本,它同样是一个单线程收集器,应用“标记 - 整顿”算法。

  1. Parallel Old 收集器

Parallel Old 是 Parallel Scavenge 收集器的老年代版本,应用多线程和“标记 - 整顿”算法

  1. CMS 收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收进展工夫为指标的收集器。

CMS 收集器是基于“标记—革除”算法实现的,它的运作过程绝对于后面几种收集器来说更简单一些,整个过程分为 4 个步骤,包含:

  • 初始标记(需 Stop The World)
  • 并发标记
  • 从新标记(需 Stop The World)
  • 并发革除

因为整个过程中耗时最长的并发标记和并发革除过程收集器线程都能够与用户线程一起工作,所以,从总体上来说,CMS 收集器的内存回收过程是与用户线程一起并发执行的。

CMS 收集器显著的 3 个缺点:

  • CMS 收集器对 CPU 资源十分敏感
  • CMS 收集器无奈解决浮动垃圾,可能会呈现 “Concurrent Mode Failure” 失败而导致另一次 Full GC 产生
  • CMS 收集器会产生大量的内存碎片

浮动垃圾:因为 CMS 并发清理阶段用户线程还在运行这,随同程序运行天然就还有新的垃圾一直产生,这一部分垃圾呈现在标记过程之后,CMS 无奈在当次收集中解决,只好在下次 GC 时在清理掉。

  1. G1 收集器

G1 是一款面向服务端利用的垃圾收集器。具备以下特点:

  • 并行与并发
  • 分代收集
  • 空间整合:G1 从整体来看是基于“标记—整顿”算法实现的收集器,从部分(两个 Region 之间)上来看是基于“复制”算法实现的,但无论如何,这两种算法都意味着 G1 运作期间不会产生内存空间碎片,收集后能提供规整的可用内存
  • 可预测进展:G1 除了谋求低进展外,还能建设可预测的进展工夫模型,能让使用者明确指定在一个长度为 M 毫秒的工夫片段内,耗费在垃圾收集上的工夫不得超过 N 毫秒

G1 收集器的运作大抵可划分为以下几个步骤:

  • 初始标记
  • 并发标记
  • 最终标记(须要 Stop The World)
  • 筛选回收

5)内存调配与回收策略

  1. 对象优先在 Eden 调配

对象在新生代 Eden 区中调配,当 Eden 区中没有足够的空间进行调配时,虚拟机将发动一次 Minor GC。

Minor GC 和 Full GC 的区别:

  • 新生代 GC(Minor GC):产生在新生代的垃圾收集动作,因为 Java 对象太多都具备朝生夕灭的个性,所以 Minor GC 十分频繁,个别回收速度也比拟快
  • 老年代 GC(Major GC/ Full GC):指产生在老年代的 GC,呈现了 Major GC,常常会随同至多一次的 Minor GC(但非相对的,在 Parallel Scanvenge 收集器的收集策略里就有间接进行 Major GC 的策略抉择过程)。Major GC 的速度个别会比 Minor GC 慢 10 倍以上
  1. 大对象间接进入老年代

所谓的大对象是指,须要大量间断内存空间的 Java 对象,最典型的大对象就是那种很长的字符串以及数组

  1. 长期存活的对象将进入老年代

在虚拟机中会为每个对象定义一个对象年龄(Age)计数器。对象每熬过一次 Minor GC 年龄就减少 1 岁,当它年龄减少到肯定水平(默认 15 岁),就会进入老年代。

  1. 动静对象年龄断定

如果在 Survivor 空间中雷同年龄所有对象大小的总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象就能够间接进入老年代。

  1. 空间调配担保

在产生 Minor GC 之前,虚构机会先查看老年代最大可用的间断空间是否大于新生代所有对象总空间,如果条件成立,则能够确保 Minor GC 是平安的。如果不成立,则虚构机会查看 HandlePromotionFailure 设置值是否容许担保失败。如果容许,那么会持续查看老年代最大可用的间断空间是否大于历次降职到老年代对象的均匀大小,如果大于,将尝试进行一次 Minor GC,如果小于,或者 HandlePromotionFailure 设置不容许冒险,那这时也要改为进行一次 Full GC。

第四章 虚拟机性能监控与故障解决工具

1)JDK 的命令行工具

这些工具都异样玲珑,因为这些命令行工具大多数是 jdk/lib/tools.jar 类库的一层薄包装,次要性能代码是在 tools 类库中实现的。

  1. jps:虚拟机过程情况工具

性能:列出正在运行的虚拟机过程,并显示虚拟机执行主类(main()函数所在的类)名称以及这些过程的本地虚拟机惟一 ID

次要选项:

  1. jstat:虚拟机统计信息监督工具

性能:用于监督虚拟机各种运行状态信息的命令行工具。

次要选项:

  1. jinfo:Java 配置信息工具

性能:实时地查看和调整虚拟机各项参数

  1. jmap:Java 内存映像工具

性能:用于生成堆转储快照(heapdump 或 dump 文件)。还能够查问 finalize 执行队列,Java 堆和永恒代的详细信息,如空间使用率、以后用的是哪种收集器等

次要选项:

  1. jhat:虚拟机堆转储快照剖析工具

性能:与 jmap 搭配应用,来剖析 jmap 生成的堆转储快照。

  1. jstack:Java 堆栈跟踪工具

性能:用于生成虚拟机以后时刻的线程快照(threaddump 或 javacore 文件)

线程快照就是以后虚拟机中每一条线程正在执行的办法堆栈的汇合,生成线程快照的次要目标是定位线程呈现长时间进展的起因,如线程间死锁、死循环、申请内部资源导致的长时间期待等都是导致线程长时间进展的常见起因。

次要选项:

2)JDK 的可视化工具

  1. JConsole:Java 监督与治理控制台

基于 JMX 的可视化监督、管理工具。它治理局部的性能是针对 JMX MBean 进行治理。

  1. VisualVM:多合一故障解决工具

是目前为止随 JDK 公布的性能最弱小的运行监督和故障处理程序。解决运行监督、故障解决外,还提供了性能剖析。

这篇咱们次要是针对《深刻了解 Java 虚拟机:JVM 高级个性与最佳实际》第二局部做了相干的读书笔记,一方面是因为篇幅起因,接下来会针对每局部别离公布。另一方面是为了让读者有短缺的工夫进行消化,毕竟一口吃不成大瘦子 ~ 请读者缓缓浏览,静待下局部读书笔记的出炉!👨💻

不要空谈,不要贪懒,和小菜一起做个 吹着牛 X 做架构 的程序猿吧~ 点个关注做个伴,让小菜不再孤独。咱们下文见!

👀 明天的你多致力一点,今天的你就能少说一句求人的话!

👉🏻 微信公众号:菜农曰,没关注的同学们记得关注哦!

正文完
 0