Out of Memory Error

问题现象

在执行启动脚本的时候,总是报jdk版本问题。

所以找到jdk路径执行 ./java -version 查看jdk版本。却发现不输出jdk版本,直接在路径下生成hs_err_pid*文件。

问题分析

  • 查看log内容

发现文中字段引起关注

# Native memory allocation (mmap) failed to map 703594496 bytes for committing reserved memory.
# Possible reasons:
#   The system is out of physical RAM or swap space
#   The process is running with CompressedOops enabled, and the Java Heap may be blocking 
#
....

#  Out of Memory Error (os_linux.cpp:2749), pid=21745, tid=0x00007f9acdbc4700

....
  • 本地内存分配失败, 可能的原因有两种
  1. 系统物理内存或虚拟内存不足
  2. 程序在压缩指针模式下运行, Java堆会阻塞本地堆的增长
  • 查看内存状态

然后使用free -h命令查询, 发现内存足够

触发了操作系统OOM的机制了。

Linux用户内存都是读写时分配,所以系统发现需要内存基本上都是发生在handle_mm_fault()的时候(其他特殊流程类似,这里忽略),handle_mm_fault()要为缺的页分配内存,就会调alloc_pages()系列函数,从而调prepare_alloc_pages(),进而进入__alloc_pages_direct_reclaim(),这里已经把可以清到磁盘上的缓冲都清了一次了。这样之后还是分配不到内存,就只好进入OMM Killer了(pagefault_out_of_memory())。

OOM本身说白了是为保证系统自身的正常运行而作的一个优化处理手段,但他也有一些问题,下面通俗的看下:

系统中的内存只有可能被正在运行的进程和内核占据了,大家都不让,系统就只有死。内核是官家,进程是商家,官家不能杀,只好杀商家,商家杀一个也是杀,杀十个也是杀,那就杀个最胖的,少拉点仇恨。也就只能这样了,换你,你能怎么选?

这样做的缺点:一个程序的行为有可能使另一个正常程序"躺枪“(一个进程A狂分配内存,导致系统把毫无关系的进程B给kill了)。


引用别人的文章中的一段话。。

  • 查看vm.overcommit_memor参数设置

overcommit_memory=0, 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。
overcommit_memory=1, 表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
overcommit_memory=2, 表示内核允许分配超过所有物理内存和交换空间总和的内存
  • 优缺点分析

0:Heuristic overcommit handling. 就是由操作系统自己决定过量分配策略
1:Always overcommit. 一直允许过量分配
2:Don't overcommit. 不允许过量分配

-------------------------------------------

0 — 默认设置。内核执行启发式内存过量使用处理,方法是估算可用内存量,并拒绝明显无效的请求。遗憾的是因为内存是使用启发式而非准确算法计算进行部署,这个设置有时可能会造成系统中的可用内存超载。
1 — 内核执行无内存过量使用处理。使用这个设置会增大内存超载的可能性,但也可以增强大量使用内存任务的性能。
2 — 内存拒绝等于或者大于总可用 swap 大小以及 overcommit_ratio 指定的物理 RAM 比例的内存请求。如果您希望减小内存过度使用的风险,这个设置就是最好的。

使用sysctl vm.overcommit_memory来查看, 发现vm.overcommit_memory = 2,也可以查看CommitLimit,和Committed_AS 进行分析。

#查看内存申请和可用情况
#CommitLimit 表示系统可申请的总内存
#Committed_AS为当前已经申请的内存
[root@nc-zhangfr1]# cat /proc/meminfo | grep Commit
CommitLimit:    15951192 kB
Committed_AS:   16334032 kB
 
#说明:
#  Committed_AS: OS会预测启动这个程序时, 所有的进程可能会用到多少的内存, 如果超过了CommitLimit, 就会报错。

CommitLimit     就是overcommit的阈值,申请的内存总数超过CommitLimit的话就算是overcommit。
Committed_AS 表示所有进程已经申请的内存总大小,(注意是已经申请的,不是已经分配的),如果 Committed_AS 超过 CommitLimit 就表示发生了overcommit,超出越多表示 overcommit 越严重。Committed_AS 的含义换一种说法就是,如果要绝对保证不发生OOM (out of memory) 需要多少物理内存。

  • 为什么会考虑调整overcommit_memory?

一个保守的操作系统不会允许memory overcommit,有多少就分配多少,再申请就没有了,这其实有些浪费内存,因为进程实际使用到的内存往往比申请的内存要少,比如某个进程malloc()了200MB内存,但实际上只用到了100MB,按照UNIX/Linux的算法,物理内存页的分配发生在使用的瞬间,而不是在申请的瞬间,也就是说未用到的100MB内存根本就没有分配,这100MB内存就闲置了。

默认情况下系统是保守的,值为overcommit_memory=0。该项我觉得设成1是可以的,但是不建议使用参数值2,参数设置为允许所有的内存分配(含SWAP)----- 即使主机可能已经无法响应了。因为这样可能会造成机器完全无响应,官家和商家一起同归于尽。

解决方式 

几个解决方案哪种都可以解决

1、echo 1 > /proc/sys/vm/overcommit_memory,此方式临时生效,系统重启后消失
2、sudo sysctl vm.overcommit_memory=0, 即vm.overcommit_memory = 0, 允许系统自己决定过量分配策略
3、编辑/etc/sysctl.conf , 添加vm.overcommit_memory=1,然后sysctl -p 使配置文件永久生效
当然这是我们在开发环境下的解决方式, 在生产环境还是要尽量去优化调整JVM的参数来保证每个程序都有足够的内存来保证运行

4、增大swap空间
 

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值