记录Slab占用内存过大导致kill程序的情况

本文深入探讨了Linux系统中slab内存管理机制,特别是在老化测试中遇到的内存占用问题。通过分析内核打印信息,揭示了slab机制如何影响系统内存,并提供了手动清理slab缓存的方法及长期解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

起因

在应用程序的老化测试时发现,系统被kill掉,但是从kill掉的内核打印信息,没有发现太大的问题。

[411371.892812] ntpd invoked oom-killer: gfp_mask=0x27000c0(GFP_KERNEL_ACCOUNT|__GFP_NOTRACK), nodemask=0, order=1, oom_score_adj=0
[411371.911869] COMPACTION is disabled!!!
[411371.916076] CPU: 0 PID: 1871 Comm: ntpd Not tainted 4.9.118 #1168
[411371.922965] Hardware name: xxxx
[411371.926972] [<c010ec9c>] (unwind_backtrace) from [<c010af08>] (show_stack+0x10/0x14)
[411371.935713] [<c010af08>] (show_stack) from [<c02c4568>] (dump_stack+0x70/0x8c)
[411371.943874] [<c02c4568>] (dump_stack) from [<c01c6c4c>] (dump_header.constprop.3+0x7c/0x1b4)
[411371.953388] [<c01c6c4c>] (dump_header.constprop.3) from [<c0189b40>] (oom_kill_process+0xac/0x438)
[411371.963481] [<c0189b40>] (oom_kill_process) from [<c018a3a4>] (out_of_memory+0x358/0x400)
[411371.972704] [<c018a3a4>] (out_of_memory) from [<c018e47c>] (__alloc_pages_nodemask+0x954/0xa0c)
[411371.982509] [<c018e47c>] (__alloc_pages_nodemask) from [<c01161c8>] (copy_process.part.3+0xf8/0x12ac)
[411371.992891] [<c01161c8>] (copy_process.part.3) from [<c01174c4>] (_do_fork+0xa8/0x33c)
[411372.001820] [<c01174c4>] (_do_fork) from [<c01177f0>] (sys_vfork+0x20/0x28)
[411372.009682] [<c01177f0>] (sys_vfork) from [<c0106e20>] (ret_fast_syscall+0x0/0x48)
[411372.021360] [ pid ]   uid  tgid total_vm      rss nr_ptes nr_pmds swapents oom_score_adj name
[411372.031093] [ 1133]     0  1133      559      373       6       0        0         -1000 ubusd
[411372.041688] [ 1142]     0  1142      480       92       4       0        0             0 askfirst
[411372.061911] [ 1290]     0  1290      560      341       5       0        0         -1000 logd
[411372.071568] [ 1292]     0  1292      571      110       6       0        0             0 logread
[411372.084341] [ 1333]     0  1333     9234      366       9       0        0             0 adbd
[411372.094664] [ 1833]     0  1833      619      424       5       0        0         -1000 netifd
[411372.108989] [ 1871]     0  1871      782      421       5       0        0             0 ntpd
[411372.120958] [ 1916]     0  1916      730      109       5       0        0             0 adb_shell
[411372.131689] [ 1918]     0  1918      761      488       6       0        0             0 sh
[411372.141524] [ 1928]     0  1928      730      109       6       0        0             0 exe
[411372.151432] [ 1933]     0  1933     8837     1046      13       0        0             0 test
[411372.162027] Out of memory: Kill process 1933 (test) score 8 or sacrifice child
[411372.170821] Killed process 1933 (test) total-vm:35348kB, anon-rss:2428kB, file-rss:1756kB, shmem-rss:0kB

从这里来看,并没有发现test进程存在内存占用过大的原因,测试程序本身需要使用的内存大小刚好与rss的大小差不多,同时检查确认程序流程,不存在内存泄露,但是为什么程序还是被kill掉呢?

疑问

程序没有内存泄露,但是为什么还是被kill掉呢?
一般的,都是有应用程序向系统申请内存,但是系统发现剩余的内存大小无法满足当前的申请,进行一系列的操作之后还是无法满足,将会选择最合适的程序将其kill,这样系统将可以回收它的内存,从而满足系统中其他进程的内存需求。所以,程序被kill掉,并不一定说该程序有内存泄露,只是说当系统内存被kill时,它最适合被kill。
那么,程序被kill掉的具体原因是什么呢?

进展

在程序被kill之前,可以查看进程占用的内存信息,看看进程是否存在内存泄露:

cat /proc/PID/status

其中部分信息如下:
VmPeak:	    3068 kB
VmSize:	    3068 kB
VmLck:	       0 kB
VmPin:	       0 kB
VmHWM:	     612 kB
VmRSS:	     612 kB

我们主要查看VmRSS的大小是否逐渐在增大,如果该值逐渐增大,很大可能是程序存在内存泄露。但是在test测试中,程序的该值并没有很明显的变化,所以转向系统内存信息。

每隔一段时间,查看系统内存的信息,操作如下:

root@Linux: /# cat /proc/meminfo 
MemTotal:         493184 kB
MemFree:          442572 kB
MemAvailable:     452300 kB
Buffers:            3424 kB
Cached:             3224 kB
SwapCached:            0 kB
Active:             8940 kB
Inactive:            284 kB
Active(anon):       2588 kB
Inactive(anon):      120 kB
Active(file):       6352 kB
Inactive(file):      164 kB
Unevictable:           0 kB
Mlocked:               0 kB
HighTotal:             0 kB
HighFree:              0 kB
LowTotal:         493184 kB
LowFree:          442572 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                20 kB
Writeback:             0 kB
AnonPages:          2616 kB
Mapped:             2204 kB
Shmem:               124 kB
Slab:              30528 kB
SReclaimable:      13904 kB
SUnreclaim:        16624 kB
KernelStack:         704 kB
PageTables:          296 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      246592 kB
Committed_AS:      55424 kB
VmallocTotal:     507904 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB
CmaTotal:          65536 kB
CmaFree:           59280 kB

通过cat /proc/meminfo查看系统的内存信息,其中,Slab是slab占用的内存大小,SReclaimable是可回收的,而SUnreclaim是不可回收的。发现Slab占用了系统快30M的内存,留意这个信息。接着,再查看一下,slab的详细使用情况:

root@Linux: /# cat /proc/slabinfo 
slabinfo - version: 2.1
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
ext4_groupinfo_4k     58     81    296   27    2 : tunables    0    0    0 : slabdata      3      3      0
ext4_groupinfo_1k      1     28    288   28    2 : tunables    0    0    0 : slabdata      1      1      0
jbd2_1k                0      0   3072   10    8 : tunables    0    0    0 : slabdata      0      0      0
bridge_fdb_cache       0      0    320   25    2 : tunables    0    0    0 : slabdata      0      0      0
sd_ext_cdb             2     18    216   18    1 : tunables    0    0    0 : slabdata      1      1      0
sgpool-128             2     14   2304   14    8 : tunables    0    0    0 : slabdata      1      1      0
sgpool-64              2     25   1280   25    8 : tunables    0    0    0 : slabdata      1      1      0
sgpool-32              2     21    768   21    4 : tunables    0    0    0 : slabdata      1      1      0
sgpool-16              2     16    512   16    2 : tunables    0    0    0 : slabdata      1      1      0
sgpool-8               2     21    384   21    2 : tunables    0    0    0 : slabdata      1      1      0
cfq_io_cq             10     31    264   31    2 : tunables    0    0    0 : slabdata      1      1      0
cfq_queue              9     22    360   22    2 : tunables    0    0    0 : slabdata      1      1      0
fat_inode_cache        3     26    616   26    4 : tunables    0    0    0 : slabdata      1      1      0
fat_cache              0      0    200   20    1 : tunables    0    0    0 : slabdata      0      0      0
squashfs_inode_cache     88    200    640   25    4 : tunables    0    0    0 : slabdata      8      8      0
jbd2_transaction_s      0     42    384   21    2 : tunables    0    0    0 : slabdata      2      2      0
jbd2_inode             1     76    208   19    1 : tunables    0    0    0 : slabdata      4      4      0
jbd2_journal_handle      0     18    216   18    1 : tunables    0    0    0 : slabdata      1      1      0
jbd2_journal_head      0     68    240   17    1 : tunables    0    0    0 : slabdata      4      4      0
jbd2_revoke_table_s      4     21    192   21    1 : tunables    0    0    0 : slabdata      1      1      0
jbd2_revoke_record_s      0     16    256   16    1 : tunables    0    0    0 : slabdata      1      1      0
ext4_inode_cache      15     46    840   19    4 : tunables    0    0    0 : slabdata      4      4      0
ext4_free_data         0     72    224   18    1 : tunables    0    0    0 : slabdata      4      4      0
ext4_allocation_context      0     28    288   28    2 : tunables    0    0    0 : slabdata      1      1      0
ext4_prealloc_space      2     16    256   16    1 : tunables    0    0    0 : slabdata      1      1      0
ext4_system_zone      17     36    216   18    1 : tunables    0    0    0 : slabdata      2      2      0
ext4_io_end            0     68    232   17    1 : tunables    0    0    0 : slabdata      4      4      0
ext4_extent_status      4     72    216   18    1 : tunables    0    0    0 : slabdata      4      4      0
mbcache                0      0    224   18    1 : tunables    0    0    0 : slabdata      0      0      0
dio                    0      0    640   25    4 : tunables    0    0    0 : slabdata      0      0      0
fasync_cache           0      0    208   19    1 : tunables    0    0    0 : slabdata      0      0      0
posix_timers_cache      0      0    336   24    2 : tunables    0    0    0 : slabdata      0      0      0
UNIX                  20     34    960   17    4 : tunables    0    0    0 : slabdata      2      2      0
ip4-frags              0      0    304   26    2 : tunables    0    0    0 : slabdata      0      0      0
ip_mrt_cache           0      0    384   21    2 : tunables    0    0    0 : slabdata      0      0      0
UDP-Lite               0      0    896   18    4 : tunables    0    0    0 : slabdata      0      0      0
tcp_bind_bucket        1     16    256   16    1 : tunables    0    0    0 : slabdata      1      1      0
inet_peer_cache        0      0    384   21    2 : tunables    0    0    0 : slabdata      0      0      0
secpath_cache          0      0    256   16    1 : tunables    0    0    0 : slabdata      0      0      0
flow_cache             0      0    296   27    2 : tunables    0    0    0 : slabdata      0      0      0
xfrm_dst_cache         0      0    576   28    4 : tunables    0    0    0 : slabdata      0      0      0
ip_fib_trie            3     18    216   18    1 : tunables    0    0    0 : slabdata      1      1      0
ip_fib_alias           4     18    216   18    1 : tunables    0    0    0 : slabdata      1      1      0
ip_dst_cache           4     21    384   21    2 : tunables    0    0    0 : slabdata      1      1      0
PING                   0      0    896   18    4 : tunables    0    0    0 : slabdata      0      0      0
RAW                    8     18    896   18    4 : tunables    0    0    0 : slabdata      1      1      0
UDP                    1     18    896   18    4 : tunables    0    0    0 : slabdata      1      1      0
tw_sock_TCP            0      0    328   24    2 : tunables    0    0    0 : slabdata      0      0      0
request_sock_TCP       0      0    376   21    2 : tunables    0    0    0 : slabdata      0      0      0
TCP                    1     18   1728   18    8 : tunables    0    0    0 : slabdata      1      1      0
eventpoll_pwq         16     36    216   18    1 : tunables    0    0    0 : slabdata      2      2      0
eventpoll_epi         16     50    320   25    2 : tunables    0    0    0 : slabdata      2      2      0
inotify_inode_mark      0      0    232   17    1 : tunables    0    0    0 : slabdata      0      0      0
scsi_data_buffer       0      0    200   20    1 : tunables    0    0    0 : slabdata      0      0      0
request_queue          4     24   1312   24    8 : tunables    0    0    0 : slabdata      1      1      0
blkdev_requests       16     80    408   20    2 : tunables    0    0    0 : slabdata      4      4      0
blkdev_ioc            11     17    240   17    1 : tunables    0    0    0 : slabdata      1      1      0
bio-0                 10     84    384   21    2 : tunables    0    0    0 : slabdata      4      4      0
biovec-max            10     45   3328    9    8 : tunables    0    0    0 : slabdata      5      5      0
biovec-128             0      0   1792   18    8 : tunables    0    0    0 : slabdata      0      0      0
biovec-64              0     16   1024   16    4 : tunables    0    0    0 : slabdata      1      1      0
biovec-16              0     18    448   18    2 : tunables    0    0    0 : slabdata      1      1      0
uid_cache              0      0    320   25    2 : tunables    0    0    0 : slabdata      0      0      0
dmaengine-unmap-2      1     16    256   16    1 : tunables    0    0    0 : slabdata      1      1      0
sock_inode_cache      42     50    640   25    4 : tunables    0    0    0 : slabdata      2      2      0
skbuff_fclone_cache      0      0    640   25    4 : tunables    0    0    0 : slabdata      0      0      0
skbuff_head_cache      0     54    448   18    2 : tunables    0    0    0 : slabdata      3      3      0
configfs_dir_cache     44     51    232   17    1 : tunables    0    0    0 : slabdata      3      3      0
file_lock_cache        0     26    312   26    2 : tunables    0    0    0 : slabdata      1      1      0
file_lock_ctx          1     19    208   19    1 : tunables    0    0    0 : slabdata      1      1      0
shmem_inode_cache    294    308    568   28    4 : tunables    0    0    0 : slabdata     11     11      0
pool_workqueue         7     21    768   21    4 : tunables    0    0    0 : slabdata      1      1      0
proc_inode_cache     376    406    552   29    4 : tunables    0    0    0 : slabdata     14     14      0
sigqueue               0     24    328   24    2 : tunables    0    0    0 : slabdata      1      1      0
bdev_cache            14     21    768   21    4 : tunables    0    0    0 : slabdata      1      1      0
kernfs_node_cache  18429  18476    264   31    2 : tunables    0    0    0 : slabdata    596    596      0
mnt_cache             25     36    448   18    2 : tunables    0    0    0 : slabdata      2      2      0
filp                 344    576    448   18    2 : tunables    0    0    0 : slabdata     32     32      0
inode_cache         1461   1736    520   31    4 : tunables    0    0    0 : slabdata     56     56      0
dentry              2343   3500    320   25    2 : tunables    0    0    0 : slabdata    140    140      0
names_cache           18     35   4352    7    8 : tunables    0    0    0 : slabdata      5      5      0
buffer_head         1422   1424    248   16    1 : tunables    0    0    0 : slabdata     89     89      0
nsproxy                0      0    208   19    1 : tunables    0    0    0 : slabdata      0      0      0
vm_area_struct      1256   1320    272   30    2 : tunables    0    0    0 : slabdata     44     44      0
mm_struct             31     69    704   23    4 : tunables    0    0    0 : slabdata      3      3      0
fs_cache              33     50    320   25    2 : tunables    0    0    0 : slabdata      2      2      0
files_cache           34     48    512   16    2 : tunables    0    0    0 : slabdata      3      3      0
signal_cache         101    152    832   19    4 : tunables    0    0    0 : slabdata      8      8      0
sighand_cache        101    105   1536   21    8 : tunables    0    0    0 : slabdata      5      5      0
task_struct          106    132   1472   22    8 : tunables    0    0    0 : slabdata      6      6      0
cred_jar             134    231    384   21    2 : tunables    0    0    0 : slabdata     11     11      0
anon_vma_chain       658    702    216   18    1 : tunables    0    0    0 : slabdata     39     39      0
anon_vma             408    432    224   18    1 : tunables    0    0    0 : slabdata     24     24      0
pid                  103    125    320   25    2 : tunables    0    0    0 : slabdata      5      5      0
radix_tree_node      644    656    488   16    2 : tunables    0    0    0 : slabdata     41     41      0
idr_layer_cache      192    208   1248   26    8 : tunables    0    0    0 : slabdata      8      8      0
kmalloc-8192          16     18   8448    3    8 : tunables    0    0    0 : slabdata      6      6      0
kmalloc-4096         480    483   4352    7    8 : tunables    0    0    0 : slabdata     69     69      0
kmalloc-2048          44     56   2304   14    8 : tunables    0    0    0 : slabdata      4      4      0
kmalloc-1024         201    225   1280   25    8 : tunables    0    0    0 : slabdata      9      9      0
kmalloc-512          444    462    768   21    4 : tunables    0    0    0 : slabdata     22     22      0
kmalloc-256          372    384    512   16    2 : tunables    0    0    0 : slabdata     24     24      0
kmalloc-192          277    306    448   18    2 : tunables    0    0    0 : slabdata     17     17      0
kmalloc-128         1589   1596    384   21    2 : tunables    0    0    0 : slabdata     76     76      0
kmalloc-64         15937  16200    320   25    2 : tunables    0    0    0 : slabdata    648    648      0
kmem_cache_node      107    125    320   25    2 : tunables    0    0    0 : slabdata      5      5      0
kmem_cache           107    126    384   21    2 : tunables    0    0    0 : slabdata      6      6      0

从这里可以了解到slab的使用情况,记录下来。

slab是Linux操作系统的一种内存分配机制。其工作是针对一些经常分配并释放的对象,如进程描述符等,这些对象的大小一般比较小,如果直接采用伙伴系统来进行分配和释放,不仅会造成大量的内碎片,而且处理速度也太慢。而slab分配器是基于对象进行管理的,相同类型的对象归为一类(如进程描述符就是一类),每当要申请这样一个对象,slab分配器就从一个slab列表中分配一个这样大小的单元出去,而当要释放时,将其重新保存在该列表中,而不是直接返回给伙伴系统,从而避免这些内碎片。slab分配器并不丢弃已分配的对象,而是释放并把它们保存在内存中。当以后又要请求新的对象时,就可以从内存直接获取而不用重复初始化。

接着将可以隔较长一段时间,重复的进行cat /proc/meminfocat /proc/slabinfo操作,对比几次的信息,检查问题。

最后发现,经过较长一段时间的测试之后,Slab占用的内存数量大大增加,如果是slab占用较大的内存,则是内核频繁分配结构体导致,导致系统可用内存减小。直到出现Out of memory导致kill程序。

解决

了解到是Slab导致的占用内存过高的问题之后,可以手动的刷Slab,操作如下:

echo 3 > /proc/sys/vm/drop_caches	/* 回刷缓冲 */

其中drop_caches的4个值有如下含义:

  • 0:不做任何处理,由系统自己管理
  • 1:清空pagecache
  • 2:清空dentries和inodes
  • 3:清空pagecache、dentries和inodes

但是这样的办法不是最佳的,最好还是应该通过slabinfo信息,了解到应用程序进行什么操作,导致内核频繁申请结构体导致Slab占用大量内存,看能否避免这样的问题,同时,内核有自动回收机制,可修改触发自动回收的阀值,当slab空闲内存达到一定量的时候,进行有效的回收。

后续

后来在参考文章看到信息,概括如下:
文中开头的说到的老化测试程序test,就是大量的保存文件,频繁的文件io操作(open、write、close),导致了dentry_cache占用了系统太多的内存资源。
inode对应于物理磁盘上的具体对象,而dentry是一个内存实体,其中的d_inode成员指向对应的inode,故可以把dentry看成是Linux文件系统中某个索引节点(inode)的链接,这个索引节点可以是文件,也可以是目录。而dentry_cache是目录项高速缓存,是Linux为了提高目录项对象的处理效率而设计的,它记录了目录项到inode的映射关系。

系统的自动slab缓存回收1

在slab缓存中,对象分为SReclaimable(可回收)和SUnreclaim(不可回收),而在系统中绝大多数对象都是可回收的。内核有一个参数,当系统内存使用到一定量的时候,会自动触动回收操作。

  • 内核参数:
    vm.min_free_kbytes = 836787
    代表系统所保留空闲内存的最低限。
    在系统初始化时会根据内存大小计算一个默认值,计算规则是:
    min_free_kbytes = sqrt(lowmem_kbytes * 16) = 4 * sqrt(lowmem_kbytes)(注:lowmem_kbytes即可认为是系统内存大小)
    另外,计算出来的值有最小最大限制,最小为128K,最大为64M。
    可以看出,min_free_kbytes随着系统内存的增大不是线性增长,因为随着内存的增大,没有必要也线性的预留出过多的内存,能保证紧急时刻的使用量便足矣。

  • min_free_kbytes的主要用途是计算影响内存回收的三个参数 watermark[min/low/high]

  1. watermark[high] > watermark [low] > watermark[min],各个zone各一套
  2. 在系统空闲内存低于 watermark[low]时,开始启动内核线程kswapd进行内存回收(每个zone一个),直到该zone的空闲内存数量达到watermark[high]后停止回收。如果上层申请内存的速度太快,导致空闲内存降至watermark[min]后,内核就会进行direct reclaim(直接回收),即直接在应用程序的进程上下文中进行回收,再用回收上来的空闲页满足内存申请,因此实际会阻塞应用程序,带来一定的响应延迟,而且可能会触发系统OOM。这是因为watermark[min]以下的内存属于系统的自留内存,用以满足特殊使用,所以不会给用户态的普通申请来用。
  3. 三个watermark的计算方法:
    watermark[min] = min_free_kbytes换算为page单位即可,假设为min_free_pages。(因为是每个zone各有一套watermark参数,实际计算效果是根据各个zone大小所占内存总大小的比例,而算出来的per zone min_free_pages)
    watermark[low] = watermark[min] * 5 / 4
    watermark[high] = watermark[min] * 3 / 2
    所以中间的buffer量为 high - low = low - min = per_zone_min_free_pages * 1/4。因为min_free_kbytes = 4* sqrt(lowmem_kbytes),也可以看出中间的buffer量也是跟内存的增长速度成开方关系。
  4. 可以通过/proc/zoneinfo查看每个zone的watermark
  • min_free_kbytes大小的影响
    min_free_kbytes设的越大,watermark的线越高,同时三个线之间的buffer量也相应会增加。这意味着会较早的启动kswapd进行回收,且会回收上来较多的内存(直至watermark[high]才会停止),这会使得系统预留过多的空闲内存,从而在一定程度上降低了应用程序可使用的内存量。极端情况下设置min_free_kbytes接近内存大小时,留给应用程序的内存就会太少而可能会频繁地导致OOM的发生。
    min_free_kbytes设的过小,则会导致系统预留内存过小。kswapd回收的过程中也会有少量的内存分配行为(会设上PF_MEMALLOC)标志,这个标志会允许kswapd使用预留内存;另外一种情况是被OOM选中杀死的进程在退出过程中,如果需要申请内存也可以使用预留部分。这两种情况下让他们使用预留内存可以避免系统进入deadlock状态。
    可测试,当调整完min_free_kbytes值大于系统空闲内存后,kswapd进程的确从休眠状态进入运行态,开始回收内存。

同时,还有一个参数vm.vfs_cache_pressure = 200
该文件表示内核回收用于directory和inode cache内存的倾向;缺省值100表示内核将根据pagecache和swapcache,把directory和inode cache保持在一个合理的百分比;降低该值低于100,将导致内核倾向于保留directory和inode cache;增加该值超过100,将导致内核倾向于回收directory和inode cache。

参考文章:
Linux服务器Cache占用过多内存导致系统内存不足问题的排查解决
linux系统slab内存占用


  1. 该段更多的参考 linux系统slab内存占用 ↩︎

<think>好的,用户现在问的是怎么分析内存溢出。首先,我需要回忆一下之前用户的问题历史。之前他问过用nmon分析网络性能瓶颈,现在转向内存问题,可能是在系统性能调优过程中遇到了不同层面的问题。用户可能是系统管理员或者开发人员,需要解决服务器或应用中的内存溢出问题。 首先,内存溢出(OOM)通常指应用或系统申请的内存超过了可用内存导致崩溃或错误。分析内存溢出需要分步骤,包括确认现象、监控工具的使用、分析堆栈或日志、以及优化策略。 用户可能需要知道如何从基础开始检查,比如使用哪些命令或工具,比如top、free、vmstat等。然后可能需要更深入的工具,如jmap、VisualVM对于Java应用,或者Valgrind对于C/C++程序。此外,用户可能还需要了解如何生成和分析核心转储文件,或者查看系统日志中的OOM Killer记录。 另外,考虑到用户之前使用nmon,可能对系统监控工具有一定了解,可以推荐结合nmon来监控内存使用情况,但nmon更多是实时监控,而内存溢出分析可能需要更详细的历史数据和堆栈跟踪。 需要注意用户的技术水平,可能需要分步骤解释,确保每个步骤清晰,工具的使用方法具体,比如命令示例和输出解读。同时,常见的内存溢出原因,如内存泄漏、配置不当、资源未释放等,需要给出对应的排查方法。 还要考虑到不同环境下的分析方式,比如Java应用和原生应用的不同工具,容器化环境(如Docker)中的内存分析可能有所不同,是否需要提及?但用户的问题比较通用,可能先集中在常规分析方法,再简要提及其他环境。 最后,给出优化建议,比如代码审查、配置调整、压力测试等,帮助用户解决问题。确保回答结构清晰,步骤明确,示例实用,覆盖常见场景和工具。</think>--- ### **内存溢出(OOM)分析步骤详解** --- #### **1. 确认内存溢出现象** - **典型表现**: - 应用崩溃(如 Java 的 `OutOfMemoryError`、C/C++ 的 `SIGSEGV` 信号)。 - 系统日志出现 `OOM-killer` 记录Linux 内核因内存不足终止进程)。 - 监控工具(如 `top`、`free`)显示内存使用率长期接近 100%。 --- #### **2. 基础监控工具** ##### **工具 1:`free` 查看系统内存** ```bash free -h # 以易读格式显示内存总量、已用、空闲、缓存等 ``` - **关键指标**: - `available`:系统实际可用内存(含缓存和缓冲)。 - `swap used`:交换分区使用量(过高表明物理内存不足)。 ##### **工具 2:`top` 或 `htop` 实时监控进程** ```bash top -o %MEM # 按内存使用率排序进程 ``` - **重点关注**: - `RES`(常驻内存)和 `VIRT`(虚拟内存小。 - 异常进程内存占比是否持续增长。 ##### **工具 3:`vmstat` 观察内存趋势** ```bash vmstat 1 # 每秒输出一次内存统计 ``` - **关键列**: - `swpd`:交换分区使用量。 - `free`:空闲内存。 - `si`(swap-in)、`so`(swap-out):交换频率(频繁交换表明内存不足)。 --- #### **3. 定位具体应用内存问题** ##### **场景 1:Java 应用** - **步骤 1:生成堆转储文件(Heap Dump)** ```bash jmap -dump:format=b,file=heap.hprof <PID> # 手动生成 ``` - 或添加 JVM 参数自动生成 OOM 时的堆转储: ```bash -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump ``` - **步骤 2:分析堆转储** - 使用 **Eclipse MAT** 或 **VisualVM**: - 查找占用内存的对象(如 `char[]`、`HashMap$Node`)。 - 检查对象引用链,定位未释放的代码位置。 - **步骤 3:监控 JVM 内存分代** ```bash jstat -gcutil <PID> 1s # 每秒输出堆各区域使用率 ``` - **关键指标**: - `Old` 区(老年代)使用率长期 >90%:可能存在内存泄漏。 - `Full GC` 频率过高:内存分配不当或泄漏。 ##### **场景 2:C/C++ 应用** - **工具 1:`valgrind` 检测内存泄漏** ```bash valgrind --leak-check=full ./your_program ``` - 输出中标记 `definitely lost` 的块为明确泄漏。 - **工具 2:`gdb` 分析核心转储文件(Core Dump)** ```bash gdb ./your_program core.<PID> # 加载核心转储 (gdb) bt full # 查看崩溃时的完整堆栈 ``` ##### **场景 3:容器化环境(如 Docker/K8s)** - **步骤 1:查看容器内存限制** ```bash docker stats # 显示容器实时内存使用 docker inspect <容器ID> | grep -i memory # 查看内存限制 ``` - **步骤 2:进入容器分析进程** ```bash docker exec -it <容器ID> /bin/bash top -o %MEM # 内部监控进程 ``` --- #### **4. 系统级深度排查** ##### **方法 1:分析内核日志** ```bash dmesg | grep -i oom # 查找 OOM-killer 记录 ``` - 输出示例: ``` [12345.678] Out of memory: Kill process 1234 (java) score 789 ``` - 记录被终止的进程内存评分。 ##### **方法 2:检查内存分配统计** ```bash cat /proc/meminfo # 详细内存分配信息 ``` - **关键指标**: - `Slab`:内核对象缓存(过高可能由内核模块泄漏导致)。 - `AnonPages`:匿名内存页(如进程堆、栈)。 ##### **方法 3:使用 `pmap` 查看进程内存映射** ```bash pmap -x <PID> # 显示进程内存详细分布 ``` - 检查是否存在异常的内存块(如未释放的共享库或匿名映射)。 --- #### **5. 常见内存泄漏原因** 1. **代码逻辑问题**: - 未关闭资源(如文件句柄、数据库连接)。 - 静态集合类无限增长(如全局 `HashMap` 未清理)。 2. **配置不当**: - JVM 堆小设置过小(`-Xmx`)或未限制容器内存。 3. **第三方库缺陷**: - 某些库可能存在未释放内存的 API 调用。 --- #### **6. 优化与解决建议** - **代码修复**: - 使用弱引用(WeakReference)管理缓存。 - 避免在循环中创建对象。 - **配置调整**: - 合理设置 JVM 堆小(如 `-Xmx4g -Xms4g`)。 - 限制容器内存(如 Docker 的 `-m 2g`)。 - **监控增强**: - 部署 Prometheus + Grafana 长期监控内存趋势。 - 定期运行压力测试,模拟高负载场景。 --- ### **示例:Java 内存泄漏分析** **现象**:应用每隔几天崩溃,日志显示 `OutOfMemoryError: Java heap space`。 **分析步骤**: 1. 通过 `jstat` 发现老年代(Old Gen)使用率持续上升至 100%。 2. 生成堆转储文件,用 MAT 分析发现 `ConcurrentHashMap` 占用了 80% 内存。 3. 检查代码发现全局缓存未设置过期策略,导致数据无限累积。 **解决**:改用带 LRU 淘汰策略的缓存(如 `Caffeine` 或 `Ehcache`)。 --- 通过上述工具和方法,可系统化定位内存溢出根本原因,结合代码优化和配置调整彻底解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chengwei_peng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值