Full-Stack-Notes/notes/《深入理解Java虚拟机》读书笔记.md
2019-06-10 16:28:43 +08:00

16 KiB
Raw Blame History

《深入理解Java虚拟机》读书笔记

目录

第二章 java内存区域与内存溢出异常
    2.2 运行时数据区域
        2.2.1 程序计数器
        2.2.2 Java虚拟机栈
        2.2.3 本地方法栈
        2.2.4 Java堆
        2.2.5 方法区
第四章 虚拟机性能监控与故障处理工具
    4.2.1 JDK命令行工具
        4.2.1 jps虚拟机进程状况工具
        4.2.2 jstat虚拟机统计信息监视工具
            1. gc 参数返回结果
            2. class 参数返回结果
            3. gcmetacapacity 参数返回结果
        4.2.3 jinfoJava配置信息工具
        4.2.4 jmapJava内存映像工具
        4.2.5 jhat虚拟机堆转储快照分析工具
        4.2.6 jstackJava堆栈跟踪工具
    4.3 JDK的可视化工具
第十二章 Java内存模型

正文

第二章 java内存区域与内存溢出异常

2.2 运行时数据区域


2.2.1 程序计数器

当前线程所执行的字节码的行号指示器。

  • 为了线程切换后能够恢复到正确地执行位置,每个线程都需要有一个独立的程序计数器;
  • 如果正在执行java方法计数器记录的是正在执行的虚拟机字节码指令的地址如果执行的是Native方法则计数器值为空
  • 此内存区域是唯一一个在java虚拟机规范中没有规定任何outOfMemoryError情况的区域。

2.2.2 Java虚拟机栈

虚拟机栈描述的是Java方法执行的内存模型每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出入口等信息。每一个方法从调用直到执行完成的过程就对应着一个栈帧在虚拟机中入栈到出栈的过程。

  • 局部变量表存放了编译器可知的各种基本数据类型、对象引用和returnAddress类型指向一条字节码指令的地址
  • 在java虚拟机规范中对这个区域规定了两种异常
    • 如果线程请求的栈深入大于虚拟机所允许的深度将抛出StackOverflowError异常
    • 如果虚拟机栈扩展时无法申请到足够的内存就会抛出OutOfMemoryError异常。

2.2.3 本地方法栈

与虚拟机栈类似针对Native 方法服务。

2.2.4 Java堆

此内存区域的唯一目的就是存放对象实例几乎所有的对象实例都在这里分配内存。Java 堆可以处于物理上不连续的内存空间中只要是逻辑上是连续的即可。如果堆中没有内存完成实例分配并且堆也无法在扩展时将会抛出OutOfMemoryError异常。

2.2.5 方法区

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

  • 当方法区无法满足内存分配的需求时将会抛出OutOfMemoryError异常。
  • 运行时常量池是方法区的一部分Class文件中出了有类的版本、字段、方法、接口等描述信息外还有一项信息是常量池用于存放编译期生成的各种字面量和符号引用这部分内容将在类加载后进入方法区的运行时常量池中存在。

第四章 虚拟机性能监控与故障处理工具

部分内容参考自博客:JDK的命令行工具

4.2.1 JDK命令行工具

4.2.1 jps虚拟机进程状况工具

命令格式: jps [options] [hostid]

参数 描述
-l 输出主类的全名如果是执行的jar则输出jar的路径
-v 输出JVM启动时的JVM参数
-m 输出虚拟机进程启动时传递给主类main() 函数的参数

4.2.2 jstat虚拟机统计信息监视工具

命令格式jstat [options] vmid [interval [s|ms][count]] ]

命令格式jstat [-命令选项][vmid][间隔时间/毫秒][查询次数]

参数 功能作用
-class 监视类装载、卸载数量,以及总空间和装载耗时等
-gc 监视堆中 eden、 survivor 、老年代、元空间等空间大小和已使用情况GC次数、耗时等
-gcmetacapacity 元空间
-gcutil 与gc 类似,但是注重的是占比情况
-printcompilation 输出已经被JIT重新编译的方法
-gcoldcapacity 老年代统计信息
-gcnew 新生代
-gccause 与-gcutil相似但是会输出上一次GC的原因
1. gc 参数返回结果

使用-gc 命令; 为了能够更加直观在程序中设置了VM相关参数 然后运行、查看结果并分析。

参数 解析
S0C surivor(s0)区域大小
s1c s1区大小
S0U S0的使用大小
S1U S1的使用大小
EC eden可以使用的大小
EU eden已经使用
OC 老年代可以使用的大小
OU 老年代已经使用的带下
MC 元空间可以使用的大小
MU 元空间已经使用的大小
CCSC 压缩类空间大小
CCSU 压缩类已经使用大小
YGC 年轻代垃圾回收次数
YGCT 年轻代垃圾回收总耗时
FGC 老年代垃圾回收次数
FGCT 老年代垃圾回收总耗时
GCT 垃圾回收消耗总时间
2. class 参数返回结果

-class 参数; 监视类装载、卸载数量、总空间以及装载所耗费的时间。

参数 解析
Loaded 加载class的数量
Bytes 占用空间大小
Unloaded 未加载数量
Bytes 未加载占用空间
Time 时间
3. gcmetacapacity 参数返回结果

元数据空间统计(-gcmetacapacity)

参数 描述
MCMN 最小元数据容量
MCMX 最大元数据容量
MC 当前元数据空间大小
CCSMN 最小压缩类空间大小
CCSMX 最大压缩类空间大小
CCSC 当前压缩类空间大小
YGC 年轻代垃圾回收次数
FGC 老年代垃圾回收次数
FGCT 老年代垃圾回收消耗时间
GCT 垃圾回收消耗总时间

4.2.3 jinfoJava配置信息工具

命令格式jinfo [option] ipd

在控制台输入 jinfo ,则会提示相关命令参数,可借助提示执行相关命令。

  -flag <name>         to print the value of the named VM flag
  -flag [+|-]<name>    to enable or disable the named VM flag
  -flag <name>=<value> to set the named VM flag to the given value
  -flags               to print VM flags
  -sysprops            to print Java system properties
  <no option>          to print both of the above

4.2.4 jmapJava内存映像工具

jmapJVM Memory Map for java命令用于生成堆转储快照; 当然还可以使用-XX:HeapDumpOnOutOfMemoryError 参数可以让虚拟机在OOM异常之后自动生产dump文件。

选项 作用
-dump 生成Java堆转储快照。 格式 -dump:[live,]format=b,file=< filename >,其中live子参数说明是否只dump出存活的对象
finalizerinfo 显示F-Queue中等待Finalizer线程执行finalize方法的对象
-heap 显示java堆详细信息如使用哪种收集器、参数配置、分代状况等
-histo 显示堆中对象统计信息,包括类,实例数量、合计容量
-F 当虚拟机进程对 -dump选项没有响应时可使用这个选项强制生成dump快照

4.2.5 jhat虚拟机堆转储快照分析工具

可用 VisualVM 或者其他可视化分析工具代替

4.2.6 jstackJava堆栈跟踪工具

jstack [ option ] vmid

参数 作用
-F 当正常输出的请求不被响应时,强制输出线程堆栈
-l 除堆栈外,显示关于锁的附加信息
-m 如果调用本地方法的话,可以显示 c/C++的堆栈

4.3 JDK的可视化工具

官方工具1 JConsoleJava监视与管理控制台

官方工具2 VisualVM多合一故障处理工具

IDEA下推荐插件JProfiler

第十二章 Java内存模型


关于主内存与工作内存之间具体的交互协议,即一个变量如何从主内存拷贝到工作内存、 如何从工作内存同步回主内存之类的实现细节Java内存模型中定义了以下8种操作来完成

  1. lock锁定:作用于主内存的变量,它把一个变量标识为一条线程独占的状态。
  2. unlock解锁:作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
  3. read读取作用于主内存的变量它把一个变量的值从主内存传输到线程的工作内存中以便随后的load动作使用。
  4. load载入作用于工作内存的变量它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
  5. use使用:作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。
  6. assign赋值:作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  7. store存储作用于工作内存的变量它把工作内存中一个变量的值传送到主内存中以便随后的write操作使用。
  8. write写入作用于主内存的变量它把store操作从工作内存中得到的变量的值放入主内存的变量中。

Java内存模型还规定了在执行上述8种基本操作时必须满足如下规则

  1. 不允许read和load、 store和write操作之一单独出现即不允许一个变量从主内存读取了但工作内存不接受或者从工作内存发起回写了但主内存不接受的情况出现。
  2. 不允许一个线程丢弃它的最近的assign操作即变量在工作内存中改变了之后必须把该变化同步回主内存。
  3. 不允许一个线程无原因地没有发生过任何assign操作把数据从线程的工作内存同步回主内存中。
  4. 一个新的变量只能在主内存中“诞生”不允许在工作内存中直接使用一个未被初始化load或assign的变量换句话说就是对一个变量实施use、 store操作之前必须先执行过了assign和load操作。
  5. 一个变量在同一个时刻只允许一条线程对其进行lock操作但lock操作可以被同一条线程重复执行多次多次执行lock后只有执行相同次数的unlock操作变量才会被解锁。
  6. 如果对一个变量执行lock操作那将会清空工作内存中此变量的值在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值。
  7. 如果一个变量事先没有被lock操作锁定那就不允许对它执行unlock操作也不允许去unlock一个被其他线程锁定住的变量。
  8. 对一个变量执行unlock操作之前必须先把此变量同步回主内存中执行store、 write操作

先行发生原则:

  1. 程序次序规则Program Order Rule在一个线程内按照程序代码顺序书写在前面的操作先行发生于书写在后面的操作。 准确地说,应该是控制流顺序而不是程序代码顺序,因为要考虑分支、 循环等结构。
  2. 管程锁定规则Monitor Lock Rule一个unlock操作先行发生于后面对同一个锁的lock操作。 这里必须强调的是同一个锁,而“后面”是指时间上的先后顺序。
  3. volatile变量规则Volatile Variable Rule对一个volatile变量的写操作先行发生于后面对这个变量的读操作这里的“后面”同样是指时间上的先后顺序。
  4. 线程启动规则Thread Start RuleThread对象的start方法先行发生于此线程的每一个动作。
  5. 线程终止规则Thread Termination Rule线程中的所有操作都先行发生于对此线程的终止检测我们可以通过Thread.join方法结束、 Thread.isAlive的返回值等手段检测到线程已经终止执行。
  6. 线程中断规则Thread Interruption Rule对线程interrupt方法的调用先行发生于被中断线程的代码检测到中断事件的发生可以通过Thread.interrupted方法检测到是否有中断发生。
  7. 对象终结规则Finalizer Rule一个对象的初始化完成构造函数执行结束先行发生于它的finalize方法的开始。
  8. 传递性Transitivity如果操作A先行发生于操作B操作B先行发生于操作C那就可以得出操作A先行发生于操作C的结论。