tomcat 与 oom_killer
场景
在一台centos上,我启动了tomcat,运行之后内存剩余500M,但是第二天tomcat已经被停止了。要知道申请JVM内存是成倍申请,申请到了之后不会回收,也就是说,我的tomcat启动成功后,就拥有固定的内存,而我可以保证它的使用不会超出该内存而去申请下一段内存,但是为什么莫名的停止呢?而且没有留下任何日志,说明是系统kill掉的。
分析
那么我们想办法打出日志,加上以下参数,我们期待在下一次出现问题时能及时打印堆栈信息再挂掉。
1 2
| JAVA_OPTS="-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/usr/local/tomcat/logs"
|
第二天起床照样发现已被kill,查看日志,/var/log/message
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| Jul 3 03:53:03 rcmsit kernel: YDService invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0 Jul 3 03:53:04 rcmsit kernel: YDService cpuset=/ mems_allowed=0 Jul 3 03:53:04 rcmsit kernel: CPU: 1 PID: 9434 Comm: YDService Not tainted 3.10.0-514.26.2.el7.x86_64 #1 Jul 3 03:53:04 rcmsit kernel: Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 Jul 3 03:53:04 rcmsit kernel: ffff880119648000 00000000210107be ffff88005c037938 ffffffff81687133 Jul 3 03:53:04 rcmsit kernel: ffff88005c0379c8 ffffffff816820de ffffffff810eb0dc ffff88005c14b980 Jul 3 03:53:04 rcmsit kernel: ffff88005c14b998 0000000000000202 ffff880119648000 ffff88005c0379b8 Jul 3 03:53:04 rcmsit kernel: Call Trace: Jul 3 03:53:04 rcmsit kernel: [<ffffffff81687133>] dump_stack+0x19/0x1b Jul 3 03:53:04 rcmsit kernel: [<ffffffff816820de>] dump_header+0x8e/0x225 Jul 3 03:53:04 rcmsit kernel: [<ffffffff810eb0dc>] ? ktime_get_ts64+0x4c/0xf0 Jul 3 03:53:04 rcmsit kernel: [<ffffffff8113d22f>] ? delayacct_end+0x8f/0xb0 Jul 3 03:53:04 rcmsit kernel: [<ffffffff81184d0e>] oom_kill_process+0x24e/0x3c0 Jul 3 03:53:04 rcmsit kernel: [<ffffffff811847ad>] ? oom_unkillable_task+0xcd/0x120 Jul 3 03:53:04 rcmsit kernel: [<ffffffff81184856>] ? find_lock_task_mm+0x56/0xc0 Jul 3 03:53:04 rcmsit kernel: [<ffffffff81093c0e>] ? has_capability_noaudit+0x1e/0x30 Jul 3 03:53:04 rcmsit kernel: [<ffffffff81185546>] out_of_memory+0x4b6/0x4f0 Jul 3 03:53:04 rcmsit kernel: [<ffffffff81682be7>] __alloc_pages_slowpath+0x5d7/0x725 Jul 3 03:53:04 rcmsit kernel: [<ffffffff8118b655>] __alloc_pages_nodemask+0x405/0x420 Jul 3 03:53:04 rcmsit kernel: [<ffffffff811cf9ca>] alloc_pages_current+0xaa/0x170 Jul 3 03:53:04 rcmsit kernel: [<ffffffff81180be7>] __page_cache_alloc+0x97/0xb0 Jul 3 03:53:04 rcmsit kernel: [<ffffffff81183760>] filemap_fault+0x170/0x410 Jul 3 03:53:04 rcmsit kernel: [<ffffffffa01b7016>] ext4_filemap_fault+0x36/0x50 [ext4] Jul 3 03:53:04 rcmsit kernel: [<ffffffff811ac83c>] __do_fault+0x4c/0xc0 Jul 3 03:53:04 rcmsit kernel: [<ffffffff811accd3>] do_read_fault.isra.42+0x43/0x130 Jul 3 03:53:04 rcmsit kernel: [<ffffffff811b1461>] handle_mm_fault+0x6b1/0x1000 Jul 3 03:53:04 rcmsit kernel: [<ffffffff81692cc4>] __do_page_fault+0x154/0x450 Jul 3 03:53:04 rcmsit kernel: [<ffffffff81692ff5>] do_page_fault+0x35/0x90 Jul 3 03:53:04 rcmsit kernel: [<ffffffff8168f208>] page_fault+0x28/0x30 Jul 3 03:53:04 rcmsit kernel: Mem-Info: Jul 3 03:53:04 rcmsit kernel: active_anon:625329 inactive_anon:205647 isolated_anon:0#012 active_file:2142 inactive_file:3816 isolated_file:0#012 unevictable:0 dirty:0 writeback:0 unstable:0#012 slab_reclaimable:26535 slab_unreclaimable:52499#012 mapped:615 shmem:12902 0 pagetables:3722 bounce:0#012 free:33074 free_pcp:192 free_cma:0 Jul 3 03:53:04 rcmsit kernel: Node 0 DMA free:15400kB min:276kB low:344kB high:412kB active_anon:108kB inactive_anon:192kB active_file:0kB inactive_file:0kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:15992kB managed:15908kB mlocked:0kB dirty:0kB writ eback:0kB mapped:0kB shmem:44kB slab_reclaimable:68kB slab_unreclaimable:36kB kernel_stack:0kB pagetables:20kB unstable:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? yes Jul 3 03:53:04 rcmsit kernel: lowmem_reserve[]: 0 3327 3773 3773 Jul 3 03:53:04 rcmsit kernel: Node 0 DMA32 free:108368kB min:59340kB low:74172kB high:89008kB active_anon:2321152kB inactive_anon:634816kB active_file:7216kB inactive_file:13476kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:3653624kB managed:3408868kB mlocked:0kB dirty:0kB writeback:0kB mapped:2328kB shmem:500612kB slab_reclaimable:91688kB slab_unreclaimable:184240kB kernel_stack:12304kB pagetables:12780kB unstable:0kB bounce:0kB free_pcp:696kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unrecla imable? no Jul 3 03:53:04 rcmsit kernel: lowmem_reserve[]: 0 0 446 446 Jul 3 03:53:04 rcmsit kernel: Node 0 Normal free:9892kB min:7964kB low:9952kB high:11944kB active_anon:180056kB inactive_anon:187580kB active_file:1904kB inactive_file:4676kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:524288kB managed:457256kB mlocke d:0kB dirty:0kB writeback:0kB mapped:132kB shmem:15424kB slab_reclaimable:14384kB slab_unreclaimable:25720kB kernel_stack:2208kB pagetables:2088kB unstable:0kB bounce:0kB free_pcp:296kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:96 all_unreclaimable? no Jul 3 03:53:04 rcmsit kernel: lowmem_reserve[]: 0 0 0 0 Jul 3 03:53:04 rcmsit kernel: Node 0 DMA: 17*4kB (UEM) 1*8kB (E) 2*16kB (UE) 2*32kB (EM) 2*64kB (UM) 2*128kB (UE) 2*256kB (UE) 2*512kB (EM) 3*1024kB (UEM) 1*2048kB (E) 2*4096kB (M) = 15404kB Jul 3 03:53:04 rcmsit kernel: Node 0 DMA32: 6745*4kB (UEM) 1058*8kB (UEM) 922*16kB (UEM) 546*32kB (UEM) 208*64kB (UEM) 83*128kB (UEM) 24*256kB (UEM) 13*512kB (UEM) 4*1024kB (U) 0*2048kB 0*4096kB = 108500kB Jul 3 03:53:04 rcmsit kernel: Node 0 Normal: 682*4kB (UEM) 180*8kB (UEM) 151*16kB (UEM) 39*32kB (UEM) 18*64kB (UEM) 9*128kB (UEM) 3*256kB (UEM) 2*512kB (EM) 0*1024kB 0*2048kB 0*4096kB = 11928kB Jul 3 03:53:04 rcmsit kernel: Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=2048kB Jul 3 03:53:04 rcmsit kernel: 143267 total pagecache pages Jul 3 03:53:04 rcmsit kernel: 7945 pages in swap cache Jul 3 03:53:04 rcmsit kernel: Swap cache stats: add 284622, delete 276677, find 2373345/2374779 Jul 3 03:53:04 rcmsit kernel: Free swap = 0kB Jul 3 03:53:04 rcmsit kernel: Total swap = 1023996kB Jul 3 03:53:04 rcmsit kernel: 1048476 pages RAM .... Jul 3 03:53:04 rcmsit kernel: [ pid ] uid tgid total_vm rss nr_ptes swapents oom_score_adj name Jul 3 03:53:04 rcmsit kernel: [ 553] 0 553 26364 23 51 223 -1000 sshd Jul 3 03:53:04 rcmsit kernel: [10530] 27 10530 452707 79498 396 91599 -1000 mysqld Jul 3 03:53:04 rcmsit kernel: [30398] 0 30398 582767 20612 109 62 0 java Jul 3 03:53:04 rcmsit kernel: [ 515] 0 515 35253 2031 17 4 0 redis-server Jul 3 03:53:04 rcmsit kernel: [ 520] 0 520 35253 1816 17 1 0 redis-server Jul 3 03:53:04 rcmsit kernel: [ 524] 0 524 35253 1878 17 1 0 redis-server Jul 3 03:53:04 rcmsit kernel: [ 5404] 0 5404 612065 43032 228 0 0 java Jul 3 03:53:04 rcmsit kernel: [16265] 0 16265 520357 65939 261 0 -1000 java Jul 3 03:53:04 rcmsit kernel: [ 3251] 0 3251 1005500 235844 563 0 -1000 java Jul 3 03:53:04 rcmsit kernel: [ 5709] 1000 5709 1007769 134443 358 0 0 java Jul 3 03:53:04 rcmsit kernel: [ 9434] 0 9434 142807 793 38 0 0 YDService Jul 3 03:53:04 rcmsit kernel: Out of memory: Kill process 5709 (java) score 109 or sacrifice child Jul 3 03:53:04 rcmsit kernel: Killed process 5709 (java) total-vm:4031076kB, anon-rss:537772kB, file-rss:0kB, shmem-rss:0kB
|
在03:53的时候,看到oom_killer被唤醒。oom_killer会选择占用内存最高的,然后kill 掉 oom_score_adj 值高的进程。
日志显示,oom_killer 杀的进程号为5709,而5709是一个java进程,所以也可以断定就被是kill掉的tomcat ,它占用的内存为612065,oom_score_adj为0,所以oom_killer选择kill掉它。
插播 oom_killer介绍
Linux下允许程序申请比系统可用内存更多的内存,这个特性叫Overcommit。
Overcommit原理:优化,申请内存不一定马上使用,可能申请后,如果你在使用的时候有资源释放了,那么就说明 你的申请是有效的
容易出现的问题: 如果使用的时候,还是没有释放出足够的内存,当你用到这个Overcommit给你的内存的时候,系统还没有资源的话,OOM killer就跳出来了。
oom_killer:策略:Linux下有3种Overcommit的策略(参考内核文档:vm/overcommit-accounting),可以在/proc/sys/vm/overcommit_memory配置(取0,1和2三个值,默认是0)。
- 0:启发式策略,比较严重的Overcommit将不能得逞,比如你突然申请了128TB的内存。而轻微的overcommit将被允许。另外,root能Overcommit的值比普通用户要稍微多些。
- 1:永远允许overcommit,这种策略适合那些不能承受内存分配失败的应用,比如某些支付模块应用。
- 2:永远禁止overcommit,在这个情况下,系统所能分配的内存不会超过swap+RAM*系数(/proc/sys/vm/overcmmit_ratio,默认50%,你可以调整),如果这么多资源已经用光,那么后面任何尝试申请内存的行为都会返回错误,这通常意味着此时没法运行任何新程序
继续分析
关闭进程选择策略:没用的且耗内存多的程序被枪,作为用户,我们可以通过设置一些值来影响OOM killer做出决策。Linux下每个进程都有个OOM权重,在/proc/pid/oom_adj里面,取值是-17到+15,取值越高,越容易被干掉。
最终OOM killer是通过/proc/pid/oom_score这个值来决定哪个进程被干掉的。这个值是系统综合进程的内存消耗量、CPU时间(utime + stime)、存活时间(uptime - start time)和oom_adj计算出的,消耗内存越多分越高,存活时间越长分越低。总之,总的策略是:损失最少的工作,释放最大的内存同时不伤及无辜的用了很大内存的进程,并且杀掉的进程数尽量少。
具体参考:http://blog.csdn.net/fjssharpsword/article/details/9341563
解决
- 加内存,享受价格成倍增长的乐趣
- 修改/proc/pid/oom_adj,取值-17~15,默认0,越小越不容易选中
- 根据项目情况修改堆栈配置。(下文tomcat 调优)
- 修改代码,比如说大批量处理,不应该一开始把数据全加载到内存中。
- 多说一句:我发现每天固定有个时间点,内存会莫名升高,估计是腾讯云在跑批,所以内存不要用得太紧,多留一点。
tomcat启动慢
原因
Tomcat 7/8使用org.apache.catalina.util.SessionIdGeneratorBase.createSecureRandom类产生安全随机类SecureRandom的实例作为会话ID,这个操作巨慢。
解决
- 在Tomcat环境中解决,可以通过配置JRE使用非阻塞的Entropy Source。在catalina.sh中加入这么一行:
1
| -Djava.security.egd=file:/dev/./urandom
|
- 在JVM环境中解决,打开$JAVA_PATH/jre/lib/security/java.security这个文件
1 2 3
| securerandom.source=file:/dev/urandom 替换成 securerandom.source=file:/dev/./urandom
|
/dev/urandom 和 /dev/random
/dev/random和/dev/urandom是Linux系统中提供的随机伪设备,这两个设备的任务,是提供永不为空的随机字节数据流。很多解密程序与安全应用程序(如SSH Keys,SSL Keys等)需要它们提供的随机数据流。
这两个设备的差异在于:
/dev/random的random pool依赖于系统中断,因此在系统的中断数不足时,/dev/random设备会一直封锁,尝试读取的进程就会进入等待状态,直到系统的中断数充分够用.
/dev/random设备可以保证数据的随机性。
/dev/urandom不依赖系统的中断,也就不会造成进程忙等待,但是数据的随机性也不高。
tomcat 调优
1
| JAVA_OPTS="-Xms512m -Xmx2048m -XX:PermSize=256M -XX:MaxPermSize=512m -Ddubbo.shutdown.hook=true -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/administrator/software/server/tomcat-rcm/dump/oom.hprof"
|
以上为2核4G的配置,只做参考,根据自己项目修改,在catalina.sh加上。
Xmx:用来设置你的应用程序能够使用的最大内存数,如果程序要花很大内存的话,那就需要修改增加此数的值。
Xms:用它来设置程序初始化的时候内存栈的大小,增加这个值的话你的程序的启动性能会得到提高。
XX:PermSize:表示非堆区初始内存分配大小,其缩写为permanent size(持久化内存)
XX:MaxPermSize:表示对非堆区分配的内存的最大上限
XX:+HeapDumpOnOutOfMemoryError:JVM会在遇到OutOfMemoryError时拍摄一个“堆转储快照”,并将其保存在一个文件中。
JAVA_OPTS这个变量会在tomcat启动时应用,但是也会在执行shutdown.sh时应用。当你关闭tomcat时如果告诉你内存不够,不要惊慌,这只是在执行shutdown.sh会启动另一个程序,而这时申请不到像启动时那么大的内存,就会报错,你可以忽略该异常,执行kill来关闭tomcat。
tomcat访问项目时不需要加项目名称
修改conf目录下的server.xml配置
1 2 3 4
| <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false"> <Context path="" docBase="F:\temp" reloadable="false" /> </Host>
|