《深入理解Java虚拟机 2》性能监控与调优

本系列是用来记录《深入理解Java虚拟机》这本书的读书笔记。方便自己查看,也方便大家查阅。

欲速则不达,欲达则欲速!

第四章 虚拟机性能监控与故障处理工具总结

一、JDK的命令行工具

1、jps:虚拟机进程状况工具

可以列出正在运行的虚拟机进程,并显示虚拟机执行主类(main所在类)名称以及这些进程的本地虚拟机唯一ID(Local Virtual Machine Identifier LVMID)

2、jstat:虚拟机统计信息监视工具

jstat(JVM Statistics Monitoring Tool)是用于监视虚拟机各种运行状态信息的命令行工具,它可以显示本地或者远程虚拟机进程中的类加载、内存、垃圾收集、JIT编译等运行数据,在没有GUI图形界面,只提供纯文本控制台环境的服务器上,它将时运行期定位虚拟机性能问题的首选工具。

3、jinfo:java配置信息工具

jinfo(Configuration Info for java)的作用是实时地查看和调整虚拟机各项参数

4、jmap:java内存映象工具

jmap(Merory Map for java)命令用于生成堆转储快照(一般称为heapdump或dump文件)

5、jhat:虚拟机堆转储快照分析工具

Sun JDK提供jhat(JVM Heap Analysis Tool)命令与jmap搭配使用,分析jmap生成的堆转储快照jhat内置了一个微型的HTTP/HTML服务器,生成dump文件的分析结果后,可以在浏览器中查看。

6、jstack:java堆栈跟踪工具

jstack(Stack Trace for java)命令用于生成虚拟机当前时刻的线程快照(一般称为threaddump或者Javacore文件),线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的常见原因,线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做了些什么事情,或者等待着什么资源。

在JDK1.5中java.lang.Thread类新增了一个getAllStackTreces()方法,用于获取虚拟机中所有线程的StackTraceElement对象,使用这个方法可以通过简单的几行代码就完成jstack的大部分功能,在实际项目中不妨调用这个方法做个管理员页面,可以随时使用浏览器来查看线程堆栈。

<%@ page import="java.util.Map"%>

<html>
<head>
<title>服务器线程信息</title>
</head>
<body>
<pre>
<%
    for (Map.Entry<Thread, StackTraceElement[]> stackTrace : Thread.getAllStackTraces().entrySet()) {
        Thread thread = (Thread) stackTrace.getKey();
        StackTraceElement[] stack = (StackTraceElement[]) stackTrace.getValue();
        if (thread.equals(Thread.currentThread())) {
            continue;
        }
        out.print("\n线程:" + thread.getName() + "\n");
        for (StackTraceElement element : stack) {
            out.print("\t"+element+"\n");
        }
    }
%>
</pre>
</body>
</html>

7、HSIDS:JIT生成代码反汇编

HSIDS是一个Sun官方推荐的HotSpot虚拟机JIT编译代码的反汇编插件

二、JDK的可视化工具

1、JConsole:java监视与管理控制台

JConsole(java monitoring and management console)是一种基于JMX的可视化监视管理工具,它管理部分的功能是针对JMX MBean进行管理,MBean可以使用代码,中间件服务器的管理控制台或者符合JMX规范的软件进行访问。

2、VisualVM:多合一故障处理工具

VisualVM(All-in-One java Troubleshooting Tool)是到目前为止随JDK发布的功能最强大的运行监视和故障处理程序,并且可以预见在未来一段时间内都是官方主力发展的虚拟机故障处理工具,官方在VisualVM的软件说明上写上了“All-in-One”的描述字样,预示着它除了运行监视、故障处理外,还提供了很多其他方面的功能,如性能分析,VisualVM的性能分析功能甚至比起JProfiler YourKit等专业且收费的Profiling工具都不会逊色多少,并且VisualVM还有一个很大的优点:不需要被监视的程序基于特殊Agent运行,因此它对应用程序的实际性能的影响很小,使得它可以直接应用在生产环境中,这个优点是JProfiler YourKit等工具无法与之媲美的。

第五章 调优案例分析与实战

在《深入理解java虚拟机》书中本节的主要内容是讲作者在工作过程中对调优的一些经验实战。对于我们读者来说,重点是学习作者分析解决问题的具体思路。

一、编译时间和类加载时间的优化

编译时间是指虚拟机的JTI编译器编译热点代码的耗时。

虚拟机内置了两个运行时编译器,如果java方法被调用次数达到一定程序,就会被判定为热代码交给JIT编译器,即时编译为本地代码,提高运行速度,这就是Hotspot虚拟机名字的由来。

二、高性能硬件上的程序部署策略

1、基本信息简介

(1)环境:一个15万PV/天左右的在线文档类型网站最近更换了硬件系统,新系统硬件为4个CPU、16G物理内存、OS为64位CentOS5.4、Resin作为Web服务器。

(2)说明:整个服务器暂时没有部署别的应用,所有硬件资源都可以提供给访问量并不算太大的网站使用。管理员为了尽量利用硬件资源选用了64位的JDK1.5,并通过-Xms和-Xms参数将java堆固定在12GB。

(3)问题:使用一段时间后发现使用效果不理想,网站经常不定期出现长时间没有响应的现象。

(4)排查:监控服务器运行状况后发现网站没有响应是由于GC停顿导致的,虚拟机运行在Server模式中,默认使用吞吐量优先收集器,回收12GB的堆,一次Full GC的停顿时间高达15秒。并且由于程序设计的关系,访问文档时要把文档从磁盘提取到内存中,导致内存中出现很多由文档序列化产生的大对象,这些大对象很多都进入了老年代,没有在Minor GC中清理掉。这种情况下即使有12GB的堆,内存也很快被消耗殆尽,由此导致每隔十几分钟出现十几秒的停顿。

(5)分析:先不延伸讨论程序代码的问题,程序部署上主要问题显然是过大的堆内存进行回收时带来的长时间停顿。硬件升级前使用32位系统1.5GB的堆,用户只感受到访问网站比较缓慢,但不会发生十分明显的停顿,因此才考虑升级硬件来提升程序效能,如果重新缩小给java堆的内存,那么硬件上的投资就浪费了。

2、高性能硬件上部署程序的方式

目前主要有两种方式:

(1)通过64位JDK来使用大内存

(2)使用若干个32位虚拟机建立逻辑集群来利用硬件资源

3、通过64位JDK来使用大内存

对于用户交互性强、对停顿时间敏感的系统,可以给java虚拟机分配超过堆的前提是有把握把应用程序的Full GC频率控制得足够低,至少要低到不会影响用户使用,譬如十几个小时乃至一天才出现一次Full GC,这样可以通过在深夜执行定时任务的方式出发Full GC甚至自动重启服务器来将内存可用空间保持在一个稳定的水平。

控制Full GC频率的关键是看应用中绝对多数对象能否符合“朝生夕灭”的原则,即大多数对象的生存时间不应太长,尤其是不能产生批量的、长生存时间的大对象,这样才能保证老年代空间的稳定。

在大多数网站形成的应用里,主要对象的生存周期都应该是请求级或页面级的,会话级和全局级的长生命对象相对减少。只要代码写的合理,应当都能实现在超大堆中正常使用而没有Full GC,这样的话,使用超大堆内存时,网站响应的速度才比较有保证。除此之外,如果读者计划使用64位JDK来管理大内存,还需要考虑下面面临的问题:

(1)内存回收导致的长时间停顿

(2)现阶段,64位JDK的性能测试结果普遍低于32位JDK

(3)需要保证程序足够稳定,因为这种应用要是产生堆溢出几乎无法产生堆转储快照(因为要产生十几GB乃至更大的dump文件),哪怕产生了快照也几乎无法进行解析。

(4)相同的程序在64位JDK中消耗的内存一般比32位JDK大,这是由于指针膨胀及数据型对其补白等因素导致的。

4、使用若干个32位虚拟机建立逻辑集群来利用硬件资源

具体做法是在一台物理机器上启动多个应用服务器进程,给每个服务器进行分配不同的端口,然后再前端搭建一个负载均衡器,以反向代理的方式来分配访问请求。读者不需要太在意均衡器转发所消耗的性能,即使使用64位JDK,许多应用也不止有一台服务器,因此许多应用中前段的均衡器总是要存在的。

考虑到一台物理机上建立逻辑集群的目的仅仅是尽可能的利用硬件资源,并不需要关心状态保留、热转移之类的高可用性需求,也不需要保证每个虚拟机进程有绝对准确的均衡负载,因此使用无session复制的亲和式集群是一个相当不错的选择。我们仅仅需要保证集群具有亲和性,也就是均衡器按一定的规则算法将一个固定的用户请求永远分配到固定的一个集群节点进行处理即可,这样程序开发阶段就基本不用为集群环境做什么特别的考虑。

5、使用逻辑集群的方式部署程序,可能会遇到下面的一些问题

(1)尽量避免节点竞争全局资源,最典型的是磁盘竞争,各个节点如果同时访问某个磁盘文件的话(尤其是并发读写操作容易出现问题),很容易导致IO异常。

(2)很难最高效率的利用某些资源池,如连接池,一般都是在各个节点建立自己独立的连接池,这样有可能导致一些节点池满了而另外一些节点仍有较多空余。尽管可以使用集中式的JNDI,但这有一定的复杂性且可能带来额外的性能代价。

(3)各个节点仍然不可避免的受到32位的内存限制,在32位Windows平台中每个进程只能使用2GB的内存,考虑到堆以外的内存开销,堆一般最多只能开到1.5GB。在某些Linux,Unix系统中,可以提升到3GB乃至接近4GB的内存,但32位中仍然受最高4GB(2^32)内存的限制。

(4)大量使用本地缓存(如大量使用hashmap所谓K/V缓存)的应用,在逻辑集群中造成较大的内存浪费,因为每个逻辑节点上都有一份缓存,这时可以考虑把本地缓存改成集中式缓存。

6、小结

介绍完这两种部署方式,再重新回到这个案例中,最后的部署方案调整为建立5个32位JDK的逻辑集群,每个进程按2GB内存计算,占用了10GB的内存。另外建立一个Apache服务作为前端均衡器代理访问门户。考虑到用户对响应要求较低,因此改为CMS收集器进行垃圾回收。部署完成后,服务再没有出现长时间停顿现象,速度比硬件升级前有较多提升。

三、eclipse运行速度调优

Eclipse运行速度调优

四、总结

调优确实是一个“苦力”活,但对人员素质要求比较高,好像上述所说的经历,就是因为经验不足带导致这么长的调优时间。当然,阳光总在风雨后,熬过去了,确实进步不少,写代码的大局观也提升不少。所以调优这事,是面试的一大好题,最能反映个人能力。

 

鸣谢:特别感谢作者周志明提供的技术支持!

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哪 吒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值