关于linux下java进程内存占用大于Xmx设置,且一直提升

本文探讨了Java游戏服务器中遇到的内存问题,包括堆内存、堆外内存的管理,以及如何通过调整JVM参数、引入tcmalloc库和更换JDK版本来解决内存持续增长的难题。

服务器很久前出现了这个问题, 

网上查了一大堆

1,java进程的内存占用大概是 堆内存+堆外内存(DirectBufferSize)+JVM的一些占用

2, -Xmx设置的是堆内存, 如果不具体指定 -XX:MaxDirectMemorySize(堆外内存),也相当于设置了XX:MaxDirectMemorySize

3,可以用pmap -x pid来查看具体内存使用, 用/proc/{pid}/smaps 来更详细查看

 

不过,发现用-XX:MaxDirectMemorySize设置了之后, 还是没效果

继续查,发现有glibc的问题, 需要安装tcmalloc,在启动参数里面增加 export LD_PRELOAD=/usr/local/lib/libtcmalloc.so(根据你的安装位置),  但是只解决了虚拟内存高,实际内存还在增加

https://www.sohu.com/a/200462136_505827

 

最终发现, jdk版本 1.7.80 等几个版本, 有一些java.util.zip.Deflate.init() 方面的内存不释放的bug(我也没仔细排查),

用jdk 1.7.76替换之, 运行游戏服务器7天, 内存没继续上升

### 回答问题:如何检查 Java 程序占用了多少内存--- ## ✅ 核心回答 要准确检查一个 **Java 程序占用内存**,能只看堆内存(Heap),而应从两个层面分析: 1. **JVM 堆内存使用情况**(可通过 Java API 或工具查看) 2. **整个 JVM 进程的实际物理内存占用(RSS)**(操作系统级别) 因为 Java 应用的总内存 = 堆 + 元空间 + 线程栈 + 直接内存 + 代码缓存 + GC 和 JIT 开销,所以仅看堆会严重低估实际使用量。 --- ## ✅ 一、方法 1:通过 `jstat` 查看堆内存使用(最常用) ```bash # 获取进程 PID jps -l # 查看堆内存和 GC 情况(每秒刷新一次) jstat -gc <pid> ``` 输出示例: ``` S0C S1C S0U S1U EC EU OC OU 1024.0 1024.0 0.0 512.0 8192.0 6500.0 20480.0 12000.0 ``` 📌 计算堆使用量: - **已用堆内存 = EU + SU + OU ≈ 6500 + 512 + 12000 = 19,012 KB ≈ 18.6 MB** - 最大堆大小 = `-Xmx` 设置值(可用 `jinfo <pid>` 查看) > ⚠️ 注意:这只是堆内存包括其他部分。 --- ## ✅ 二、方法 2:通过 `jmap -heap` 查看堆详细信息 ```bash jmap -heap <pid> ``` 输出内容包含: - 使用的垃圾收集器 - 堆初始大小(init)、当前大小(current)、最大大小(max) - 新生代、老年代配置 📌 示例片段: ``` Heap Configuration: MinHeapFreeRatio = 40 MaxHeapFreeRatio = 70 MaxHeapSize = 4294967296 (4096.0MB) NewSize = 143130624 (136.5MB) ... Heap Usage: PS Young Generation Eden Space: capacity = 80740352 (77.0MB) used = 65000000 (62.0MB) ... PS Old Generation capacity = 2863661056 (2731.0MB) used = 1200000000 (1144.5MB) ``` 👉 总堆使用 ≈ 62 + 1144.5 = **1206.5 MB** --- ## ✅ 三、方法 3:查看整个 JVM 进程的真实内存占用(推荐用于生产) Java 程序实际占用内存大于内存。建议使用以下命令查看操作系统级别的内存使用: ### ✅ Linux/Unix: 使用 `ps` 命令 ```bash # 查看指定 PID 的 RSS(常驻内存) ps -p <pid> -o pid,ppid,cmd,rss,size,vsize # 示例输出: # PID PPID CMD RSS SIZE VSZ # 12345 12344 java ... 210500 210500 4198528 ``` - **RSS (Resident Set Size)**:实际使用的物理内存(单位 KB)→ **210,500 KB ≈ 205.6 MB** - **VSZ (Virtual Memory Size)**:虚拟内存总量(含映射文件等) 📌 推荐命令(简洁输出 RSS): ```bash ps -p $(jps | grep YourApp | awk '{print $1}') -o rss=,vsz= ``` --- ### ✅ 更精确监控:使用 `top` 或 `htop` ```bash top -p <pid> ``` 在交互界面中可看到: - `%MEM`:内存使用百分比 - `RES`:常驻内存(即 RSS) - `VIRT`:虚拟内存 --- ## ✅ 四、方法 4:编程方式获取 JVM 内存使用(Java 代码) ```java import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryUsage; public class MemoryUsageChecker { public static void printMemoryUsage() { // 获取堆内存信息 MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean(); MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage(); MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage(); System.out.printf("【堆内存】已使用: %.2f MB, 提交: %.2f MB, 最大: %.2f MB%n", heapUsage.getUsed() / 1024.0 / 1024.0, heapUsage.getCommitted() / 1024.0 / 1024.0, heapUsage.getMax() / 1024.0 / 1024.0); System.out.printf("【非堆内存】已使用: %.2f MB, 提交: %.2f MB%n", nonHeapUsage.getUsed() / 1024.0 / 1024.0, nonHeapUsage.getCommitted() / 1024.0 / 1024.0); // 注意:无法直接获取 native 内存(如线程栈、直接内存) } public static void main(String[] args) { printMemoryUsage(); } } ``` 📌 输出示例: ``` 【堆内存】已使用: 1206.50 MB, 提交: 2048.00 MB, 最大: 4096.00 MB 【非堆内存】已使用: 380.20 MB, 提交: 400.00 MB ``` > ❗注意:这仍包括线程栈、DirectBuffer、JIT 编译代码等 native 内存--- ## ✅ 五、完整内存构成图解(Java 应用总内存) | 内存区域 | 是否包含在堆中 | 说明 | |--------|----------------|------| | Heap (Eden/Survivor/Old) | ✅ 是 | `-Xms`, `-Xmx` 控制 | | Metaspace | ❌ 否 | 类元数据,受 `-XX:MaxMetaspaceSize` 控制 | | Compressed Class Space | ❌ 否 | 存储压缩类指针数据 | | Thread Stacks | ❌ 否 | 每个线程默认 1MB(64位),大量线程时很耗内存 | | Direct Buffers | ❌ 否 | NIO 中通过 `ByteBuffer.allocateDirect()` 分配 | | Code Cache | ❌ 否 | JIT 编译后的本地代码存储区 | | GC & Internal Overhead | ❌ 否 | GC 数据结构、JVM 内部对象 | 📌 实际经验公式: > **JVM 总内存 ≈ 堆大小 × 1.2 ~ 1.5** > 若开启大量线程或使用 Netty/MappedByteBuffer,则可能更高。 --- ## ✅ 六、最佳实践建议 | 场景 | 推荐方法 | |------|----------| | 开发调试 | `jstat -gc <pid>` + `jmap -histo` | | 内存泄漏排查 | `jmap -dump:format=b,file=heap.hprof <pid>` + MAT 分析 | | 容器环境监控 | `ps -p <pid> -o rss=` 结合 Prometheus Exporter | | 自动化监控 | 编写脚本定时采集 `jstat` 和 `ps` 数据 | --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值