JVM_03调优案例分析

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

1. 案例

一个网站服务器,原先配置为32位,给JVM堆内存大小为1.5GB,使用网站是比较缓慢但无明显卡顿。为了更好的用户体验,升级服务器为64位,16G内存;管理员选用64位jdk,设置JVM堆内存为12GB。但升级后的网站却不定期出现长时间无响应现象。

2. 原因

网站的无响应是由于GC停顿导致的,JVM在server模式,默认使用吞吐量优先的GC收集器,每次回收12GB的堆时,Full GC停顿时间长达12秒,导致卡顿无响应。因为程序设计,访问文档时,会将文档从磁盘提取到内存,这种大对象很多长时间存活进入了老年代,并没有在Minor GC中清理掉。这种情况哪怕有12GB内存也会很快被消耗掉,从而频繁的进行Full GC,使网站无响应。

3. 解决方案

在高性能硬件上部署程序,目前有两种方式:

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

使用这种方案,首先我们要清楚,JDK64位的性能经研究表明是赶不上32位JDK的,其次相同程序在64位JDK中消耗内存更大(由于指针膨胀、数据类型对齐不白等因素)。而且我们要把应用程序的Full GC频率降低,保证十几小时或一天才进行一次,这样我们可以在深夜定时执行一次,从而保证用户体验。

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

通过搭建多个服务器构成集群,共同利用硬件资源,并在前端设置一个负载均衡器,通过反向代理的方式来分配访问请求。

这种方案需要注意:

## 避免节点竞争全局资源,如磁盘;如多节点同时访问磁盘,会造成IO异常

## 对于资源池的利用效率不高;每个节点多为独立建立资源池,会浪费一定资源

## 每个节点可能都会有一份本地缓存,造成内存浪费

4. 解决

采用方案二,搭建5个32位JDK逻辑集群,每个节点分配2GB内存(JVM堆1.5GB),并另外搭建一个Apache服务作为负载均衡代理;并将JVM的GC收集器改为CMS。

二、集群间同步导致的内存溢出

1. 概述

一个基于B/S的MIS系统,硬件是两台2个CPU、8GB内存的HP小型机,服务器是WebLogic9.2,每台机器启动3个WebLogic实例,这样就构成了6个节点的亲和式集群。

由于是亲和式集群,没有进行Session同步,而是将部分共享数据存入了由JBossCache构建的全局缓存,服务启用后,正常服务了较长时间,但最近不定期的出现内存溢出问题。

2. 原因

JBossCache基于自己的JGroups进行集群间的数据通信,JGroups使用协议栈的方式来实现收发数据包的各种所需特性的自由组合。由于信息有传输失败需要重发的可能性,在确认所有在GMS(Group Membership Service)注册的节点都收到信息前,发送的信息都需要在内存中保留;而MIS服务端有个负责安全校验的全局Filter,每次收到请求都需要在各个节点更新操作时间。所以当网络不能满足传输要求时,重发数据在内存中不断堆积,很快产生了内存溢出。

3. 解决

使用JBossCache这种集群缓冲来同步的话,要尽可能减少写操作,减少网络的开销

三、堆外内存导致的溢出错误

基于B/S的电子考试系统,为实现客户端能实时从服务端接收考试数据,系统使用逆向AJAX技术,选用CometD1.1.1作为服务端推送框架,服务器是Jetty7.1.4,硬件为一台普通PC机(Core i5、4G内存、32位系统)。

1. 问题

不定时抛出内存溢出异常,使用jstat命令行工具发现JVM堆内存并未使用完,但依然抛出内存溢出异常

2. 原因

32为Windows系统平台限制为2GB,给了Java堆1.6GB,剩余空间只有0.4GB,而Direct Memory需要使用剩余空间。当进行GC时,虽然会对Direct Memory进行回收,但是当它空间不足时,却不会像新生代或者老年代一样通知JVM进行GC,只会等待老年代满后的Full GC。所以哪怕堆中还有空闲,依然会抛出内存溢出异常。本案中的CometD框架,正好有大量的NIO操作要使用Direct Memory。

3. 总结

除了Java堆和永久代,还有以下区域会占用较多内存,受到总内存限制:

1)Direct Memory 2)线程堆栈 3)Socket缓存区 4)JNI代码 5)虚拟机和GC

四、外部命令导致系统缓慢

1. 概述

一个数字校园应用系统,运行在一天4个CPU的Solaris10系统上,中间件为GlassFish服务器。系统在进行大并发压力测试时,发现请求响应缓慢;而且CPU使用率很高,且大部分并非应用本身占用。

2. 原因

每个用户请求处理都要执行一个外部shell脚本来获得系统的一些信息,执行shell脚本通过Java的Runtime.getRuntime().exec()方法来调用的。这种命令非常耗费资源,需要先克隆一个与当前JVM一样环境变量的进程,再用新进程执行外部命令。

3. 解决

去掉这个shell脚本,改为使用Java API去获取这些信息

五、服务器JVM进程崩溃

系统参数与案例二一致,正常运行一段时间后,发现在运行期间频繁出现集群节点的虚拟机进程自动关闭的现象,并且日志显示远端断开连接异常。

1. 原因

通过了解,系统最近与一个OA门户进行了集成,在MIS系统工作流的待办事项变化时,要通过Web服务器通知OA门户系统,把待办事项的变化同步到OA门户之中。测试发现同步事项的Web服务响应缓慢,且响应结果皆为连接中断。

由于MIS系统用户多,待办事项变化很快,为了不被OA系统拖累,采用了异步的方式调用Web服务,但由于两边服务速度的完全不对等,随着时间增长,等待线程和Socket连接也越来越多,最终超过虚拟机承受能力而崩溃。

2. 解决

通知OA门户修复无法使用的集成接口,并将异步调用改为生产者/消费者模式的消息队列实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值