存档

作者存档

又见OutOfMemory——一次内存溢出故障诊断全过程

2010年11月9日 admin 7 条评论

这是一个几月前的案例,问题比较典型,在分析和事后学习的过程中让我对本地内存溢出有了一定的了解。在此和大家分享。

先说一下背景,应用环境是AIX5.3+WebSphere6.0.2.37。在今年的一季度曾发生过几次OOM故障,当时通过几次内存参数优化,最后确定为“-Xgcprolicy:gencon –Xms512m –Xmx1280m –Xmn200m”,此后稳定了半年,直到此次再发生应用宕机。

赶到现场,发现profiles目录下生成有javacore和heapdump文件,Javacore的第一行“Cause of thread dump : Dump Event "systhrow" (00040000) Detail "java/lang/OutOfMemoryError" received”表明了宕机的原因是OOM,但是令我困惑的是这段内容:

0SECTION       MEMINFO subcomponent dump routine
NULL           =================================
1STHEAPFREE    Bytes of Heap Space Free: 818cb70
1STHEAPALLOC   Bytes of Heap Space Allocated: 20000000

在分配的512MB(十六进制20000000)的堆空间中,有129MB(818cb70)空闲空间,按理说,这种情况下不该发生OutOfMemory。就算有申请一个大对象,同时JVM堆的新生代由于碎片原因没有连续空间满足要求,那么应该发生堆扩展,所以此次内存溢出不是堆(Heap)溢出,GC日志的分析也支持了这一点。

既然Javacore无法得到有用信息,我把目光转向了SystemErr.log,在对应日期的地方,我发现了大量如下报错:

[8/26/10 14:12:57:860 GMT+08:00] 0000002f SystemErr     R Exception in thread "WebContainer : 1" java.lang.RuntimeException: java.lang.OutOfMemoryError: Failed to create a thread: retVal -1073741830, errno 11
[8/26/10 14:12:57:860 GMT+08:00] 0000002f SystemErr     R     at com.ibm.io.async.ResultHandler.runEventProcessingLoop(ResultHandler.java:801)
[8/26/10 14:12:57:860 GMT+08:00] 0000002f SystemErr     R     at com.ibm.io.async.ResultHandler$2.run(ResultHandler.java:881)
[8/26/10 14:12:57:860 GMT+08:00] 0000002f SystemErr     R     at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1497)
[8/26/10 14:12:57:860 GMT+08:00] 0000002f SystemErr     R Caused by: java.lang.OutOfMemoryError: Failed to create a thread: retVal -1073741830, errno 11
    at java.lang.Thread.startImpl(Native Method)
    at java.lang.Thread.start(Thread.java:980)
    at com.ibm.ws.util.ThreadPool.addThread(ThreadPool.java:630)
    at com.ibm.ws.util.ThreadPool$3.run(ThreadPool.java:1148)
    at com.ibm.ws.security.util.AccessController.doPrivileged(AccessController.java:63)
    at com.ibm.ws.util.ThreadPool.execute(ThreadPool.java:1146)
    at com.ibm.ws.util.ThreadPool.execute(ThreadPool.java:1040)
    at com.ibm.ws.runtime.WSThreadPool.execute(WSThreadPool.java:151)
    at com.ibm.io.async.ResultHandler.startHandler(ResultHandler.java:248)
    at com.ibm.io.async.ResultHandler.runEventProcessingLoop(ResultHandler.java:570)
    at com.ibm.io.async.ResultHandler$2.run(ResultHandler.java:881)
    at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1497)

在事后的学习中,我知道“Java.lang.OutOfMemoryError: unable to create native thread” 这样的异常是在说,本地内存耗尽,从而新的线程无法创建。而在当时我第一感觉是操作系统参数设置问题,之前我曾写过一篇由于nofile参数导致Too many open file的故障。于是我运行如下命令

#lsattr -El sys0 -a maxuproc
maxuproc 128 Maximum number of PROCESSES allowed per user True

    然后运行chgsys修改默认的128为1024,这里我犯了一个错误,WebSphere单个Server就是一个Java进程,错误日志里是不能创建一个thread,而非process,与Oracle会创建多个oracle进程不一样。果然两天后又出现了同样的问题。
    这一次的SystemErr日志中,除了上述的内容,还多了

[8/24/10 9:55:19:813 GMT+08:00] 00000036 SystemErr     R Exception in thread "WebContainer : 4" java.lang.RuntimeException: java.lang.OutOfMemoryError: Unable to allocate 8192 bytes of direct memory after 5 retries
[8/24/10 9:55:19:813 GMT+08:00] 00000036 SystemErr     R     at com.ibm.io.async.ResultHandler.runEventProcessingLoop(ResultHandler.java:801)
[8/24/10 9:55:19:813 GMT+08:00] 00000036 SystemErr     R     at com.ibm.io.async.ResultHandler$2.run(ResultHandler.java:881)
[8/24/10 9:55:19:813 GMT+08:00] 00000036 SystemErr     R     at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1497)
[8/24/10 9:55:19:813 GMT+08:00] 00000036 SystemErr     R Caused by: java.lang.OutOfMemoryError: Unable to allocate 8192 bytes of direct memory after 5 retries
    at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:197)
    at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:303)
    at com.ibm.ws.buffermgmt.impl.WsByteBufferPoolManagerImpl.allocateBufferDirect(WsByteBufferPoolManagerImpl.java:656)
    at com.ibm.ws.buffermgmt.impl.WsByteBufferPoolManagerImpl.allocateCommon(WsByteBufferPoolManagerImpl.java:570)
    at com.ibm.ws.buffermgmt.impl.WsByteBufferPoolManagerImpl.allocateDirect(WsByteBufferPoolManagerImpl.java:506)
    at com.ibm.io.async.ResultHandler.runEventProcessingLoop(ResultHandler.java:498)
    at com.ibm.io.async.ResultHandler$2.run(ResultHandler.java:881)
    at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1497)
Caused by: java.lang.OutOfMemoryError
    at sun.misc.Unsafe.allocateMemory(Native Method)
    at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:184)
    … 7 more

我们可以看到是由于DirectByteBuffer无法分配导致的内存溢出,而Native Method指明了这是“本地”的溢出。通过这两个关键字,我查到了IBM的一份BUG记录:PK31010: OUTOFMEMORYERROR DUE TO DIRECTBYTEBUFFER,但是我的版本已是最新,无奈继续搜寻。

Troubleshooting native memory issues这份文档中,介绍了3种在WebSphere中最常见的导致OOM的原因。其中第二个DirectByteBuffer use就是我们问题的症结。

Java 1.4 中添加的新 I/O (NIO) 类引入了一种基于通道和缓冲区来执行 I/O 的新方式。就像 Java 堆上的内存支持 I/O 缓冲区一样,NIO 添加了对直接 ByteBuffer 的支持(使用java.nio.ByteBuffer.allocateDirect() 方法进行分配),ByteBuffer 受本机内存而不是 Java 堆支持。直接 ByteBuffer 可以直接传递到本机操作系统库函数,以执行 I/O — 这使这些函数在一些场景中要快得多,因为它们可以避免在 Java 堆与本机堆之间复制数据。

    对于在何处存储直接 ByteBuffer 数据,很容易产生混淆。应用程序仍然在 Java 堆上使用一个对象来编排 I/O 操作,但持有该数据的缓冲区将保存在本机内存中,Java 堆对象仅包含对本机堆缓冲区的引用。非直接 ByteBuffer 将其数据保存在 Java 堆上的 byte[] 数组中。下图展示了直接与非直接 ByteBuffer 对象之间的区别:

     直接与非直接 java.nio.ByteBuffer 的内存拓扑结构

ByteBuffer 内存安排

    直接 ByteBuffer 对象会自动清理本机缓冲区,但这个过程只能作为 Java 堆 GC 的一部分来执行,因此它们不会自动响应施加在本机堆上的压力。GC 仅在 Java 堆被填满,以至于无法为堆分配请求提供服务时发生,或者在 Java 应用程序中显式请求它发生(不建议采用这种方式,因为这可能导致性能问题)。

    发生垃圾收集的情形可能是,本机堆被填满,并且一个或多个直接 ByteBuffers 适合于垃圾收集(并且可以被释放来腾出本机堆的空间),但 Java 堆几乎总是空的,所以不会发生垃圾收集。

摘自《理解JVM如何使用Windows和Linux上的本机内存》

解决此问题的方法,在文档中给出的是禁止异步A/O,通过在Web Container中设置参数来避免上节中所出现的由于Java堆空闲而不发生垃圾回收,导致本地堆撑满的情况。

    Servers -> Application Servers -> serverName -> Web Container Settings -> Web Container -> Custom Properties:
    Press New:
    Add the following pair:

      Name: com.ibm.ws.webcontainer.channelwritetype
      Value: sync

    Press OK, and then save the configuration.

    添加此属性后应用又恢复正常,但是原先提高性能的特性反而导致内存溢出,违背了初衷,现在的做法只能算一个妥协。我会继续查找此问题的解决方法,最不济也要有个使用NIO的Best Practice。

文档中另两个容易导致本地堆OOM的原因是:

过大的堆上限

Memory layout of a vm in the os

我们知道32位机器单个进程可以访问的内存地址空间为4G,如右图所示,但实际情况下Windows系统由于内核态和用户态的划分,用户态只有2G的空间,Linux和AIX的可用空间大一点,但也在3G左右。,由于JVM实例进程寻址是一定的,所以Heap大小和Native Area此消彼长。而Native Area中有一部分就是供给线程的内存空间。

“应用程序中的每个线程都需要内存来存储器堆栈(用于在调用函数时持有局部变量并维护状态的内存区域)。每个 Java 线程都需要堆栈空间来运行。根据实现的不同,Java 线程可以分为本机线程和 Java 堆栈。除了堆栈空间,每个线程还需要为线程本地存储(thread-local storage)和内部数据结构提供一些本机内存。堆栈大小因 Java 实现和架构的不同而不同。一些实现支持为 Java 线程指定堆栈大小,其范围通常在 256KB 到 756KB 之间。”

1.5后一般线程堆栈大小为1M,“对于拥有数百个线程的应用程序来说,线程堆栈的总内存使用量可能非常大。如果运行的应用程序的线程数量比可用于处理它们的处理器数量多,效率通常很低,并且可能导致糟糕的性能和更高的内存占用”(摘自《理解JVM如何使用Windows和Linux上的本机内存》)

解决此问题的方法:设置恰当的JVM最大堆,32位不要超过1.5G;设置恰当的线程池大小(一般50~100),当线程使用较多时, 使用-Xss256k参数设置线程指定堆栈大小(IBM JDK还可以使用-Xmso ××k来设置Stack size for OS Threads 32-bit);更换64位的JDK。

第三个原因是线程池的TLS泄漏

这个现象我倒没见到过,如果你的thread dump里下面三个属性里的值有大于300,那么就要注意了

"WebContainer : 1003" (TID:0×37D62000
"Default : 338" (TID:109934D0
"TCPChannel.DCS : 303" (TID:0×4D720000

解决的方法是设置线程池的最小最大值一致。

总有一些世界观,是傻逼呵呵地矗立在那里的

2010年8月15日 admin 1 条评论

上一篇标题是be foolish,这一篇就就要写idiot了。《Three Idiots》,或者又叫《三傻大闹宝莱坞》,让无聊的周末晚上,变得丰富多彩起来。

这是一部校园青春片,归类在喜剧片里,于是自然的让屏幕前的我乐不可支,但如果仅仅如此,那么它就是美国校园喜剧的翻版,我也不会在这个技术博里分享观看后感受。作为同样经历经济高速发展的邻居——印度,这部电影所探讨的理想与现实、爱情与金钱、注重结果还是过程,深深的感触到了我。

噪鹃从来不自己筑巢,他只在别人的巢里下蛋,要孵蛋的时候他们会怎样?他们会把其它的蛋从巢里挤出去,竞争结束了,他们的生命从谋杀开始,这就是大自然——要么竞争,要么死……

丛林法则,优胜劣汰,“病毒”的开场白在父母送你去补习班的时候,在你选择大学专业的时候,会听到各种各样类似的版本。在大学里,为了绩点而奋斗,只看为考试而划重点的内容,不是为了知识,而是为了“从这5年后,当你看到你的朋友买车买房时不会诅咒自己。”

一定要让自己活在“高压锅”里么?某种程度上是的,当你的父母把唯一的空调装在你的房间里让你安心学习,把自己年轻时的遗憾作为目标寄托在你的身上时,当你家里有个生病的父亲、没有嫁妆出嫁的姐姐、以及辛苦照顾这个家的母亲时,我们很难有勇气说出:过自己喜欢过的生活、做自己喜欢做的事,哪怕钱赚的再少只要自己觉得幸福就足够了(这应该就是所谓的责任感使命感吧)。所以我们像男主角的两个朋友,要么生活在“虚伪”里,要么生活在“恐惧”中。

于是我们一个个的“成功”了,但是“理想”这种东西,要么戒了,要么又作为自己的遗憾,硬加到自己的孩子身上。

影片中的爱情,则是以一场订婚一场逃婚来演绎,笔墨不多,却同样有内心抉择的痛苦。是嫁给一个生活贴了标签的成功男士,还是去找能让心情像头发一般飘逸起来的男主角?影片中女主当然选择了后者,但面对现实的生活压力,有多少人能无视暂时的闲言碎语,有多少姻缘因为经济的原因而拆散,又有多少夫妻在坚硬的钢筋水泥的城市里扎不下根来。

所以当影片放到Farhan他爸爸说到:把(电脑)退了,换一部专业摄像机,钱还不够就向我要的时候。放到Raju Rastogi说出“断了两条腿,我才真正站了起来,获得这样的生活态度不容易,我不会放弃”的时候,当病毒主任最终也说出“做你想做的”,并把32年都没送出的太空笔插到Rancho的衣领上时,我再也无法控制自己的眼泪不夺眶而出。

不过看完片后思及自身,又让我感到另外一种悲哀——我不知道自己除了工作之外,还有什么值得投入精力的爱好,不知道自己真正想要的生活是什么。乔治奥威尔痛苦的是“英国人的[阶级]烙印是打在舌头上的”,“他的一切疙瘩都来自于这个事实:他认为他应该去爱他的同胞,但是他连同他们随便交谈都做不到。”而我则是“认为应该去过自己喜欢的生活,但是却连什么是自己喜欢的都不知道”。长年的填鸭式的教育,父母的期盼,老娘舅里形形色色的人生故事,让我以为生活就是如此。看来我们都需要一个像Rancho这样的朋友,告诉我们世界上还有种不合时宜的世界观,也能通向成功。

一个没有一出生就背负使命的园丁的儿子很少见,那至少让我们记住这句话“Follow excellence,Success will chase you !"

分类: 生活感想 标签:

Linux 性能监控

2010年5月19日 admin 没有评论

我在Unix/Linux分类中曾转过《Linux System and Performance Monitoring》系列的文章,今天又在Vpsee看到了类似的几篇。内容基本一样,但是排版好许多,最主要是读起来很流畅,一步步的递进很好,没有英文资料翻译过来的那种生硬感,估计是根据那个系列结合自己经验重写过的。

下午把vpsee逛了圈,感觉是VPS服务商里blog写的最有技术含量的,不仅有linux下的应用、优化,还有架构的分析以及较少看到资料的瘦客户端介绍。还有,我经常逛的LinuxTOY就是架设在他们的服务器上的。

Linux 性能监测:介绍

Linux 性能监测:CPU

Linux 性能监测:Memory

Linux 性能监测:IO

Linux 性能监测:Network

分类: 每周精华 标签: ,

WebLogic9和10集群安装配置过程

2009年9月6日 admin 2 条评论

weblogic的集群很早以前做过,当时写了一份文档,最近真好又有机会实验了Weblogic 10.3的集群配置,两相比较,同时对照着文档过了一遍,确定下文对weblogic9和10都适用。截了图也方便新手学习。

环境

平台:两台Windows 2003 SP2(SP版本要一样),必须位于同一网段,并且必须是IP广播(UDP)可到达的

软件:Weblogic9.1

拓扑

在A机上建立一个Administrator Server,作为管理节点;在AB机上分别建立Managed Server,加入集群appCluster,作为应用程序的运行环境;在B机上分别建立Proxy Server,作为提供外部访问的服务地址。

机器 配置信息 角色 备注
app1 IP:*.*.*.* PORT:7001 AdminServer 管理服务器
app1 IP: *.*.*.*PORT:7002 Managed Server 受管服务器
app2 IP: *.*.*.*PORT:7002 Managed Server 受管服务器
app2 IP: *.*.*.*PORT:80 Proxy Server 代理服务器

安装过程

在机器A上

创建新的Domain

选择“Tool- Configuration Wizard”,单击“下一步”按钮

clip_image002

选择安装域源

选择安装“WebLogic Server”,单击“下一步”按钮

clip_image004

配置管理员用户名和密码

输入Weblogic管理员管理服务器(AdminServer)的用户名和密码,在此以“weblogic”作用登录管理服务器的用户名和密码,单击“下一步”按钮

阅读全文…

分类: weblogic, 中间件 标签: ,

WebSphere Information Integrator安装过程

2009年5月1日 admin 1 条评论

 

Wii是WebSphere Information Integrator的简称,原先是DB2中的一个组件,现在是单独的软件包,包括Websphere Federation Server,Websphere Replication Server和Websphere Data Event Publisher。现在WII又改名成Infosphere information Integrator,所以搜索找资料的时候,注意关键词的选用。

我这次装的版本是RepServ_9.5_Win_32-bit,补丁打到了sp2,sp3补丁地址见文中。

安装主机

192.16.29.234

Oracle 9.2.0.7,本地建立客户端并配置好

192..16.29.235:1521 sid:reposity

 

 

 

选择安装目录

 

选择需要安装的包装器

 

勾上简体中文语音

 

填入本地以创建的一个具有管理员权限的用户

 

 

安装完成。

 

阅读全文…

一次因为系统参数而导致的WAS无响应

2009年4月30日 admin 1 条评论

最近一个项目在做压力测试的时候,压力测试人员设置完脚本运行8小时后,第二天总会发现虽然脚本运行正常,但是有一个节点上的Server没有相应。查看日志说日志没有任何报错,记录的最后一条总是前一天的半夜。因为半夜正好是做批处理的时候,一开始怀疑是否是这导致的宕机,但是停掉批处理程序后依旧发生这种现象(而且是在下班时刻发生),也就排除了这个可能。

下面是我的排错经过:

查看无法提供服务的app2的systemout.log日志,发现从昨天晚上5点09分后,没有新的日志输出。最后的日志为

[4/15/09 17:09:28:193 GMT+08:00] 00000953 IncidentStrea W com.ibm.ws.ffdc.IncidentStreamImpl write FFDC0013I: FFDC failed to write to incident stream file /was/IBM/WebSphere/AppServer/profiles/AppSrv01/logs/ffdc/APP2_44ca44ca_09.04.15_17.09.28_0.txt, caught exception java.lang.NullPointerException

 

查看system.err文件,没有发现有价值的内容

于是查看ffdc目录下的17:09生成的日志,在APP2_7b2c7b2c_09.04.15_17.09.27_0.txt中发现有Stack Dump = javax.imageio.IIOException: Can’t create output stream!信息。

在APP2_2e542e54_09.04.15_17.09.27_1.txt中发现有Caused by: [SOAPException: faultCode=SOAP-ENV:Client; msg=Error opening socket: java.net.SocketException: Too many open files; targetException=java.lang.IllegalArgumentException: Error opening socket: java.net.SocketException: Too many open files]

 

于是使用ulimit –a命令查看AIX的limit参数

APP2:/was/IBM/WebSphere/AppServer/profiles/AppSrv01/logs/ffdc#ulimit -a
time(seconds) unlimited
file(blocks) 2097151
data(kbytes) 131072
stack(kbytes) 32768
memory(kbytes) 32768
coredump(blocks) 2097151
nofiles(descriptors) 2000

 

发现一个进程可以打开的最大文件数为2000.

用ps –ef | grep java命令找到app2的sid,然后用procfiles sid命令查看app2的进程现在打开了多少文件

APP2:/was/IBM/WebSphere/AppServer/profiles/AppSrv01/bin#procfiles 815304

815304 : /was/IBM/WebSphere/AppServer/java/bin/java -Declipse.security -Dwas.status.sock

Current rlimit: 2000 file descriptors

………………

1993: S_IFREG mode:0444 dev:10,15 ino:160745 uid:0 gid:0 rdev:0,0

O_RDONLY size:15694

1994: S_IFREG mode:0444 dev:10,15 ino:160745 uid:0 gid:0 rdev:0,0

O_RDONLY size:15694

1995: S_IFREG mode:0444 dev:10,15 ino:160745 uid:0 gid:0 rdev:0,0

O_RDONLY size:15694

1999: S_IFREG mode:0444 dev:10,15 ino:164300 uid:0 gid:0 rdev:0,0

O_RDONLY size:1042

打开文件数已经到达2000,于是app2无法创建新的systemout.log日志文件,也就无法再提供服务。

由此分析得AIX的nofiles参数对于websphere来说不够大(对于Hp-ux,安装前调整系统参数时就需要把maxfiles调整为8192,但是AIX内核是自调整,所以IBM安装要求上没有提到需要手动修改什么参数)特别是在压力测试时候,短时间内的积累可能会达到2000的限制,实际生产环境中也可能会遇到如此情况,当然集群中另外一台APP跑的好好的也挺让人奇怪。

接下来就是调整ulimit中的nofiles限制到4096,然后重启nodeagent,再由控制台重启app即可。当然也可以直接用命令重启app,但是因为nodeagent没有重启过,环境变量没有生效,下次在控制台中重启app后nofiles依旧会被限制在2000。