深入理解java虚拟机-第三版-梳理

java内存区域 - 运行时数据区域

程序计数器

可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条要执行的指令。线程私有

java栈

java虚拟机栈描述的是java方法执行的线程内存模型。每个方法被执行的时候,java虚拟机都会创建一个用于存储局部变量表、操作数、动态链接、方法出口。java命令行指定的栈的大小的配置参数是 -Xss,默认数值是1M。
如下图,一个方法对应的栈帧的执行情况
20200627114236
20200627114650

本地方法栈

专门服务于native方法的

java堆

所有的对象实例以及数据都应当在堆上分配。垃圾收集器管理的就是这个区。java命令行指定堆大小的配置参数是 -Xmx、-Xms,-Xmx:堆最大值 -Xms:堆初始值。线程共享

方法区

用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存。运行时常量池也是方法区的一部分

静态常量池

静态常量池是Class类文件结构的一部分。主要存放字面量(Literal)和符号引用(Symbolic Referneces)。字面量比较接近java语言的常量概念,如文本字符串,final的常量值等。而符号引用主要包括以下常量:

  • 被模块导出或者开放的包(package)
  • 类和接口的全限定名
  • 字段的名称和描述符
  • 方法的名称和描述符
  • 方法句柄和方法类型
  • 动态调用点和动态常量
    静态常量池中的每个常量都是一个表。

运行时常量池

运行时常量池也是方法区的一部分。在类加载后它会把字节码的静态常量池的各种字面量和符号引用都存放到运行时常量池中

java对象的内存布局

  1. 对象头
    • Mark Word
      20200626231055
    • 类型指针
      即对象指向它的类型元数据的指针
  2. 实例数据
  3. 对齐填充
    可以使用 jol 真实查看一个对象的内存布局

java垃圾收集

垃圾回收三问

  • 哪些内存(对象)需要回收
  • 什么时候回收
  • 如何回收

对象已死?- 对象存活判定算法

判断对象已死的方式

  • 引用计数算法
    缺点:ABA循环引用
  • 可达性分析算法
    固定可作为GC Roots的对象如下几种
  • 虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • native方法
  • 所有被同步锁持有的对象

垃圾收集算法

  1. 标记-清除算法
    缺点:执行效率不稳定;内存空间碎片化问题
    使用的垃圾收集器有CMS
  2. 标记–复制算法
    缺点:可用内存缩小了原来的一半
    使用的垃圾收集器有 todo
  3. 标记-整理算法
    缺点:回收时间拉长,暂停用户线程
    使用的垃圾收集器有 Parallel Scavenge

垃圾收集器

Serial收集器

新生代收集器,采用标记-复制算法,简单而高效,对于资源受限的环境,内存消耗最小的

ParNew收集器

新生代收集器,采用标记-复制算法,Serial收集器的并发版本,只有它与CMS收集器一起使用。使用-XX:+/-UseParNewGC选项来强制指定或者禁止

Parallel Scavenge收集器

新生代收集器,采用标记-复制算法。它关注的目标是吞吐量

Serial Old收集器

老年代收集器,采用标记-整理算法。它是Serial收集器的老年代版本

Parallel Old收集器

老年代收集器,采用标记-整理算法。Parallel Scavenge收集器的老年代版本。吞吐量优先

CMS收集器

老年代收集器,采用标记-清除算法。以获取最短回收停顿时间为目标。并发收集,低停顿。真正意义上的支持并发的收集器,垃圾收集线程与用户线程同时工作。-XX:UseConcMarkSweepGC

Garbage First收集器

G1收集器是垃圾收集器技术发展历史上里程碑式的成果,开创了收集器面向局部收集的设计思路和基于Region的内存布局方式

Seenandoah收集器

ZGC收集器

垃圾收集器日志

有关垃圾收集日志的命令行参数

  1. -Dlogging.file=/var/logs/yyy.log
  2. 查看GC基本信息,JDK9之前使用-XX:+PrintGC,JDK9之后使用-Xlog:gc
  3. 查看GC详细信息,JDK9之前使用-XX:+PrintGCDetail,JDK9之后使用-X-log:gc*
  4. 查看GC的时间戳,-XX:PrintGCDateStamps

    垃圾收集日志说明

    #####
    这里我们使用的是CMS收集器
  • 实例代码 Minor gc

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public class GCTest {
    // JVM Option argv: -XX:+UseConcMarkSweepGC -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:+PrintGCDateStamps
    private static final int _1MB = 1024 * 1024;

    public static void testAllocation() {
    final byte[] allocation1;
    final byte[] allocation2;
    final byte[] allocation3;
    final byte[] allocation4;

    allocation1 = new byte[2 * _1MB];
    allocation2 = new byte[2 * _1MB];
    allocation3 = new byte[2 * _1MB];
    allocation4 = new byte[4 * _1MB]; // 出现一次Minor gc

    }
    public static void main(final String[] args) {
    testAllocation();
    }
    }

    2020-06-28T21:52:03.639-0800: [GC (Allocation Failure) 2020-06-28T21:52:03.639-0800: [ParNew: 7296K->1023K(9216K), 0.0019312 secs] 7296K->3107K(19456K), 0.0019794 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
    2020-06-28T21:52:03.643-0800: [GC (Allocation Failure) 2020-06-28T21:52:03.643-0800: [ParNew: 5276K->0K(9216K), 0.0040968 secs] 7359K->6982K(19456K), 0.0041333 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
    2020-06-28T21:52:03.648-0800: [GC (CMS Initial Mark) [1 CMS-initial-mark: 6982K(10240K)] 11078K(19456K), 0.0001458 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
    2020-06-28T21:52:03.648-0800: [CMS-concurrent-mark-start]
    Heap
    par new generation total 9216K, used 4178K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
    eden space 8192K, 51% used [0x00000007bec00000, 0x00000007bf014930, 0x00000007bf400000)
    from space 1024K, 0% used [0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000)
    to space 1024K, 0% used [0x00000007bf500000, 0x00000007bf500000, 0x00000007bf600000)
    concurrent mark-sweep generation total 10240K, used 6982K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
    Metaspace used 3351K, capacity 4496K, committed 4864K, reserved 1056768K
    class space used 368K, capacity 388K, committed 512K, reserved 1048576K
  • 日志说明

    对象内存分配与回收策略

jvm性能与优化

jvm性能监控与故障处理工具

jps

jstat

jinfo

jmap

jhat

jstack

jconsole

jvisualVm

jmc

jvm性能调优

jvm即时编译器

20200705233114

类文件结构

jvm类加载机制

java内存模型

主要目的:定义程序中各种变量的访问规则。即关注在虚拟机中把变量值存储在内存和从内存中取出变量值。
内存模型规定了所有的变量都存储在主内存,每条线程还有自己的工作内存,工作内存中保存了被该线程使用的变量的主内存副本。线程对变量的所有操作都要在工作内存中进行,不能直接读写主内存中的数据

20200705233246

volatile

第一个语义是可见性。保证此变量对所有线程的可见性,这里的“可见性”是指当一条线程修改了这个变量的值,新值对于其他线程来说可以立即得知,即volatile保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新
第二个语义是禁止指令重排序优化

volatile只保证可见性,不保证原子性

先行发生(Happens-Before)原则

七个先行发生原则