JVM 遇到的问题-1.0

JVM 遇到的问题-1.0

Q1. java 虚拟机规范定义的运行时数据区有几块?分别是什么?

A :一共有5块

B分别是:程序计数器,java 堆,java虚拟机栈,本地方法栈,方法区(jdk8之后改成元空间)。

⚠️ 注意事项:

1). 直接内存不属于运行时数据区;

2). jdk7之前方法区实现为永久代PermGen,jdk8之后彻底移除永久代,使用元空间。

3). 程序计时器是唯一没有OOM的内存区域。

Q2.直接内存是什么?又叫什么?

A: 直接内存又称为堆外内存。通过Unsafe向操作系统直接申请的内存,

典型 API:ByteBuffer.allocateDirect(),Netty 大量使用堆外缓冲区。存放 NIO 网络 IO 的数据缓冲区;不在 Java 堆中,不受-Xmx/-Xms限制。

⚠️ 注意事项:

  1. 堆上只会生成一个很小的DirectByteBuffer代理对象,真实数据存在堆外;
  2. 依靠Cleaner虚引用异步回收,极易发生堆外内存泄漏;
  3. 调控参数:-XX:MaxDirectMemorySize;OOM 报错:Direct buffer memory。

Q3:JVM 的组成 = 运行时数据区 + 直接内存?

A:这样说,表述不严谨,是不对的。JVM 完整的架构是五大子系统:

类加载子系统,运行时数据区,执行引擎,gc子系统,本地方法接口。

B: 从进程内存占用视角看,JVM 进程使用的内存包含:运行时数据区 + 直接内存。直接内存不属于 JVM 规范定义的运行时数据区,只是排查内存故障时需要一并分析。

Q4:如何系统掌握各区域 OOM 场景,并且动手复现?

1)熟记映射表:异常日志 → 定位内存区域

|------------------------------------|-----------|
| 报错信息 | 对应区域 |
| Java heap space | Java 堆 |
| Metaspace | 元空间 (方法区) |
| Direct buffer memory | 直接内存 |
| unable to create new native thread | 虚拟机栈 |
| StackOverflowError | 虚拟机栈 |

2)每一个 OOM Demo 强制配套 3 组 JVM 参数

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Plain Text # 1. GC日志输出 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log # 2. OOM自动dump堆快照 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=oom.hprof # 3. 限制对应区域内存,稳定复现 |

3)分层练习

① 基础 Demo 复现(while 循环简易案例)

② 模拟业务真实内存泄漏(ThreadLocal、静态缓存、动态类)

③ 使用 MAT 分析 hprof 快照,定位引用链,区分【内存泄漏 / 单纯内存不足】

Q5:OOM 发生之后,JVM 进程一定会退出吗?

A:不一定。

如果代码捕获 OOM 异常,当前线程终止,其他线程可继续运行;持续分配对象会反复抛出 OOM,进程不会直接死掉。

Q6:StackOverflowError 和 OOM 的本质区别?

A:

  • StackOverflowError:单个线程栈空间容量不足,方法调用层级过深;
  • unable to create new native thread OOM:系统资源不足,无法新建线程分配栈内存。

Q7:发生堆 OOM,一定是堆太小吗?

A:不一定。两种场景:

  1. 业务流量变大,对象总量上升 → 需要调大堆;
  2. 代码内存泄漏,无用对象持续占用 → 改代码,单纯调堆治标不治本。

Q8:为什么 Netty 优先使用直接内存?

A:减少一次内核缓冲区与 JVM 堆缓冲区的数据拷贝(减少一次内存复制,零拷贝思想,提升 IO 性能);代价是堆外内存管理复杂,容易泄漏。

Q9:程序计数器为什么不会产生 OOM?

A:每条线程的程序计数器只保存一条指令地址,内存占用极小;JVM 规范明确规定此区域不需要回收,不会出现内存溢出。

相关推荐
wuminyu16 小时前
Virtual Thread重投递至ForkJoinPool任务队列过程解析
java·linux·c语言·jvm·c++
2601_962071571 天前
类变量和全局变量的生命周期有什么区别?
java·开发语言·jvm
用户094248568032 天前
第23章:OpenJDK逃逸分析、标量替换与锁优化
java·jvm
wuminyu2 天前
LockStack在虚拟线程Mount和Unmount拷贝过程剖析
java·linux·c语言·jvm·c++
2601_962071572 天前
类变量和全局变量的隔离性有什么区别?
java·开发语言·jvm
估值探索者2 天前
【Python量化系统工程实战 #01】数据存储选型 CSVSQLiteMySQL 对比与 SQLite 实战建库
开发语言·jvm·python·sqlite·api接口·数据api接口·股票数据api接口
打工仔折腾 AI2 天前
从 Demo 到生产级 Agent:8 个关键设计机制与 Python 实现拆解
java·jvm·人工智能·后端·python·langchain·ai agent 实战
用户094248568033 天前
第22章:JIT编译分层——Interpreter、C1、C2与热点探测
java·jvm
乌暮3 天前
JVM 原理与实践:从运行时数据区到线上故障排查
java·开发语言·jvm·后端·学习·面试
长河3 天前
Java的GC(垃圾回收)中,标记无用对象的核心方法是可达性分析算法
java·jvm·算法