5、JVM--调优案例分析

5.1、案例分析

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

假如一个15w/天左右的在线文档类型网站再准备更换硬件系统

新的硬件为4个CPU、16GB物理内存,操作系统为64为Cento是

Resin作为Web服务器

整个服务器暂时没有部署别的应用,所有的硬件资源都可以提供给这个访问量不大的网站使用

管理员未来尽量利用硬件资源选用64位JDK1.5

并且通过-Xmx和-Xms参数将java堆固定再12GB

使用一段时间后发现效果不理想,网站不定期出现长时间失去响应的情况

监控服务器运行状况发现失去响应是由GC停顿导致的

虚拟机运行再Server模式,默认使用吞吐量有限收集器回收12GB的堆

一次Gull GC的停顿高达14秒

由于程序设计关系,访问这些文档时需要把文档从磁盘提取到内存,导致内存中出现很多有文档序列化产生的大对象

这些大对象很多已经进入了老年代

没有再Minor GC中清理掉

这种情况下即使有12GB的堆,内存也很快被消耗殆尽

由此导致每隔十几分钟出现十几秒停顿。

程序部署上的主要问题是过大的堆内存进行回收时带来的长时间停顿

硬件升级前使用32位系统1。5GB的堆

用户只感觉到网站比较缓慢,但是不会发生明显的停顿

为了考虑升级硬件以提升程序效能,如果缩小给java堆分配内存,那么再硬件上的投资就是显得浪费

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

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

2、使用若干个32位虚拟机简历逻辑集群来利用硬件资源

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

使用第一种部署方式:对用户交互性强、对停顿时间敏感的系统

可以给Java虚拟机分配超大堆的前提下是有把握应用程序的Full GC频率控制的足够低

至少要低到不会影响用户的使用

如十几小时乃至一天出现一次Full  GC,这样可以通过再深夜执行定时任务的方式触发FUll GC

甚至自动重启应用服务器来保持内存可用空间再一个稳定水平

控制Full GC频率的关键看应用中绝大多数对象能否符合“朝生夕灭”的原则

大多数对象的生存空间不长,尤其是不能有成批量、长生存时间的大对象产生,这样才能保障老年代空间的稳定

再大多数网站形式的应用里,主要对象的生存周期都应该是请求级或者页面级

会话级和全局级的长生命对象很少,只要代码写得合适,应当都能实现再超大堆

中正常使用而没有Full GC,

使用超大内存堆时,网站响应速度才会比较有保证

初次之外还需要考虑:

1、内存回收导致的长时间卡顿

2、64位的JDK的性能测试结果普遍低于32位JDK

为了保证程序足够稳定,这种应用要是产生堆溢出几乎就无法产生堆存储快照(会产生大量的Dump文件)

哪怕产生了快照也无法进行分析

相同程序在64位JDK消耗的内存一般比32位JDK大

这是由于指针膨胀,以及数据类型对齐补白等因素导致

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

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

具体做法是再一台物理机上启动多个应用服务器进程,每个服务器进程分配不同的端口

然后再前端搭建一个负载均衡,以反向代理方式来分配访问请求

考虑再一台服务器集器上建立逻辑集群的目的仅仅是为了尽可能利用硬件资源

并不把要关心状态保留、热转移指类的高可用性需求,也不需要保证每个虚拟机进程有

绝对的负载均衡,因此使用无Session复制的亲合式方式集群是一个相当不错的选择

仅仅需要保证集群具备亲和性,也是均衡器按一定的规则算法将一个固定的用户请求

永远分配到固定的一个集群节点进行处理即可。

可能会遇到的问题:

1、尽量避免节点竞争全局资源,最典型的就是磁盘竞争,各个节点之间如果同时访问某个磁盘

   问价的话,很容易导致IO异常

2、很难最高效率地利用某些资源池(连接池),一般都是再各个节点建立独立的连接池,这样有可能

   导致一些节点满了而另外一些节点仍有较多的空余

3、各个节点仍然不可避免的受到32位的内存限制,再32位windows平台中每个进程都只能使用2GB的内存

   考虑到堆以外的内存开销,堆一般最多只能开到1.5GB。

4、大量使用本地缓存的应用,再逻辑集群中会造成较大的内存浪费,因为每一个节点上都有一份缓存

   这时候可以考虑把本地缓存改为集中式缓存

5.2.2、集群间同部导致的内存溢出

如:一个基于B/S的MIS系统,硬件为两台2个CPU、8GB内存的HP小型机

服务器是WebLogic9.2,每个机器启动了3个WebLogic实例,构成一个6个节点的亲合式集群

由于式亲合式集群,节点之间没有进行session同部,但是要实现部分数据再各个节点间共享

开始将这些数据存放在数据库中,由于读写频繁竞争很激烈,性能影响比较大

后面使用JBossCache构建一个全局缓存

全局缓存启用后,服务正常使用了一段时间,但最近却不定期的出现了很多次内存溢出的问题

在内存溢异常不出现的时候,服务内存回收状况一直正常,每次内存回收后都能恢复

到一个稳定的可用的空间,开始换衣是程序某些不正常的代码路径中存在内存泄露

但管理员反映最近程序并未更新、升级过、也没有进行什么特别操作

只好让服务带着:-XX:+HeapDumpOnOutOfMemoryError参数运行一段时间

再最近一次溢出之后,管理员发回了heapdump文件,发现里面存在大量的org.jgroups.protocols.pbcast.NAKACK对象

JBossCache是基于自家的JGroups进行集群间的数据通信,JGroups使用协议栈的方式来实现收发数据

的各种所需要特性自由组合数据包接收和发送时都要经过每层协议栈的up()和down()方法

其中的NAKACK栈用于保障各个包的有效顺序及重发

JBossCache协议栈:

https://blog.csdn.net/yangyan19870319/article/details/7631145

https://my.oschina.net/u/2338362/blog/469594

由于信息有传输失败需要重发的可能性,再确定所有注册再GMS(Group Membership Service)的节点

都受到了正确的信息前,发送的信息必须再内存中保留

此MIS服务端中有一个负责安全检验的全局Filter,每当接收到请求时,均会更新一次最后操作时间

并且将这个时间同步到所有节点去,使得一个用户再一段时间不能再多台机器上登陆

再服务使用过程中,往往一个页面会产生数次乃至数十次的请求

因此这个过滤器导致集群各个节点之间网络交互频繁

当网络情况不呢满足用户传输需求时,重发数据再内存中不断堆积,很快就产生了内存溢出

这个案例中的问题,既有JBossCache的缺陷,也有MIS系统实现方式上缺陷。
JBossCache官方的maillist中讨论过很多次类似的内存溢出异常问题,据说后续版本也有了改
进。而更重要的缺陷是这一类被集群共享的数据要使用类似JBossCache这种集群缓存来同步
的话,可以允许读操作频繁,因为数据在本地内存有一份副本,读取的动作不会耗费多少资
源,但不应当有过于频繁的写操作,那样会带来很大的网络同步的开销

猜你喜欢

转载自www.cnblogs.com/Mrchengs/p/10759776.html