JAVA虚拟机(六)调优案例分析与实战

一个在线文档网站采用了新的硬件,4个CPU,16GB物理内存。管理员为了尽量利用硬件资源选用了64位的JDK1.5,并且将堆的大小固定位12GB。但是网站不定期出现失去响应的情况。
监控服务器发现是由于GC停顿导致的,回收12GB的堆,一次Full GC停顿高达14秒。而且因为内存中有很多文档序列化产生的大对象,他们直接进入到了老年代,没有在Minor GC中清理掉。
问题点:过大的堆导致内存回收时带来了长时间的停顿。升级前使用32位系统1.5GB的堆,用户只感觉使用网站比较缓慢。
在高性能硬件上部署程序,目前主要有两种方式:
1.使用64为JDK来使用大内存。
2.使用若干个32位虚拟机建立逻辑集群来利用硬件资源。
如果使用第一种,对于交互性强的应用,给Java虚拟机分配超大堆的前提是有把握把程序Full GC的频率控制的足够低。并且需要面对几个问题:内存回收导致长时间停顿,目前64位JDK的测试性能普遍低于32位的JDK。
现阶段很多人还是选择第二种方法,同一个物理机器上启动多个应用服务器进程,然后在前端搭建一个负载均衡器,以反向代理的方式分配访问请求。可能会遇到的问题,尽量避免节点竞争全局的资源,典型的是磁盘竞争。很难高效率的利用某些连接池。各个节点仍然受到32位的内存限制,32位windows平台中每个进程只能使用2GB的内存。出去堆以外的开销,最多只能开到1.5GB。
最后的解决方案:
建立5个32为JDK逻辑集群,每个进程按2GB内存计算,总共占用10GB,并且文档服务的主要压力集中在磁盘和内存访问,CPU资源敏感度低,改用CMS收集器。

一个学校的小型项目,基于B/S的电子考试系统,客户端实时的从服务端接收考试数据。堆内存给到了1.6GB(基本已经是windows系统最大的内存了)。最后仍然报出
java.lang.OutOfMemoryError
at org.eclipse.jetty.io.nio.DirectNIOBuffer
许多人已经看出了问题所在,直接内存溢出,这块直接内存并不算在1.6GB之内,因此它的最大也只在0.4GB的一部分。

在进行垃圾回收时,虚拟机虽然会对Direct Memory进行回收,但是Direct Memory却不能像新生代,老年代那样,发现不足了就进行垃圾回收,他只能等待老年代满了之后Full GC,然后顺便帮他清理掉内存的废弃对象。NIO操作就需要使用Direct Memory内存。

一个数字校园应用系统,运行在一个4个CPU的Solaris 10操作系统上。系统在做大并发压力测试的时候,发现请求响应时间比较慢。CPU占用率较高,但不是应用占用。

最后查看占用CPU资源最多的是fork系统调用,“fork”系统调用时Linux用来产生新进程的,在Java虚拟机中,用户编写的Java代码最多只有线程概念,不应当有进程的产生。

原因是他们使用Java的 Runtime.getRuntime().exec()方法来调用外部shell脚本,这个过程会频繁的创建进行,资源消耗很大。
解决方案,去掉这个shell脚本,使用Java API来获取信息。

一个基于B/S的MIS系统,硬件为2个CPU,8GB内存的系统,运行一段时间后,频繁出现集群节点虚拟机自动关闭现象,留下一个hs_err_pid###.log后,进程就消失了。

从虚拟机进程崩溃前不久,都发生过大量相同的异常。
java.net.SocketException:Connection reset
这时一个远程连接异常,使用了异步的方式调用Web服务,但是两边的速度不对等,时间越长就积累了越多的Web服务没有调用完成,导致等待的线程和Socket连接越来越多,最终超过虚拟机的承受能力使得虚拟机进程崩溃。
解决思路,将异步调用改为生产者/消费者模式的消息队列实现。

一个后台RPC服务器,采用64位虚拟机,使用ParNew + CMS收集器组合,平时对外服务Minor GC的时间在30毫秒以内,完全可以接收,但在业务上,每10分钟加载一个80MB的数据文件到内存中,会形成超过100万个HashMap,这时Minor GC就会造成500毫秒的停顿。

此处创建了太多HashMap对象,存储效率也比较低。

猜你喜欢

转载自blog.csdn.net/bianhao92115/article/details/83991119