ZHE知识库 ZHE知识库
首页
JAVA
中间件
易学
小说漫画
其他
Source (opens new window)
首页
JAVA
中间件
易学
小说漫画
其他
Source (opens new window)
  • JUC入门笔记
  • JVM入门笔记
    • JVM基础概念
    • 字节码文件
    • 类的生命周期
      • 加载阶段
      • 链接阶段
      • 初始化阶段
    • 内存结构
      • 程序计数器
      • 虚拟机栈
      • 本地方法栈
      • 堆
      • 方法区
  • Java微服务

  • Netty笔记
  • Java
张涵哲
2023-12-06
目录

JVM入门笔记

# JVM基础概念

JVM 全称是 Java Virtual Machine 翻译过来是 Java 虚拟机,用于执行 Java 字节码文件,顾名思义它是一个专属于 Java 的虚拟机程序,任何可以安装 JDK 的操作系统都可以执行 Java 程序,这是就是 Java 的跨平台

JVM 主要有四部分组成,它的作用执行字节码文件,所以要先使用类加载器将字节码文件读取并保存到运行时数据区(JVM 的内存),保存到内存后由执行引擎执行 Java 代码,除了这三个之外 JVM 中还包含本地接口,内部封装着 C/C++ 语言编写的代码,Java 的源码中带有 native 关键字的方法调用的就是本地接口中的方法

JIT即时编译

其他编程语言例如 C/C++ 编写的程序会编译为计算机可识别的机器码,也就是直接编译成可执行文件(例如exe),而 Java 编译后的 Class 文件并不是可执行文件,需要 JVM 将它解释为机器码文件才可执行,中间多了一个步骤就导致 Java 程序的执行效率会低一些,JIT 即时编译 就是这个问题的解决方案,通过对程序运行状态进行分析,将高频执行的代码解释为机器码后保存到内存中,节省了反复解释造成的多余的性能开销

# 字节码文件

JVM 的作用是执行字节码文件,字节码并不支持直接用文本编辑器打开浏览,这里介绍三种方案:

  1. 可用 JDK 自带的javap -v xxx.class在控制台上查看,线上环境可通过jar -xvf xxx.jar解压 jar 包后查看
  2. 可以使用 jclasslib (opens new window) 图形化工具查看字节码文件,相比控制台查看更方便
  3. 使用阿里巴巴的 arthas.jar (opens new window) 运维工具,在线上动态查看正在运行的 jar 包,以及动态替换字节码文件等等
    • dashboard -i 5000 -n 10,查看程序数据面板,每个 5 秒刷新一次,刷新 10 次后结束
    • dump -d /data/java/ site.hanzhe.Application,将 Application 这个字节码文件保存到指定目录下
    • jad /data/java/ site.hanzhe.Application,反编译该字节码文件,查看 Java 源代码
    • 更多命令参数详查官方文档 (opens new window)

Java 的 .class 字节码文件大致由五部分组成:

  1. 基础信息:包含魔数、字节码文件对应的 Java 版本号、访问标识符、父类和接口等等信息等等
  2. 常量池:保存字符串常亮、类或接口名、字段名等等
  3. 字段:当前类中声明的字段信息
  4. 方法:当前类中方法的字节码指令信息
  5. 属性:类源码的文件名、包含哪些内部类等等

Magic 魔数

一个文件的扩展名并不能决定文件的类型,因为扩展名可以随意修改,通常来说程序识别文件类型会根据该文件的前几个字节来判断,例如 PNG 图片开头四个字节为89 50 4E 47,在 UTF-8 码表中对应字母 PNG

Java 字节码文件中前四个字节为CA FE BA BE为“咖啡宝贝”的谐音,这是文件头进制数,用于区分文件类型,也成为魔数

主副版本号

在 jclasslib 主界面的一般信息中可以看到主版本号和副版本号,副版本号用来标识小版本这个不重要

主版本号用来标识大版本,JDK1.2 的版本号为46,之后每升级一次版本号就递增,用版本号减去 44 就能得到 JDK 版本

  • 46 - 44 = 2,JDK 版本为 1.2
  • 52 - 44 = 8,JDK 版本为 1.8

记录一些常见的 JVM 指令,随时补充

指令 解释
iconst_值 将值放到操作数栈中
istore_下标 将操作数栈中的值存放到对应下标的变量中
iload_下标 将对应下标的值读取到操作数栈中
iadd 操作数栈中的值相加
iinc 下标 by 值 将指定下标变量的值进行增加,by 后面的值是多少就加多少

# 类的生命周期

类的生命周期主要分为五个阶段:加载 → 链接 → 初始化 → 使用 → 卸载

# 加载阶段

类加载器会读取读取字节码文件将它保存到 JVM 的内存中,这里分别用到了 JVM 内存中的方法区和堆内存,类的基础信息会在方法区中保存为一个 InstanceKlass 对象,同时在堆内存中保存一个 class 对象,类中的静态变量就是存放在堆内存中的

Java 安装目录下的 lib 目录中有个sa-jdi.jar文件,文件中提供了 HSDB 调试工具可以查看内存信息,通过jsp命令可以获取到运行的 Java 进程 ID,在 Tools/ObjectHistogram 菜单中根据类的权限定名可以看到该类的内存信息

启动 HSDB 工具的命令:java -cp sa-jdi.jar sun.jvm.hotspot.HSDB

# 链接阶段

链接阶段由三部分组成:验证 准备 解析

验证:校验字节码是否符合规范,例如是否为正确的字节码文件,版本是否对应,指令是否正确等等

准备:为当前类中所有的静态变量设置初始值,如果是被 final 修饰的静态变量则直接设置正确的值

解析:将字节码中的符号引用,替换为直引用

# 初始化阶段

# 内存结构

image-20231211191701025

# 程序计数器

程序计数器,英文名是 Program Counter Register,是线程私有内存,基于寄存器实现,主要作用是记录下一个要执行的 JVM 指令,具体体现为:

  • 当线程执行过程中 CPU 离开后重新调度时,能够接着上次执行的位置继续向下执行
  • 当方法 a 调用方法 b 时,b 方法执行结束后能回到调度的位置,继续向下执行

# 虚拟机栈

栈内存,又被称为虚拟机栈,英文名是 JVM Stacks,为线程工作提供运行空间是线程私有内存

  • 栈内存中主要用来存储栈帧,例如方法调用为a→b→c每个方法对应一个栈帧,三个栈帧依次入栈,先进后出
  • 栈帧中存储方法运行中产生的一些数据,例如局部变量
  • 方法嵌套调用过多或者错误的递归调用容易导致Stack Overflow栈内存溢出
  • 栈内存大小默认为1MB,可以通过 Xss 设置栈内存大小,例如-Xss2048k,需要注意的是物理机内存是有容量限制的,栈内存分配的空间越大那么可创建的线程数量就越少,分配内存前须谨慎考虑

CPU高占用检测方案

  1. 使用top命令查看 Java 项目对应的 PID
  2. 使用ps H -eo pid,tid,%CPU,%MEM | grep PID查看该进程的所有线程 ID
  3. 将线程ID(TID)转十六进制后,使用jstack命令查看线程信息

# 本地方法栈

本地方法栈,英文名是 Native Method Stacks,为本地方法提供运行空间是线程私有内存

本地方法栈的作用与虚拟机栈类似,不同的是本地方法栈为本地方法工作,指的是 Java 源码中常见的使用native关键字的代码,JVM 通过本地方法实现执行 C/C++ 的代码

# 堆

堆内存,英文名是 Heap,是线程共享内存,通过 new 关键字创建的对象都使用堆内存,有垃圾回收机制不定期清理内存

  • 堆内存没有默认大小,可以通过-Xms设置堆初始内存大小,-Xmx设置堆最大内存大小

堆内存诊断工具

  1. jmap -heap PID
  2. jconsole可视化工具,通过PID连接进程进行监控
  3. jvisualvm可视化工具,通过PID连接进程进行监控,高版本 JDK 没有内置该工具,需要手动下载 (opens new window)

# 方法区

方法区,英文名Method Area,是线程共享内存,主要存储类的结构信息,例如变量,方法以及构造等信息

上次更新: 2025-01-14, 15:51:56
JUC入门笔记
服务发现

← JUC入门笔记 服务发现→

Theme by Vdoing | Copyright © 2023-2025 Zhe | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式