jvm堆内堆外内存使用整理

最近遇到内存和CPU不稳定情况,深究了下JVM有不少收获,总结如下:
堆内内存是java程序员在日常工作中解除比较多的, 可以在jvm参数中使用-Xms, -Xmx 等参数来设置堆的大小和最大值
堆内内存 = 年轻代 + 老年代 + 持久代
年轻代 (Young Generation)
存放的是新生成的对象,年轻代的目标是尽可能快速的收集掉那些生命周期短的对象。
Eden
大部分对象在Eden区中生成,当Eden区满时, 依然存活的对象将被复制到Survivor区, 当一个Survivor 区满时, 此区的存活对象将被复制到另外一个Survivor区
Survivor(通常2个)
当两个 Survivor 区 都满时, 从第一个Survivor 区 被复制过来 且 依旧存活的 对象会被复制到 老年区(Tenured)
Survivor 的两个区是对称的, 没有先后关系, 所有同一个区中可能同时存在从Eden复制过来的对象 和 从前一个 Survivor 复制过来的对象。Survivor 区可以根据需要配置多个, 从而增加对象在年轻代的存在时间, 减少被放到老年代的可能。
老年代 (Old Generation)
存放了在年轻代中经历了N次垃圾回收后仍存活的对象, 是一些生命周期较长的对象
持久代 (Permanent Generation)
存放静态文件, 如静态类和方法等。持久代对垃圾回收没有显著影响, 但是有些应用可能动态生成或者调用一些class, 比如Hibernate, Mybatis 等, 此时需要设置一个较大的持久代空间来存放这些运行过程中新增的类。
设置持久代大小参数: -XX:MaxPermSize= Perm => Permanent
垃圾回收(GC)
Scavenge GC
一般当新对象生成并且在Eden申请空间失败时就会触发Scavenger GC, 对Eden区域进行GC, 清除非存活对象, 并且把尚存或的对象移动到Survivor区, 然后整理两个Survivor区。
该方式的GC是对年轻代的Eden区进行,不会影响到年老代。
由于大部分对象是从Eden区开始的, 同时Eden区分配的内存不会很大, 所以Eden区的GC会很频繁。
Full GC
对整个堆进行整理, 包括Young, Tenured 和Permanent。所消耗的时间较长, 所以要尽量减少 Full GC 的次数,导致 Full GC 的可能原因:1、老年代(Tenured) 被写满 2、持久代(Permanent) 被写满
System.gc() 被显示调用
上一次GC之后Heap 的各域分配策略动态变化
常用垃圾回收算法
Reference Counting (引用计数算法)、Mark-Sweep (标记清除法)、Coping (复制法)、Mark-Compact (标记压缩法)、Generational Collecting (分代收集法)、Region (分区法)、GC Roots Tracing (可达性算法)、堆外内存(off-heap memory)

定义:堆外内存就是把内存对象分配在Java虚拟机的堆以外的内存
java.nio.DirectByteBuffer:Java 开发者经常用 java.nio.DirectByteBuffer 对象进行堆外内存的管理和使用, 该类会在创建对象时就分配堆外内存。
在这里插入图片描述
DK1.8 取消了方法区, 由MetaSpace(元空间)代替。-XX:MaxPermSize由 -XX:MetaspaceSize, -XX:MaxMetaspaceSize 等代替
使用堆外内存的优点
减少了垃圾回收机制(GC 会暂停其他的工作)
加快了复制的速度,堆内在flush到远程时, 会先复制到直接内存(非堆内存), 然后再发送。而堆外内存(本身就是物理机内存)几乎省略了该步骤
使用堆外内存的缺点
内存难以控制,使用了堆外内存就间接失去了JVM管理内存的可行性,改由自己来管理,当发生内存溢出时排查起来非常困难。

<think>好的,我需要帮助用户解决如何通过Linux命令检查运行在Kubernetes Pod中的JVM内存使用情况和堆外内存占用的问题。首先,我得回忆一下相关的Linux命令和JVM内存管理的知识。 用户提到了Kubernetes Pod,所以可能需要用到kubectl命令来进入容器或者获取容器信息。比如使用kubectl exec进入Pod执行命令。另,用户需要查看JVM内存,这涉及到JVM自身的内存和非内存堆外内存)。这时候可能需要JVM提供的工具,比如jstat或者jcmd,这些工具可以查看内存使用情况,比如Eden区、Survivor区、老年代的使用量。 不过,用户还关心堆外内存使用。这部分可能包括Native Memory,比如直接内存(Direct Buffer)、线程栈、元空间(Metaspace)等。JVM本身可能不会完全统计这些,所以可能需要结合操作系统的工具来查看进程的内存占用情况。常用的命令有top、ps、pmap等,特别是查看RSS(Resident Set Size)和VSZ(Virtual Memory Size)。但需要注意的是,容器的内存限制可能会影响这些命令的显示,因为在容器内看到的可能不是宿主机的真实数据。 另,引用中提到的内容也需要注意。根据引用[2],JVM会预先从操作系统分配内存,导致容器级别的RSS较高,而JVM内部的和非内存可能并未达到限制。所以用户可能需要同时查看JVM内部的内存使用情况和整个Pod的内存占用情况。 接下来需要整理具体的步骤和命令。比如,进入Pod后,可以使用jstat -gc <pid>来查看内存各个区域的使用情况。而堆外内存可能需要使用jcmd <pid> VM.native_memory来查看,但需要JVM启动时开启Native Memory Tracking(NMT)。此,通过top或ps命令查看进程的RSS,结合容器的内存限制,可以判断是否有内存泄漏或者过度分配的情况。 还有可能需要检查JVM参数,比如-Xmx和-Xms的设置,以及是否使用了某些会占用堆外内存的库或框架,比如Netty使用的直接内存缓冲。这时候可能需要结合jmap或其他工具来生成转储,分析内存分布。 总结一下,步骤可能包括: 1. 使用kubectl进入Pod。 2. 查找Java进程的PID。 3. 使用jstat查看内存统计。 4. 启用NMT后使用jcmd查看Native Memory。 5. 使用top/ps查看进程内存占用。 6. 分析容器的内存指标,比如通过kubectl top pod。 同时需要提醒用户关于NMT的性能影响,以及如何正确配置JVM参数来限制堆外内存使用,避免容器被OOMKilled。可能还需要结合容器的内存监控工具,如Prometheus,来长期监控内存使用趋势。 现在需要把这些思路整理成结构化的步骤,并确保引用了提供的参考资料中的内容,比如引用[2]和引用[3]提到的JVM预分配内存和排查方法。此,要确保命令正确,并给出示例,方便用户直接使用。最后生成相关问题,帮助用户深入理解问题。</think>### 通过Linux命令检查Pod中JVM内存堆外内存使用的方法 #### 1. 进入Kubernetes Pod环境 使用`kubectl exec`命令进入目标Pod: ```bash kubectl exec -it <pod名称> -- /bin/bash ``` #### 2. 查找Java进程PID 通过`ps`命令获取JVM进程的PID: ```bash ps aux | grep java ``` 输出示例: ``` root 1 5.2 2.1 1234567 65432 ? Sl 10:00 java -Xmx512m -jar app.jar ``` #### 3. 查看JVM内存使用 使用`jstat`命令监控内存分区使用情况: ```bash jstat -gc <PID> 1000 5 # 每秒采样1次,共5次 ``` 输出示例: ``` S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT CGC CGCT GCT 512.0 512.0 0.0 256.0 4096.0 2048.0 10240.0 5120.0 2048.0 1536.0 256.0 128.0 5 0.123 1 0.456 0 0.000 0.579 ``` 字段说明: - `EC/EU`:Eden区容量/使用量 - `OC/OU`:老年代容量/使用量 - `MC/MU`:元空间容量/使用量 #### 4. 分析堆外内存(需启用NMT) **步骤1**:启动JVM时添加参数: ```bash -XX:NativeMemoryTracking=detail ``` **步骤2** 查看原生内存分布: ```bash jcmd <PID> VM.native_memory summary ``` 输出示例: ``` Total: reserved=1456234KB, committed=456234KB - Java Heap (reserved=524288KB, committed=262144KB) - Class (reserved=234567KB, committed=12345KB) - Thread (reserved=12345KB, committed=2345KB) - Code (reserved=45678KB, committed=12345KB) - GC (reserved=34567KB, committed=12345KB) - Internal (reserved=2345KB, committed=1234KB) - Other (reserved=12345KB, committed=2345KB) ``` #### 5. 操作系统级内存监控 使用`pmap`分析进程内存映射: ```bash pmap -x <PID> | sort -nk3 | tail ``` 输出示例: ``` Address Kbytes RSS Dirty Mode Mapping 00007f8d90000000 1048580 456780 123456 rw-s [anon] ``` 重点关注`[anon]`段的大内存块,可能对应堆外内存分配[^2]。 #### 6. 容器内存监控 使用`kubectl top`查看Pod整体内存消耗: ```bash kubectl top pod <pod名称> ``` 输出示例: ``` NAME CPU(cores) MEMORY(bytes) app-pod 350m 876Mi ``` #### 关键问题排查点 1. **内存泄漏**:持续观察`jstat`输出,若`OU`(老年代使用量)持续增长且频繁触发FGC后不下降 2. **元空间泄漏**:`MC/MU`持续增长,可能由动态类生成导致 3. **直接内存泄漏**:NMT中`Internal`或`Other`部分异常增长,常见于NIO的DirectBuffer使用 4. **线程泄漏**:通过`jstack <PID> | grep 'java.lang.Thread.State' | wc -l`统计线程数 ### 典型问题解决案例 某Pod频繁OOMKilled但JVM使用正常,通过以下步骤排查: 1. `jstat`显示内存使用稳定在300MB(Xmx=512MB) 2. `pmap`发现多个100MB+的匿名内存映射 3. `jcmd VM.native_memory`显示`Internal`部分占用800MB 4. 最终定位到JNI库存在内存泄漏,修复后内存稳定[^3]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值