JVM 规范小结

简介: 一、基础 1. 与 Java 语言 JVM 与 Java 语言没有必然的联系,只与 class 文件格式有关联。 2. 与 .class 文件 class 文件包含了 Java 虚拟机指令集(字节码)和符号表,以及其他辅助信息。

一、基础

1. 与 Java 语言

JVM 与 Java 语言没有必然的联系,只与 class 文件格式有关联。

2. 与 .class 文件

  1. class 文件包含了 Java 虚拟机指令集(字节码)和符号表,以及其他辅助信息。
  2. JVM 在 class 文件中施加了许多强制性语法和结构化约束。
  3. 凡是能用 class 文件正确表达出来的编程语言都可以在 JVM 中执行。

3. 数据类型

  1. 原始类型(基本类型),如 int、double、long <---> 原始值
  2. 引用类型,如 reference <---> 引用值

JVM 希望类型检查能在程序运行前(编译阶段)完成。

(1)基本类型

① 整数类型
  1. byte 8位有符号二进制补码整数,默认值为0(下面3个也是)
  2. short 16位有...
  3. int 32位有...
  4. long 64位有...
  5. char 16位无符号整数表示的、指向基本多文种平面(BMP)的 Unicode 码点,UTF-16 编码,默认为 null('u0000')
② 浮点数类型
  1. float 32位单精度(IEEE 754标准)
  2. double 64位单精度(IEEE 754标准)
  3. 五个特殊数值:正数0、负数0、正无穷大、负无穷大、NaN
  4. 除了 NaN 之外,其他值都是 有序的
  5. 有且仅有一个值 NaN 与自身比较返回 false
  6. 任何数字与 NaN 比较都会返回 true
③ returnAddress

值指向虚拟机一个地址

④ boolean

在编译之后一般使用 int 数据类型代替

(2)引用类型

  1. class type
  2. array type
  3. interface type

数据最外维是 组件类型,最里面维度称为 元素类型。如List,其中List是组件类型,Integer是元素类型。

顺便一提,Integer具有一个缓冲池 -128~127,默认情况下直接从池中取值(取的值相等则变量地址相同),除非 new Integer(10) 或者取超出范围的值。

4. 运行时数据区

(1)pc 寄存器

  1. 每个 JVM 线程都有自己的 pc 寄存器
  2. 每个线程只执行一个方法
  3. 容量至少应当能保存一个 returnAddress 类型的数据或者一个与平台相关的本地指针的值

(2)虚拟机栈

  1. StackOverflowError 线程请求分配的栈容量超过 JVM 栈允许的最大容量
  2. OutOfMemoryError 栈可以动态扩展,在尝试扩展时无法申请到足够的内存

(3)堆

  1. 是可供各个线程共享的 运行时内存区域
  2. 是供所有类实例和数组对象 分配内存 的区域
  3. 所使用的内存,不需要 保证是 连续
  4. 抛出OutOfMemoryError

(4)方法区

  1. 是可供各个线程共享的 运行时内存区域
  2. 包括【 运行时常量池,字段和方法数据,构造函数和普通方法的字节码内容,在类、实例、接口初始化时用到的特殊方法】
  3. 抛出OutOfMemoryError

(5)运行时常量池

  1. class 文件中每一个类或接口的常量池表
  2. 抛出OutOfMemoryError
(6)本地方法栈
  1. 支持 native 的执行(其他语言编写的方法)
  2. 抛出StackOverflowError
  3. 抛出OutOfMemoreyError

5. 帧栈

  1. 存储数据和部分过程结果的数据结构
  2. 也用来处理动态链接、方法返回值和异常分派
  3. 随着方法调用而创建,随着方法结束而销毁
  4. 为程序提供调试功能
  5. 当前栈帧 - 当前类 - 当前方法

(1)局部变量表

  1. 索引访问
  2. long 和 double 的值占用两个连续的局部变量

(2)操作数栈

  1. long 和 double 占用两个单位的栈深度

(3)动态链接

  1. 每个栈帧都包含一个指向当前方法所在类型的运行时常量池的引用
  2. 晚期绑定?

6. 浮点运算

(1)JVM 的浮点操作

  1. 遇到被0除、上下限溢出和非精确时,不会抛出错误
  2. 不支持 IEEE 754 的信号浮点比较
  3. 舍入操作:向最接近数舍入模式,如果无法精确,则舍入到最低有效位为 0 的那个值
  4. 浮点数值 -> 整型数值:向零舍入
  5. 不支持 IEEE 754 的单精度扩展和双精度扩展格式

(2)浮点模式

  1. 每个方法都有这项属性
  2. 分为 FP-strict 模式、非FP-strict模式
  3. 体现在 class 文件的方法 method_info 结构的访问标志 access_flags 中的 ACC_STRICT 标志位
  4. JDk 1.1以及之前版本的编译器是 非FP-strict模式

(3)数值集合转换

  1. 支持扩展指数集合的 JVM 实现数值在标准浮点数集合与扩展指数集合之间饿映射关系是允许或必要的
  2. float => 单精度浮点数集合中的元素
  3. double => 双精度浮点数集合中的元素

7. 特殊方法

(1) 构造方法(初始化方法)

(2)签名多态性

  1. 由 java.lang.invoke.MethodHandle 类进行声明
  2. 只有一个类型为 Object[] 的形参
  3. 返回值为 Object
  4. ACC_VARARGS 和 ACC_NATIVE 标志被设置

8. 异常

(1)同步异常

  1. athrow 字节码指令被执行
  2. 虚拟机同步检测到程序发生非正常的执行情况

(2)异步异常

  1. 调用了 Thread 或者 ThreadGroup 的 stop 方法
  2. JVM 实现发生了内部错误
  3. 每个执行的方法都配备0~多个异常处理器

9. 类库

需要JVM特殊支持的类

  1. 反射:java.lang.reflect、Class
  2. 加载和创建类或接口的类:ClassLoader
  3. 连接和初始化类或接口:ClassLoader
  4. 安全:java.security
  5. 多线程:Thread
  6. 弱引用:java.lang.ref

10. 公有设计、私有实现

  1. == 统一设计、各自实现

二、垃圾收集器与内存分配策略

1. 判断对象存活情况

(1)引用计数算法

  1. 优点:判定效率高,实现简单
  2. 缺点:难以解决对象之间相互引用的问题
  3. JVM 不使用

(2)可达性分析算法

  1. 特点:GC Roots

2. 垃圾收集算法

(1)标记 - 清除算法

  1. 缺点:效率不高,产生大量不连续的碎片

(2)复制算法

  1. 特点:内存空间划分成两块
  2. 缺点:内存空间缩小一半,对象存活率较高时效率变低;不适用于老年代。

(3)标记 - 整理算法

  1. 特点:向一端移动,直接清理端边界以外的内存

(4)分代回收算法

  1. 新生代:复制算法
  2. 老年代:标记 - 清理/整理

3. HotSpot 算法实现

  1. 核心:根节点枚举,数据结构采用 OopMap
  2. 在特定位置(安全点 SafePoint)记录
  3. 安全区域(SafeRegion):在一端代码片段之中,引用关系不会发生变化。在这个区域中的任一地方开始 GC 都是安全的

4. 垃圾回收器

  1. 新生代:Serial、ParNew、Parallel Scavenge
  2. 老年代:CMS、Serial Old(MSC)、Parallel Old
  3. 任意代:G1
  • Serial 单线程
  • ParNew 多线程版本的 Serial

5. CMS 收集器

以获取最短回收停顿时间为目标的收集器;常用在互联网或者 B/S 系统的服务端上。【注重服务端的响应速度,希望系统停顿时间最短】

过程

  1. 初始标记 stw
  2. 并发标记
  3. 重新标记 stw
  4. 并发清除

缺点一:对 CPU 资源敏感

i-CMS 增量式并发收集器,适用于CPU数量过少时,收集器线程资源占用率高的情况(已被 deprecated,效率低,不提倡使用)

缺点二:无法处理浮动垃圾

缺点三:使用标记 - 清除算法

6. G1 收集器

面向服务端

  1. 并行、并发
  2. 分代收集
  3. 空间整合
  4. 可预测的停顿,建立可预测的停顿时间模型

过程

  1. 初始标记
  2. 并发标记
  3. 最终标记
  4. 筛选回收

7. 内存分配与回收策略

  1. 对象优先在新生代 Eden区分配,无空间时发起一次 Minor GC
  2. 大对象直接进入老年代
  3. 长期存活的对象将进入老年代(年龄计数器)
  4. 动态对象年龄判断(年龄相对较大直接进入老年代)
  5. 空间分配担保(老年代有足够空间容纳新生代)

8. 关于GC

  1. 新生代:Minor GC
  2. 老年代:Major GC 或 Full GC

三、内存模型与线程

优化

  1. 指令重排序

内存间交互操作

  1. lock
  2. unlock
  3. use
  4. assign
  5. load
  6. store
  7. read
  8. write

volatile

  1. 所有线程可见
  2. 禁止指令重排序优化(机器指令增加 lock)

先行发生关系

  1. 程序次序规则
  2. 管程锁定规则
  3. volatile 变量规则
  4. 线程启动规则
  5. 线程终止规则
  6. 线程中断规则
  7. 对象终结规则
  8. 传递性

线程实现

  1. 内核线程(需要系统调用:系统态<->用户态)
  2. 用户线程
  3. 用户线程 + 轻量级进程
  4. 抢占式调度,而不是协同式调度变量
目录
相关文章
|
2月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
62 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
安全 Java 容器
Happens-beofre 先行发生原则(JVM 规范)
Happens-beofre 先行发生原则(JVM 规范)
115 0
|
1月前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
291 1
|
2月前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
44 4
|
2天前
|
存储 Java 程序员
【JVM】——JVM运行机制、类加载机制、内存划分
JVM运行机制,堆栈,程序计数器,元数据区,JVM加载机制,双亲委派模型
|
22天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
1月前
|
Java
JVM内存参数
-Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80
|
1月前
|
Java
JVM运行时数据区(内存结构)
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一 (3)程序计数器:保存指令执行的地址,方便线程切回后能继续执行代码
22 3
|
1月前
|
存储 缓存 监控
Elasticsearch集群JVM调优堆外内存
Elasticsearch集群JVM调优堆外内存
50 1
|
1月前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。