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. 抢占式调度,而不是协同式调度变量
目录
相关文章
|
25天前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
46 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
安全 Java 容器
Happens-beofre 先行发生原则(JVM 规范)
Happens-beofre 先行发生原则(JVM 规范)
112 0
|
24天前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
35 4
|
3月前
|
Java Docker 索引
记录一次索引未建立、继而引发一系列的问题、包含索引创建失败、虚拟机中JVM虚拟机内存满的情况
这篇文章记录了作者在分布式微服务项目中遇到的一系列问题,起因是商品服务检索接口测试失败,原因是Elasticsearch索引未找到。文章详细描述了解决过程中遇到的几个关键问题:分词器的安装、Elasticsearch内存溢出的处理,以及最终成功创建`gulimall_product`索引的步骤。作者还分享了使用Postman测试接口的经历,并强调了问题解决过程中遇到的挑战和所花费的时间。
|
24天前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
50 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
27天前
|
存储 缓存 算法
JVM核心知识点整理(内存模型),收藏再看!
JVM核心知识点整理(内存模型),收藏再看!
JVM核心知识点整理(内存模型),收藏再看!
|
14天前
|
存储 算法 Java
聊聊jvm的内存结构, 以及各种结构的作用
【10月更文挑战第27天】JVM(Java虚拟机)的内存结构主要包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和运行时常量池。各部分协同工作,为Java程序提供高效稳定的内存管理和运行环境,确保程序的正常执行、数据存储和资源利用。
39 10
|
14天前
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
|
23天前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
45 2
|
27天前
|
Java API 对象存储
JVM进阶调优系列(2)字节面试:JVM内存区域怎么划分,分别有什么用?
本文详细解析了JVM类加载过程的关键步骤,包括加载验证、准备、解析和初始化等阶段,并介绍了元数据区、程序计数器、虚拟机栈、堆内存及本地方法栈的作用。通过本文,读者可以深入了解JVM的工作原理,理解类加载器的类型及其机制,并掌握类加载过程中各阶段的具体操作。