http://www.ibm.com/developerworks/cn/aix/library/es-Javaperf/es-Javaperf4.html
最大化 AIX 上的 Java 性能,第 4 部分: 监视流量
这个由五个部分组成的系列提供了若干技巧和技术,这些技巧和技术通常用于优化 Java™ 应用程序,以便在 AIX® 上实现最佳的性能。本文讨论 I/O 和网络可能成为瓶颈的情况。
引言
这是由五部分组成的有关 AIX 上的 Java 性能优化系列中的第四篇文章。强烈建议您在进一步继续之前阅读本系列中的第 1 部分(如果您还没有这样做的话)。
本文讨论可能成为性能瓶颈的另外两个方面:
- 网络
- 磁盘 I/O
这两个方面通常作为特定于 AIX 的问题出现,需要独立于 Java 应用程序进行优化。因此,本文暂不使用第 2 部分和第 3 部分 中使用的形式,而是集中于如何找到完成优化工作所需要的信息。因此,本文仅提供了少量的技巧,但是我们希望将整体的性能工具讨论与这里的少量技巧相结合,为您提供足够的信息以着手开始性能优化工作。
I/O 和网络瓶颈
本文的目的是讨论 I/O 或网络可能成为瓶颈的情况。
如果您已经阅读了本系列中之前的每篇文章,我们希望您开始了解每个较小的部分是如何适应全局的。我们已尝试基于这些技巧的常见适用领域对其进行分类,但是此分类决不是互斥的。对于网络和 I/O,您不会如此容易地看到实际问题原因,但是您最终会感觉到它们对应用程序的影响。只有对应用程序的充分了解才能指导您确定问题根源。例如,在本系列的前面,我们讨论了确保堆不分页的重要性。使用 -Xmx 开关指定的最大堆大小应该小于系统上安装的物理内存总量(通过“bootinfo –r”或“lsattr -El sys0 -a realmem”来显示,有关更多的此类命令,请参见“AIX Commands you should not leave home without”。
诸如 topas 和 iostat 等工具可以显示各个磁盘的使用情况,但是在大多数情况下,问题根源要么是 GC 周期,要么是某个已知的功能部分;如果您了解自己的应用程序,则确定问题根源应该是相当简单的。诸如 filemon 等工具甚至会告诉您哪些文件正在受到访问,从而从优化工作中排除猜测成分。如果您的 Java 应用程序性能由于错误配置的系统而受到影响,则是改变重点并改为考虑系统性能优化的时候了。例如,磁盘瓶颈的解决方案可以是把数据进行有效的分布或者是选择使用更加高速的磁盘。此主题超出了本文的范围;有关此主题的更多信息,请参阅诸如Understanding IBM eServer pSeries Performance and Sizing 等红皮书。
配置网络缓冲区和优化其他网络参数可以对网络密集型应用程序产生重要影响。有关网络可优化参数的很好参考资料是 Performance Management Guide 的 Network Tunable Parameters 部分。一些流行的调整涉及到 thewall、socketthresh、sbmax、somaxconnect、tcp_sendspace、tcp_recvspace、rfc1323 等等。此信息既不特定于 AIX,也不特定于 Java,但是对于网络密集型应用程序,这应该是进行性能优化的第一步。
本部分的其余内容将简单介绍一些常用的工具和如何检测特定于 Java 的问题。有关更多详细信息,请参阅 AIX 5L Performance Tools Handbook 和 Understanding IBM eServer pSeries Performance and Sizing。
vmstat
多用途的 vmstat 命令应该已经是您的好朋友了。对于 I/O 工作,应查看 CPU 部分中的 wa(I/O 等待)值。如果此值非常高,则可能存在磁盘瓶颈,然后可以使用 iostat 更详细地查看磁盘使用情况。
iostat
iostat 是用于确定系统是否具有 I/O 瓶颈的理想工具。它显示针对所有磁盘的读写速度。这使其成为确定您是否需要在多个磁盘间“分散”磁盘工作负载的理想工具。该工具还会报告与 vmstat 相同的 CPU 活动。
当您的应用程序正在运行时,从简单的 iostat -s
开始,以确定系统总体上在做什么。此命令打印的内容如下:
tty: tin tout avg-cpu: % user % sys % idle % iowait 0.3 232.9 13.8 19.1 27.4 39.6 Disks: % tm_act Kbps tps Kb_read Kb_wrtn hdisk0 28.7 291.4 35.0 176503 2744795 hdisk1 0.0 0.4 0.0 3537 0 hdisk7 1.7 34.9 9.8 8920 341112 hdisk14 24.5 1206.1 36.2 1188404 10904509 hdisk18 0.0 1.2 0.1 10052 2046 hdisk8 2.1 36.8 10.5 10808 357910
查看 %iowait 数据以确定系统是否在花太多的时间等待 I/O 完成。如果系统正在分页,这就是要观察的数据。但是要注意,单凭此数据还不足以确定系统上发生的情况。例如,如果您在应用程序中写入顺序文件,则较高的 %iowait 值是很正常的。
%tm_act 显示特定磁盘的活动时间百分比。上面的跟踪显示了一个非常有趣的场景;其中 %iowait 接近 40%,但是 tm_act was 无论如何都没有接近 100%,而是仅在 30% 下面徘徊。在其上取得上述跟踪的系统有一个通过光纤通道连接的存储,结果证明瓶颈是到 SAN 存储的路由。一旦弄清楚后,看起来就非常容易了!
还可以使用 # iostat -at <interval> <count>
或 iostat -sat ...
,这两个命令将给出适配器的 tps 和 KBps 值(以及读写速度)。-s 标志将为您提供总体系统统计信息。
netstat
对于网络优化,netstat 是理想的工具。netstat -m
可用于查看 mbuf 内存使用情况,它将告诉您有关套接字和网络内存使用情况的信息。如果使用了 no -o extendednetstats=1
,则 netstat -m
将显示更多详细信息,但这样会产生性能影响,只应用于诊断目的。当使用 netstat -m
时,相关的信息显示在输出的顶部,如下所示:
67 mbufs in use: 64 mbuf cluster pages in use 272 Kbytes allocated to mbufs 0 requests for mbufs denied 0 calls to protocol drain routines 0 sockets not created because sockthresh was reached
以及输出的底部,如下所示:
Streams mblk statistic failures: 0 high priority mblk failures 0 medium priority mblk failures 0 low priority mblk failures
如果您在 netstat -m
输出中看到故障,AIX 5L Performance Tools Handbook 提供了有关要调整哪些参数的清楚描述。您可能还希望尝试netstat -i x
(将 x 替换为收集数据的间隔),以查看网络使用情况和可能丢弃的数据包。对于网络密集型应用程序,这是检查是否“一切都正常”的第一步。
netpmon
netpmon 使用跟踪功能来获得某个时间间隔期间的网络活动详细状况。它还显示进程 CPU 统计信息,其中显示了:
- 该进程使用的 CPU 时间总量
- 进程的 CPU 使用(总时间的百分比)
- 该进程执行与网络相关的代码所花的总时间
要开始优化工作,可以尝试以下命令:
netpmon -o /tmp/netpmon.log; sleep 20; trcstop
此命令行运行 netpmon 命令 20 秒钟,然后使用 trcstop 停止该命令,并将输出写到 /tmp/netpmon.log。查看所生成的数据,可以看到我们选择的示例非常适合于 Java 性能优化文章:
Process CPU Usage Statistics: ----------------------------- Network Process (top 20) PID CPU Time CPU % CPU % ---------------------------------------------------------- java 12192 2.0277 5.061 1.370 UNKNOWN 13758 0.8588 2.144 0.000 gil 1806 0.0699 0.174 0.174 UNKNOWN 18136 0.0635 0.159 0.000 dtgreet 3678 0.0376 0.094 0.000 swapper 0 0.0138 0.034 0.000 trcstop 18460 0.0121 0.030 0.000 sleep 18458 0.0061 0.015 0.000
跟踪的另一个有用部分是适配器使用情况:
----------- Xmit ----------- -------- Recv --------- Device Pkts/s Bytes/s Util QLen Pkts/s Bytes/s Demux ------------------------------------------------------------------------------ token ring 0 288.95 22678 0.0%518.498 552.84 36761 0.0222 ... DEVICE: token ring 0 recv packets: 11074 recv sizes (bytes): avg 66.5 min 52 max 1514 sdev 15.1 recv times (msec): avg 0.008 min 0.005 max 0.029 sdev 0.001 demux times (msec): avg 0.040 min 0.009 max 0.650 sdev 0.028 xmit packets: 5788 xmit sizes (bytes): avg 78.5 min 62 max 1514 sdev 32.0 xmit times (msec): avg 1794.434 min 0.083 max 6443.266 sdev 2013.966
假设您认为信息太多了,或者您希望查看更特定的信息。让我们尝试以下命令:
netpmon -O so -o /tmp/netpmon_so.txt; sleep 20; trcstop
“-O so”使得 netpmon 集中于套接字级别的流量。现在我们可以深入查看 Java 进程信息:
PROCESS: java PID: 12192 reads: 2700 read sizes (bytes): avg 8192.0 min 8192 max 8192 sdev 0.0 read times (msec): avg 184.061 min 12.430 max 2137.371 sdev 259.156 writes: 3000 write sizes (bytes): avg 21.3 min 5 max 56 sdev 17.6 write times (msec): avg 0.081 min 0.054 max 11.426 sdev 0.211
有用吗?让我们更进一步,并查找线程级别的活动。向该命令添加“-t”,如下所示:
netpmon -O so -t -o /tmp/netpmon_so_thread.txt; sleep 20; trcstop
现在,所生成的输出包含特定于线程的信息,如下所示:
THREAD TID: 114559 reads: 9 read sizes (bytes): avg 8192.0 min 8192 max 8192 sdev 0.0 read times (msec): avg 988.850 min 19.082 max 2106.933 sdev 810.518 writes: 10 write sizes (bytes): avg 21.3 min 5 max 56 sdev 17.6 write times (msec): avg 0.389 min 0.059 max 3.321 sdev 0.977
现在可以创建一个 Java 转储,查看该线程是什么,并判断它是否在按预期工作。尤其是对于具有多个网络连接的应用程序,netpmon 允许捕获全面的活动视图。
filemon
filemon 可用于确定正在被活跃地使用的文件。此工具提供非常全面的文件访问视图,并在 vmstat/iostat 确认磁盘成为瓶颈后对于深入分析非常有用。此工具也使用跟踪功能,因此其使用方式与 netpmon 类似:
filemon -o /tmp/filemon.log; sleep 60; trcstop
生成的日志文件相当大。一些可能有用的部分包括:
Most Active Files ------------------------------------------------------------------------ #MBs #opns #rds #wrs file volume:inode ------------------------------------------------------------------------ 25.7 83 6589 0 unix /dev/hd2:147514 16.3 1 4175 0 vxe102 /dev/mailv1:581 16.3 1 0 4173 .vxe102.pop /dev/poboxv:62 15.8 1 1 4044 tst1 /dev/mailt1:904 8.3 2117 2327 0 passwd /dev/hd4:8205 3.2 182 810 1 services /dev/hd4:8652 ... ------------------------------------------------------------------------ Detailed File Stats ------------------------------------------------------------------------ FILE: /var/spool/mail/v/vxe102 volume: /dev/mailv1 (/var/spool2/mail/v) inode: 581 opens: 1 total bytes xfrd: 17100800 reads: 4175 (0 errs) read sizes (bytes): avg 4096.0 min 4096 max 4096 sdev 0.0 read times (msec): avg 0.543 min 0.011 max 78.060 sdev 2.753 ...
此工具在前面引用的参考资料中作了介绍,更详细的研究超出了本文的范围。
特定于 Java 的技巧
用于避免 I/O 和网络瓶颈的 Java 通用技巧归根结底是良好的设计,并且已经在多个地方做了清楚的文档说明。不过还是看一下技巧 NI004 和技巧 NI005。
基于特征的优化技巧
下面让我们看一下典型应用程序的不同特征。您应该定位到与您的应用程序类似的行为(无论是设计上的还是观察到的),并应用对应的技巧。
网络密集型应用程序
对于网络密集型应用程序,您应该使用 netstat 来确保不存在丢弃的数据包等情况。AIX 5L Performance Tools Handbook 中的 netstat 和 netpmon 部分描述了在监视期间观察到故障时可执行的各种调整,因此这里就不再重复了。
如果怀疑网络吞吐量是瓶颈,技巧 NI001 对于确定是否存在问题会非常有用。此外,如果根本不使用 IPv6,也可以使用技巧 NI002。
如果您在考虑 AIX 和其他平台之间的应用程序性能差异,并且怀疑这种差异是由于所设置的套接字选项导致的,您可以查看一下技巧 NI004。
RMI 应用程序
如果应用程序是 RMI 客户机或服务器,您可能观察到 verbosegc 输出中有一些未做出说明的行。例如,下面的内容摘自某个 RMI 应用程序的 verbosegc 输出:
<GC(4057): GC cycle started Thu Apr 15 11:14:28 2004 <GC(4057): freed 254510616 bytes, 55% free (453352000/810154496), in 1189 ms> <GC(4057): mark: 991 ms, sweep: 198 ms, compact: 0 ms> <GC(4057): refs: soft 0 (age >= 32), weak 2, final 330, phantom 0> <GC(4057): stop threads time: 10, start threads time: 260> <GC(4058): GC cycle started Thu Apr 15 11:15:29 2004 <GC(4058): freed 267996504 bytes, 56% free (454445800/810154496), in 1243 ms> <GC(4058): mark: 1041 ms, sweep: 202 ms, compact: 0 ms> <GC(4058): refs: soft 0 (age >= 32), weak 0, final 253, phantom 0> <GC(4059): GC cycle started Thu Apr 15 11:16:31 2004 <GC(4059): freed 248113752 bytes, 56% free (455754152/810154496), in 1386 ms> <GC(4059): mark: 1095 ms, sweep: 291 ms, compact: 0 ms> <GC(4059): refs: soft 0 (age >= 32), weak 0, final 263, phantom 0>
这些 GC 周期几乎准确地相隔 60 秒钟触发一次,并且不是由于“分配故障 (Allocation Failure)”而触发的。在确保应用程序没有直接调用 System.gc() 以后,技巧 NI003 可能适用于这里。
对于 RMI 密集型应用程序,应该考虑技巧 NI005,但是要注意该技巧中提到的注意事项。
磁盘密集型应用程序
使用 iostat 和 filemon,您应该能够找出瓶颈的根源。解决方案通常是调整应用程序设计以停止依赖磁盘访问,或者是调整系统以优化磁盘访问。由于这两类调整都超出了本文的范围,我们建议您熟悉 iostat 和 filemon。前一部分中的信息应该能让您开始上路。
一般技巧集合
下文将把 Java 的命令行参数(在 class/jar 文件名称之前指定)称为“开关”。例如,命令行 java -mx2g hello
具有单个开关 -mx2g
。
技巧 NIO001 检查网络连接的速度
可以在需要分析其连接速度的两个系统之间建立 FTP 会话,并且可以执行以下 FTP 命令:
ftp> put "|dd if=/dev/zero bs=32k count=1000" /dev/null 200 PORT command successful. 150 Opening data connection for /dev/null. 1000+0 records in. 1000+0 records out. 226 Transfer complete. 32768000 bytes sent in 130.4 seconds (245.4 Kbytes/s) local: |dd if=/dev/zero bs=32k count=1000 remote: /dev/null
上面的快速测试尝试传输 1000 个零块 (blocks of zeroes),每个块的大小为 32 KB,并提供了一种确定两台 AIX 计算机之间的连接吞吐量的简单方法。上面的示例显示吞吐量为 245.4 KBps,这指示存在网络问题,因为两台 AIX 计算机都在使用 100 Mbps 的全双工网络适配器。假如上面的测试显示 1.140 E+4 Kbytes/s,这是应该改为集中于应用程序的很好暗示。您可以改变块大小和计数,以更详细地模拟您的应用程序行为。
技巧 NI002 IPv4 堆栈
如果不希望在应用程序中使用 IPv6,可以将属性 preferIPv4Stack 设置为 true,如下所示:
java -Djava.net.preferIPv4Stack=true <classname>
技巧 NIO003 远程 GC
如果您的应用程序是 RMI 客户机或服务器,也可以将 http://java.sun.com/j2se/1.4.2/docs/guide/rmi/sunrmiproperties.html 处定义的 sun.rmi.dgc.client.gcInterval 和/或 sun.rmi.dgc.server.gcInterval 属性用于 IBM Java。这两个属性都缺省设置为 60 秒,并且基于应用程序的需要,可以增加该间隔以减少多余 GC 周期带来的性能影响。
请注意:该链接顶部的警告以及与未释放分布式对象相关联的风险同样适用于 IBM Java。
技巧 NI004 套接字缓冲区大小
如果您在设置发送和接收缓冲区大小,要注意对 setSendBufferSize(int) 的调用仅用作提示。因此,如果观察到平台之间的性能差异,您应该添加一个对 getSendBufferSize() 的调用,并查看当前平台是否拾取了该提示。在最近报告的一个有关 AIX 的性能问题中,应用程序从其代码中调用了 setSendBufferSize(4096)。AIX 使用了该提示并按请求设置了缓冲区大小,而其他平台则忽略了此调用。因此,在 AIX 上察觉到的性能是糟糕的!从代码中删除此调用会使 AIX 上的应用程序性能提高四倍以上。
一般情况下,您可能希望从应用程序中省略用于调整 TCP/IP 堆栈的调用,因为 AIX 网络堆栈是已经预先微调好了的。
技巧 NI005 连接池
对于 RMI 密集型应用程序,启用线程池允许重用现有的连接而不是为每个新的 RMI 调用创建新连接。要启用线程池,可以设置以下属性:
java -Dsun.rmi.transport.tcp.connectionPool=true <classname>
也可以禁用线程池,如下所示:
java -Dsun.rmi.transport.tcp.noConnectionPool=true <classname>
请注意:最好仅对 RMI 密集型应用程序使用线程池。AIX 上的最新版 Java(1.3.1 SR7 以上,以及 1.4.1 SR2 以上)缺省禁用了线程池。
结束语
本文描述了在处理网络和磁盘 I/O 瓶颈时的常用工具和技术。
下一篇文章将以一般性观察和指向有用参考资料的链接结束本系列。
.