当前位置: 首页 > news >正文

JVM虚拟机

 jvm是java的运行环境(java二进制字节码运行环境)

JVM虚拟机的组成

JVM(Java Virtual Machine)是Java程序运行的核心,主要由以下几个模块组成:

  1. 类加载子系统(ClassLoader Subsystem)
    负责加载、验证、准备、解析和初始化类文件(.class文件),将字节码转换为JVM可执行的格式。

  2. 运行时数据区(Runtime Data Areas)
    包括方法区(Method Area)、堆(Heap)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)、程序计数器(Program Counter Register)。

  3. 执行引擎(Execution Engine)
    负责解释或编译字节码为机器码并执行,包含解释器(Interpreter)、即时编译器(JIT Compiler)和垃圾回收器(Garbage Collector)。

  4. 本地方法接口(JNI, Java Native Interface)
    提供调用本地方法(如C/C++)的接口。

  5. 本地方法库(Native Method Libraries)
    存放本地方法的具体实现。


程序计数器

程序计数器是线程私有的(每个线程都有一份,没有线程安全问题)小块内存,记录当前线程执行的字节码指令地址。程序计数器是唯一不会抛出OutOfMemoryError(内存溢出)的内存区域,它的存在保证了线程切换后能恢复到正确的执行位置。


java堆

java堆是线程共享的(有线程安全问题),用于存放对象实例和数组。几乎所有对象的实例都在堆上分配内存。是垃圾收集器管理的主要区域。内存不够抛出OOM异常。

堆可以分为新生代(Young Generation)和老年代(Old Generation)。

新生代(Young Generation)进一步细分为Eden区和2个Survivor区。

新创建的对象通常会被分配在新生代的Eden区。Eden区是对象最初分配的区域,大多数对象生命周期较短,很快会被垃圾回收。

如果垃圾处理后还存活的对象会被移动到Survivor区(S0或S1)。

每次垃圾处理后,存活的对象的年龄(age)会增长,长到一定阈值就会被放到老年代。

老年代存放长期存活的对象。

元空间/方法区:存放类的结构信息,如类名、方法、字段、字节码、常量池等,取代了早期版本堆中的永久代,为了让堆节省内存空间,防止内存溢出(OOM)。

堆内存溢出报错:OutOfMemoryError


java虚拟机栈

先进后处。每个线程运行时所需要的内存就是java虚拟机栈,每个线程都有自己独立的虚拟机栈(线程私有的)。每个栈有多个栈帧组成(栈帧包含局部变量表、操作数栈、动态链接、方法返回地址等信息)。每个线程只有一个活动栈帧,​​一个方法调用对应一个栈帧​​。

栈内存溢出报错:StackOverflowError

方法内的局部变量是否线程安全?

如果方法内局部变量没有逃离方法的作用范围,它是线程安全的如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全。


方法区/元空间

内存共享的,存放类的结构信息,如类名、方法、字段、字节码、常量池等,取代了早期版本堆中的永久代,为了让堆节省内存空间,防止内存溢出(OOM)。

元空间内存不足报错:OutOfMemoryError : Matespace

常量池

常量池(Constant Pool)是JVM方法区(元空间)中的一个重要组成部分,用于存储编译期生成的各种字面量和符号引用

查看字节码结构(类的基本信息,常量池,方法定义)

编译代码,在命令窗口执行:javap -v  字节码文件名

运行时常量池

 当类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址。

类加载器

作用:就是将字节码文件加载到JVM中

JVM内置的类加载器类型

1. 启动类加载器(Bootstrap ClassLoader)

  • 由C++实现(非Java类)
  • 加载<JAVA_HOME>/lib下的核心类库
  • 唯一没有父加载器的加载器

2. 扩展类加载器(Extension ClassLoader)

  • sun.misc.Launcher$ExtClassLoader实现
  • 加载<JAVA_HOME>/lib/ext目录的类
  • 父加载器是Bootstrap

3. 应用程序类加载器(Application/System ClassLoader)

  • sun.misc.Launcher$AppClassLoader实现
  • 加载classpath指定的类
  • 父加载器是Extension

4. 自定义类加载器

开发者可以继承java.lang.ClassLoader创建自己的加载器,典型用途:

  • 实现热部署
  • 加载非标准来源的类
  • 类隔离(如Tomcat的Web应用隔离)

类加载器双亲委派机制

加载某一个类,(自己先不加载)先委托上一级的加载器进行加载,如果上级加载器也有上级,则会继续向上委托,如果该类委托上级没有被加载,(那就往下加载)子加载器尝试加载该类

JVM为什么使用类加载器双亲委派机制?

避免类重复加载

为了安全,保证类库API不会被修改。

类装载的步骤

  1. 加载:查找和导入class文件
  2. 验证:保证加载类的准确性
  3. 准备:为类变量分配内存并设置类变量初始值
  4. 解析:把类中的符号引用转换为直接引用
  5. 初始化:对类的静态变量,静态代码块执行初始化操作
  6. 使用:JVM 开始从入口方法开始执行用户的程序代码
  7. 卸载:当用户程序代码执行完毕后,JVM便开始销毁创建的Class对象

垃圾回收机制

垃圾回收主要针对中的对象。

对象什么时候被垃圾回收机制回收?

如果一个对象没有任何的引用和指向它,那它就是垃圾,它就可能被回收

如何定位垃圾

引用计数法:对象每被引用一次,引用次数+1,引用次数为0,代表可被回收。循环引用可能出现问题。

可达性分析算法(主要):扫描堆中的对象,看是否能够沿着 GC Root 对象 为起点的引用链找到该对象,找不到,表示可以回收。

哪些对象可以作为GC Root?

垃圾回收算法

标记清除算法用的少,有内存碎片

下面俩算法用的多。

jvm

jvm常见调优参数

  • 设置堆空间大小
    • -Xms:2048m     # 初始堆大小(默认物理内存1/64)
    • -Xmx:2048m     # 最大堆大小(默认物理内存1/4)
  • 虚拟机栈的设置
    • -Xss256k    # 默认1MB(Linux/x64),Web应用建议256k-512k
  • 年轻代中Eden区和两个Survivor区的大小比例
    • -XX:SurvivorRatio=8        # 默认8表示 Eden:Survivor=8:1:1
    • -XX:NewRatio=<ratio>       # 老年代/年轻代的比例
  • 年轻代晋升老年代阈值
    • -XX:MaxTenuringThreshold=15      # 默认15(CMS下6)
  • 设置垃圾回收收集器

jvm调优工具

命令行工具,JDK自带的:

  • jsp:列出所有 Java 进程(PID 和主类名)
  • jstack:生成线程快照(检查死锁、线程阻塞等问题)
  • jmap:查看堆内存分布
  • jstat:实时监控 GC 情况(堆内存、GC 次数/时间等),1000ms 刷新一次

可视化工具:

  • JConsoleJDK 自带,基础监控(内存/线程/类加载/MBean),支持 JMX 连接。在JDK的bin目录下的exe文件
  • ​​VisualVM​:JDK 自带,支持 CPU/内存监控、线程分析、堆转储分析、JMX 连接远程服务器

java内存泄露问题如如何排查

内存泄露主要指堆内存

  • 堆内存溢出报错:OutOfMemoryError

报错原因:一些比较大的对象一直存活,一直没有被垃圾处理机制处理

  • 栈内存溢出报错:StackOverflowError

报错原因:递归问题

  • 元空间/方法区内存不足报错:OutOfMemoryError : Matespace

报错原因:动态加载的类太多了

运行闪退,运行一段时间就宕机如何排查

  1. 获取堆内存快照dump,使用jmap,只能在项目运行的时候用
  2. VisualVM去分析dump文件
  3. 通过查看堆信息的情况,定位内存溢出问题

CPU飙高的排查方案与思路

  1. 使用Linux的top命令查看整体CPU使用情况,找到占用高的进程,拿到进程ID。
  2. 获取进程中的线程:使用ps命令,找到进程中哪个线程占用高。将线程id10进制转16进制备用。
  3. 使用jstack  [进程ID] 查看该线程。jstack [进程ID] 命令会生成​该Java进程内所有线程​​的详细日志,日志里的线程id是16进制的。
  4. 到jstack 日志中找到该线程(需要10进制转16进制),日志里应该会报错误原因。

http://www.xdnf.cn/news/639469.html

相关文章:

  • 在vue中重复组件导入简化方案
  • 2025年5月25日第一轮
  • 数据结构之图结构
  • vllm 2080TI ubuntu环境安装
  • PySide6 GUI 学习笔记——常用类及控件使用方法(常用类图标QIcon)
  • 【大模型应用开发】Qwen2.5-VL-3B识别视频
  • boost之preprocessor
  • 人工智能(AI)技术包括哪些技术
  • TCP 协议的相关特性
  • How to balance work and personal life?
  • 前端流行框架Vue3教程:28. Vue应用
  • PostgreSQL windows安装
  • Vue3编译器:静态提升原理
  • VBox共享文件夹
  • 2025一带一路暨金砖国家技能发展与技术创新大赛第三届企业信息系统安全赛项
  • Go语言--语法基础5--基本数据类型--循环语句
  • [ACTF新生赛2020]easyre
  • Bolt.new:重塑 Web 开发格局的 AI 利器
  • MFC:获取所有打印机的名称(打印机模块-2)
  • 【Siggraph Asia 2023】低光增强Diffusion-Low-Light-main(引入diffusion与DWT) -- part1论文精读
  • AutoGen SelectorGroupChat 示例:社会热搜话题事件榜单
  • 成功解决ImportError: cannot import name ‘DTensor‘ from ‘torch.distributed.tensor‘
  • 选择排序算法研究
  • 【NIPS 2024】Towards Robust Multimodal Sentiment Analysis with Incomplete Data
  • C++异步(1)
  • [Protobuf] 快速上手:安全高效的序列化指南
  • SymAgent:一种用于知识图谱复杂推理的神经符号自学Agent框架
  • Oracle中的[行转列]与[列转行]
  • 【目标检测】【医学图像目标检测】BGF-YOLO:脑肿瘤检测的多尺度注意力特征融合
  • 【linux】systemctl基本语法