Document

内存溢出问题分析与处理

内存占用情况查询

检查系统内存使用情况以 GiB 为单位进行显示

remotecmd "free -g"

1. total: 物理内存总量(GiB)。

2. used: 当前已使用的物理内存量(不包括缓冲区和缓存)(GiB)。

3. free: 可用的物理内存量(GiB)。

4. shared: 被多个进程共享的内存量(GiB)。

5. buff/cache: 作为文件系统缓存或内核缓冲区而保留的物理内存量(GiB)。

6. available: 在新应用程序启动时可以立即分配给用户的空闲内存量(GiB),这是由 free 和 buff/cache 的值计算得出的。

示例结果可能如下所示:

$ remotecmd "free -g"
              total        used        free      shared  buff/cache   available
Mem:            79          10         68           0          1        67
Swap:            0           0          0

上述示例数据含义介绍

共有 79GB 的物理内存

其中 10GB 正在使用中

还有 68GB 可用

没有任何共享内存 (shared)

大约 1GB 用于文件系统缓存和内核缓冲区 (buff/cache)

并且可供新的应用使用 67GB 左右的空闲内存 (available)

没有交换分区 (swap) 使用。

内存占用结果说明

linux系统上free命令看到的buff/cache到底是什么

  • buff/cache是buffers和cache之和

  • buff是内核缓冲区用到的内存,对应的是/proc/meminfo 中的 Buffers 值,是对原始磁盘块的临时存储,也就是用来缓存磁盘的数据,一般不会特别大(20MB 左右),它既可以用作“将要写入磁盘数据的缓存”,也可以用作“从磁盘读取数据的缓存”。

  • cache是内核页缓存和Slab用到的内存,对应的是/proc/meminfo中的Cached与SReclaimable之和,不过它既可以用作“从文件读取数据的页缓存”,也可以用作“写文件的页缓存”

  • buff是对磁盘数据的缓存,而 cache是文件数据的缓存,它们既会用在读请求中,也会用在写请求中。

  • SReclaimable 是 Slab 的一部分,是可以被回收的,例如缓存

  • linux内核使用 Slab 机制,管理文件系统的目录项和索引节点的缓存。Slab 包括两部分,其中的可回收部分,是指可以被回收的内核内存,包括目录项(dentry) 和索引节点( inode )的缓存等,用 SReclaimable 记录;而不可回收部分,用 SUnreclaim 记录。

总结

  • buff/cache表示的是文件缓存cache、块缓存buffer以及文件系统目录项和索引节点的缓存SReclaimable之和,强调的是可以被系统回收的内容

  • 读写文件时,会把文件内容缓存到内存cache中,提高读写效率

  • 读写磁盘时,会把内容缓存到内存buffer中,提高读写效率

  • 当内存不够时,buff和cache是可以被系统回收的,当然我们也可以使用drop cache的命令手动回收,不过可能会导致脏页同步到磁盘带来的磁盘IO变高

清理系统缓存

清理指令:

sync

echo 1 >/proc/sys/vm/drop_caches

echo 2 >/proc/sys/vm/drop caches

echo 3 >/proc/sys/vm/drop_caches

上述 Linux 命令用于清理系统缓存:

1. echo 1 > /proc/sys/vm/drop_caches - 清除 page cache,这将释放已被应用程序使用的内存页并返回给操作系统。

2. echo 2 > /proc/sys/vm/drop_caches - 清除 dentries 和 inodes 的 cache,这有助于在不重启机器的情况下重新挂载文件系统或修复某些错误(如 "No space left on device")。

3. echo 3 > /proc/sys/vm/drop_caches - 执行前面两项命令中的全部操作,即完全清空所有可用的 cache。

注意:使用此类命令时要小心,因为它们会立即释放大量可用 RAM,并且可能对正在运行的应用和服务产生负面影响。因此,只有在必要且可以承担后果时才应该执行这些操作。

总结

总的来说针对内存占用问题,不能把清理缓存作为最优解,根除问题还是要从程序优化下手。

要多维度分析原因,对症下药,可能的根因如下:

1. 存在大量未处理的账单项:检查代码是否正确地从数据库查询、过滤和排序账单项,然后将它们存储在 acctItemList 中。如果账单项数量过多,则可能会占用太多的堆空间。

2. 没有及时清理垃圾:在某些情况下,应用程序没有正确释放不需要使用的对象的引用,造成了内存泄漏。通过使用 JVM 参数 -XX:+UseG1GC 或 -XX:+CMSClassUnloadingEnabled 来启用 Garbage Collection (GC) 和类卸载功能可以帮助检测和修复此问题。

3. 使用了超大的集合类型:例如 HashMap 或 HashSet,其初始大小设置得很小,而随着数据的增加,它们会迅速膨胀。可以通过调整集合类型的初始大小来避免此问题。

4. 创建了大型对象:如果创建了一个非常大的对象,并且该对象不能被 GC 立即回收,那么也会消耗掉大部分可用的堆空间。

要解决此问题,您可以尝试以下方法之一:

1. 优化代码逻辑:仔细检查您的代码,找出哪里正在生成大量的账单项或者其他大型对象。根据需求进行修改,使其更高效且不会对堆内存产生如此大的压力。

2. 增大堆内存:您可以使用 JVM 参数 -Xmx 指定最大堆大小,其中 是所需的值,单位通常为 GB。例如,-Xmx4g 表示将最大堆大小设置为 4GB。

3. 实施垃圾收集策略:启用适用于您的应用场景的 GC 算法,例如 G1GC (-XX:+UseG1GC) 或 CMS (-XX:+CMSClassUnloadingEnabled)。这些算法旨在更好地管理堆内存并减少碎片化。

4. 监控和分析:使用 Java Virtual Machine Monitoring Tools (JVisualVM, JConsole, etc.) 对您的应用进行性能分析和诊断。这将帮助您识别潜在的问题并找到最佳的解决方案。 请注意,以上只是一些常见的方法,您还应该根据您的特定情况进行调试和测试。 

 

posted @ 2024-05-14 15:25  李宗光  阅读(20)  评论(0编辑  收藏  举报