00-总结
2026/9/27大约 4 分钟
什么情况下进行jvm调优
- 频繁的进行垃圾回收,STW时间长
- CPU异常飙升
- 出现OOM
CPU飙升如何定位
- 第一步定位进程: 通过命令
top查询各个进程cup占用率的,找到占用高的进程的pid, - 第二步定位线程:
- 先通过命令
top -H -p 进程pid查看进程中cpu占用率高的线程id,这里看的线程id是十进制 - 然后
printf "%x\n" 线程id十进制将进程id是十进制转为十六进制。
- 先通过命令
- 第三步:定位代码
- 通过命令
jstack 进程pid > thread_dump.log获取进程中所有线程快照文件hread_dump.log,然后查询hread_dump.log文件中grep -A 20 线程id的十六进制 thread_dump.log线程的日志信息 。 - 或者直接通过
jstack 进程pid | grep 线程id的十六进制 -A 50打印的日志信息就能查到是那段代码。
- 通过命令
内存飙高如何定位
- 情况:一种是创建的了大量的对象导致的,一种是内存泄漏导致。
- 第一步定位进程:通过命令
top,然后按一下m键改为按内存排序默认按cpu排序。 - 第二步观察进程的内存回收:通过
jstat -gc 进程id 每隔多少毫秒打印一次观察垃圾回收情况变化。 - 第三步查询占用内存最高的java类型对象:通过
jmap -histo 进程id | head -20查看前20。 - 第四步如果你第三步通过查看前20名占用对能找到最好,如果还没找到通过
jmap导出一个hprof文件通过visualVM可视化工具查看导出的hporf文件。
JVM内存泄露的原因
- 静态集合不停添加数据
- 各种连接(io/数据库)未释放
- ThreadLocal 使用不当
- hash值变化了,重新hash方法,导致放在Map中的数据取不到了。
- 单例模式创建的单例不使用,没啥意义。
频繁Minor Gc
- 首先频繁Minor Gc说明年轻代内存不够了,要找出为啥不够。
- 第一步观察进程的内存回收:通过
jstat -gc 进程id 每隔多少毫秒打印一次观察垃圾回收情况变化。 - 第二步:如果Minor Gc后触发Full Gc能回收大部分内存,可以考虑是不是年轻代的内存设置的太小了。
频繁 Full Gc
- 情况:
- 大对象导致的,大对象直接分配在老年代,导致老年代的内存满了。
- 内存泄漏,一直存活,垃圾回收不掉。
- 长期存活的对象太多了。
- Survivor区太小,对象频繁晋升。
OOM常见原因
- 一次申请太多数据:比如excle文件太大,分页查询太多。
- JVM内存泄露导致。
- 服务器资源不行。
三色标记算法
三色标记是垃圾收集中可达性分析的一种算法,用于标记是否垃圾的。
三色:
- 白色:自己还没标记。如果算法运行完了都还是白色表示,不可达是垃圾对象
- 灰色:自己扫描了,但是下属还没标记。主要是在并发标记时下属节点发生了话,重新标记重点处理这一块的。
- 黑色:自己标记了,下属也标记了
三色标记的问题: - 浮动垃圾:垃圾收集器并发清除阶段,发现之前标记可达的现在不可达了。这些不可达的其实就是浮动垃圾。不过这个问题不严重,下一次垃圾回收就能清除。
- 漏标问题:垃圾收集器并发清除阶段,发现之前标记不可达的现在可达了。这就很严重了。CMS 采用 增量更新(Incremental Update)方式处理:G1 采用原始快照(SATB)方式处理。
GC的root有哪些
- 虚拟机栈(栈帧中的局部变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(即一般说的Native方法)引用的对象
对象一定存储在堆上吗?
大对象直接进入老年代: 。如果对象需要的内存空间超过某个阈值,JVM会直接将其分配到老年代,以避免在新生代发生频繁的复制开销(因为大对象在Minor GC时复制成本极高)
逃逸分析与栈上分配:没有逃逸的对象的属性会被拆解为局部变量,直接存储在虚拟机栈的栈帧中
TLAB(线程本地分配缓冲区)优先: 防止多线程。