存档

‘Websphere系列’ 分类的存档

又见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

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

Java 类加载器的又一篇文章

2010年3月3日 hashei 2 条评论

之前写过两篇关于java类加载的文章,分别是:《WebSphere的类加载机制和故障排查》,《再谈WebSphere的类加载和故障排查》。今天在IBM网站上看到一篇《深入探讨 Java 类加载器》,分享出来炒炒冷饭。以后遇到问题的时候也能有点方向。

Java 虚拟机默认的行为就已经足够满足大多数情况的需求了。不过如果遇到了需要与类加载器进行交互的情况,而对类加载器的机制又不是很了解的话,就很容易花大量的时间去调试 ClassNotFoundExceptionNoClassDefFoundError 等异常。本文将详细介绍 Java 的类加载器,帮助读者深刻理解 Java 语言中的这个重要概念。

软硬兼施 优化 WebSphere Application Server

2010年2月21日 hashei 没有评论

之前看的很多was优化案例,包括自己实施过程中都只从WebSphere的角度来考虑问题,虽然WAS的优化中包括了操作系统层面的优化,比如对AIX、HPUX的系统参数做出调整,对于网络tcp的参数做出改动,但并没有更进一步,在进行LPAR分区前就做出完整的规划。

WebSphere管理员和硬件与操作系统管理员往往交流不多,且是串行的实施顺序。虽说也就那么做下来了,但是是否充分发挥了硬件的资源,是否达到了最好的性能,最稳定的运行,还是可以有改进的余地?都值得探讨。IBM网站上这三篇文章可谓打通了两种管理员之间的任督二脉,看懂了总有好处。

本书提供了整体系统观点,重点关注在 Power System 和 AIX 上运行 WebSphere Application Server 负载的环境的端到端系统部署、调优和管理方法。因而,本书为两类截然不同的技术读者架起了一座桥梁,也就是硬件和操作系统管理员与 WebSphere Application Server 应用软件工程师。我们都了解,在典型的企业环境中,这两类技术读者需要密切合作,但仍然有着不同的视角和职责。然而,对于企业来说,在度量 Power System 和 AIX 上运行的 WebSphere Application Server 投资的成败时,最终要取决于所有系统架构师能否很好地理解如何同心协力地利用每种产品的特有优势。因而,我们首先要做的是澄清各种观点。

在 Power System 上优化 WebSphere Application Server,第 1 部分: 入门以及优化策略

在 Power System 上优化 WebSphere Application Server,第 2 部分: 设置 Power System 硬件和分区(上)

在 Power System 上优化 WebSphere Application Server,第 3 部分: 设置 Power System 硬件和分区(下)

有空么可以再看看《WebSphere Application Server V6.1 Planning and Design WebSphere Handbook Series》

使用manageprofiles来建立websphere的profile

2010年1月20日 hashei 1 条评论

在Unix的机器上(包括AIX、HPUX)安装websphere64位的话,想要建立Profile是没有图形化界面的,需要使用manageprofiles命令,下面就详细描述一下如何使用这个命令。

1、建立DM的profile,使用的模版在{install_root}/profileTemplates/dmgr下
句法为:
manageprofile.sh -create –templatePath {install_root}/profileTemplates/dmgr

调用参数为:
-create 建立一个profile. (必选)
-templatePath 指定使用模版的路径. (必选) 建立DM的路径为{install_root}/profileTemplates/dmgr
-profileName 指定profile的名字. (可选)
-profilePath 指定proflile需要建立的路径. (可选)
-hostName 指定profile对应的主机名称. (可选)
-nodeName 指定profile对应的node的名称. 此名称必须在同一cell中唯一. (可选)
-cellName 指定profile对应的cell的名称. 每一个profile的cell名称必须唯一. (可选)
-isDefault 讲此profile指定为缺省的profile,在运行命令期间不需要使用profileName的参数. (可选)
-omitAction 附加条件. (可选)
-adminUserName 在指定administrative security选项后的管理员名称. (可选)
-adminPassword 在指定administrative security选项后的管理员密码. (可选)
-portsFile 指定profile的端口设定文件. 此参数和-startingPort或-defaultPorts参数冲突. (可选)
-startingPort 制度建立profile是的所有的端口的起始的端口号. 同-portsFile或者-defaultPorts参数冲突. (可选)
-defaultPorts 对新建立的profile使用缺省的端口号. 同-portsFile 或 -startingPort 参数冲突. (可选)
-validatePorts 进行端口号验证,保证使用的端口号没有已经被用或者被保留. (可选)
-enableAdminSecurity 新建立的profile使用用户密码方式认证,使用此参数后面跟 true. (可选) 
-enableService 作为linux的service使用,使用此参数后面跟 true. (可选) 
-serviceUserName 指定作为service使用时的用户. (可选)

-webServerType 指定Web server的类型. (可选)
-webServerOS 指定Web server的操作系统的类型. (可选)

例子:
export WAS_HOME=
export PRO_HOME=
以上指定websphere的安装路径和profile的路径,或者输入命令时替换

manageprofiles.sh -create \
-templatePath $WAS_HOME/profileTemplates/dmgr \
-profilePath $PRO_HOME/Dmgr01 \
-nodeName "nodename" \
-cellName "cell name" \
-enableAdminSecurity true \
-adminUserName wasadmin \
-adminPassword password \
-profileName Dmgr01 \
-hostName `hostname` \

2、建立custom的profile,使用的模版为{install_root}/profileTemplates/managed
句法为:
manageprofile.sh -create -templatePath {install_root}/profileTemplates/managed
调用参数同上:

要联合到DM的例子
manageprofiles.sh -create \
-templatePath $WAS_HOME/profileTemplates/managed \

-profileName AppSrv01 \

-profilePath $PRO_HOME/AppSrv01 \
-nodeName test01 \

-hostName `hostname` \
-cellName test1 \
-dmgrHost 192.168.1.100 \
-dmgrPort 8879 \
-dmgrAdminPassword password \
-dmgrAdminUserName wasadmin

不用联合到DM,而后用addNode.sh联合的例子
manageprofiles.sh -create \
-templatePath $WAS_HOME/profileTemplates/managed \
-profilePath $PRO_HOME/AppSrv02 \
-nodeName test02 \
-cellName test2 \
-profileName AppSrv02 \
-hostName `hostname`

addNode.sh 192.168.1.100:8879 -username wasadmin -password password

3、建立appsrv的profile,使用的模版在install_root/profileTemplates/default
句法为:
manageprofile.sh -create -templatePath install_root/profileTemplates/default
调用参数同上:例子么也就那样

4、对于集群来说,建立DM profile,然后建立多个custom的profile,addNode进去即可。或者把现有的app profile addNode到DM的概要文件中,然后通过克隆建立集群。

两个关于JAVA性能优化的PPT

2009年12月24日 hashei 2 条评论

Tools and Tips to Diagnose Performance Issues

分析性能问题的一些工具和建议

Use JMeter as a Performance Testing Tool

性能检测工具——Jmeter介绍

案例研究: 调优 WebSphere Application Server V7 性能

2009年12月23日 hashei 2 条评论

好东西,赶紧拉下来,介绍的太完整了,从内存调整到各种池大小优化。developerworks上都难得一见的完整调优案例。

原文链接:http://www.ibm.com/developerworks/cn/websphere/techjournal/0909_blythe/0909_blythe.html

IBM WebSphere Application Server 是一种可靠的企业级应用服务器,它提供了一组核心组件、资源和服务,供开发人员在应用程序中使用。每个应用程序都具备特有的需求,并且经常采用截然不同的方式使用应用服务器的资源。为了提供高度灵活性并支持这种广泛的应用程序,WebSphere Application Server 提供了一组全面的参数来帮助您增强对应用程序的调优。

应用服务器已经为最常用的调优参数设置了默认值,以确保能为最广泛的应用程序提供开箱即用的性能改善。但是,由于任意两个应用程序都不可能采用完全相同的方式来使用应用服务器,因此无法确保一组调优参数能适用于所有应用程序。这也突显了对应用程序执行有重点的性能测试和调优的重要性。

本文将讨论在 WebSphere Application Server V7.0(和之前发行版)中最常使用的一些参数,以及对它们进行调优的方法。与其他相关文章提供的调优建议不同,本文将使用 Apache DayTrader Performance Benchmark Sample 案例研究作为本文的上下文。借助 DayTrader 应用程序,您可以清楚地确定所使用的主要服务器组件,对这些区域进行重点调优,并观察各种调优更改所带来的收益。

在继续阅读之前,需要记住关于应用服务器性能调优的一些事项:

  • 提高性能经常会牺牲应用程序或应用服务器的一些特性或功能。在计算性能调优更改时应该仔细考虑性能和特性之间的权衡。
  • 应用服务器之外的一些因素有时会影响性能,包括硬件和操作系统配置、系统中运行的进程、后端数据库资源的性能、网络延迟等等。您在自己执行性能评估时,必须将这些因素考虑在内。
  • 此处讨论的性能改善仅针对 DayTrader 应用程序,并且特定于此处描述的工作负载组成及所支持的硬件和软件栈。您通过本文介绍的调优更改实现的应用程序性能提升肯定会有所不同,并且应该通过您自己的性能测试进行评估。

阅读全文…