JVM虚拟机读书日记4

调优案例分析

大内存硬件上的程序部署策略

一个15万PV/日左右的在线文档类 型网站最近更换了硬件系统,服务器的硬件为四路志强处理器、16GB物理内存,操作系统为64位 CentOS 5.4,Resin作为Web服务器。整个服务器暂时没有部署别的应用,所有硬件资源都可以提供给 这访问量并不算太大的文档网站使用。软件版本选用的是64位的JDK 5,管理员启用了一个虚拟机实 例,使用-Xmx和-Xms参数将Java堆大小固定在12GB。使用一段时间后发现服务器的运行效果十分不 理想,网站经常不定期出现长时间失去响应。 监控服务器运行状况后发现网站失去响应是由垃圾收集停顿所导致的,在该系统软硬件条件下, HotSpot虚拟机是以服务端模式运行,默认使用的是吞吐量优先收集器,回收12GB的Java堆,一次Full GC的停顿时间就高达14秒。由于程序设计的原因,访问文档时会把文档从磁盘提取到内存中,导致内 存中出现很多由文档序列化产生的大对象,这些大对象大多在分配时就直接进入了老年代,没有在 Minor GC中被清理掉。这种情况下即使有12GB的堆,内存也很快会被消耗殆尽,由此导致每隔几分 钟出现十几秒的停顿,令网站开发、管理员都对使用Java技术开发网站感到很失望。 分析此案例的情况,程序代码问题这里不延伸讨论,程序部署上的主要问题显然是过大的堆内存 进行回收时带来的长时间的停顿。经调查,更早之前的硬件使用的是32位操作系统,给HotSpot虚拟机 只分配了1.5GB的堆内存,当时用户确实感觉到使用网站比较缓慢,但还不至于发生长达十几秒的明 显停顿,后来将硬件升级到64位系统、16GB内存希望能提升程序效能,却反而出现了停顿问题,尝试 过将Java堆分配的内存重新缩小到1.5GB或者2GB,这样的确可以避免长时间停顿,但是在硬件上的投 资就显得非常浪费

每一款Java虚拟机中的每一款垃圾收集器都有自己的应用目标与最适合的应用场景,如果在特定 场景中选择了不恰当的配置和部署方式,自然会事倍功半。目前单体应用在较大内存的硬件上主要的 部署方式有两种:

  1. 通过一个单独的Java虚拟机实例来管理大量的Java堆内存。
  2. 同时使用若干个Java虚拟机,建立逻辑集群来利用硬件资源。

对于用户交互性强、对停顿时间敏感、内存又较大的 系统,并不是一定要使用Shenandoah、ZGC这些明确以控制延迟为目标的垃圾收集器才能解决问题 ,使用Parallel Scavenge/Old收集器,并 且给Java虚拟机分配较大的堆内存也是有很多运行得很成功的案例的,但前提是必须把应用的Full GC 频率控制得足够低,至少要低到不会在用户使用过程中发生,譬如十几个小时乃至一整天都不出现一 次Full GC,这样可以通过在深夜执行定时任务的方式触发Full GC甚至是自动重启应用服务器来保持内 存可用空间在一个稳定的水平。 控制Full GC频率的关键是老年代的相对稳定,这主要取决于应用中绝大多数对象能否符合“朝生 夕灭”的原则,即大多数对象的生存时间不应当太长,尤其是不能有成批量的、长生存时间的大对象产 生,这样才能保障老年代空间的稳定。

单独的虚拟机实例来管理大量的java内存面临的问题

  1. 回收大块堆内存而导致的长时间停顿,自从G1收集器的出现,增量回收得到比较好的应用, 这个问题有所缓解,但要到ZGC和Shenandoah收集器成熟之后才得到相对彻底地解决。
  2. 大内存必须有64位Java虚拟机的支持,但由于压缩指针、处理器缓存行容量(Cache Line)等因 素,64位虚拟机的性能测试结果普遍略低于相同版本的32位虚拟机。
  3. 必须保证应用程序足够稳定,因为这种大型单体应用要是发生了堆内存溢出,几乎无法产生堆转储快照(要产生十几GB乃至更大的快照文件),哪怕成功生成了快照也难以进行分析;如果确实出了问题要进行诊断,可能就必须应用JMC这种能够在生产环境中进行的运维工具。
  4. 相同的程序在64位虚拟机中消耗的内存一般比32位虚拟机要大,这是由于指针膨胀,以及数据类 型对齐补白等因素导致的,可以开启(默认即开启)压缩指针功能来缓解。

同时使用若干个Java虚拟机,建立逻辑集群来利用硬件资源面临的问题

  1. 节点竞争全局的资源,最典型的就是磁盘竞争,各个节点如果同时访问某个磁盘文件的话(尤其 是并发写操作容易出现问题),很容易导致I/O异常。
  2. 很难最高效率地利用某些资源池,譬如连接池,一般都是在各个节点建立自己独立的连接池,这 样有可能导致一些节点的连接池已经满了,而另外一些节点仍有较多空余。尽管可以使用集中式的 JNDI来解决,但这个方案有一定复杂性并且可能带来额外的性能代价。
  3. 如果使用32位Java虚拟机作为集群节点的话,各个节点仍然不可避免地受到32位的内存限制,在 32位Windows平台中每个进程只能使用2GB的内存,考虑到堆以外的内存开销,堆最多一般只能开到 1.5GB。在某些Linux或UNIX系统(如Solaris)中,可以提升到3GB乃至接近4GB的内存,但32位中仍 然受最高4GB(2的32次幂)内存的限制。
  4. 大量使用本地缓存(如大量使用HashMap作为K/V缓存)的应用,在逻辑集群中会造成较大的内 存浪费,因为每个逻辑节点上都有一份缓存,这时候可以考虑把本地缓存改为集中式缓存。

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

一个基于B/S的MIS系统,硬件为两台双路处理器、8GB内存的HP小型机,应用中间件是WebLogic 9.2,每台机器启动了3个WebLogic实例,构成一个6个节点的亲合式集群。由于是亲合式集群,节点之 间没有进行Session同步,但是有一些需求要实现部分数据在各个节点间共享。最开始这些数据是存放 在数据库中的,但由于读写频繁、竞争很激烈,性能影响较大,后面使用JBossCache构建了一个全局 缓存。全局缓存启用后,服务正常使用了一段较长的时间。但在最近不定期出现多次的内存溢出问 题。在内存溢出异常不出现的时候,服务内存回收状况一直正常,每次内存回收后都能恢复到一个稳 定的可用空间。开始怀疑是程序某些不常用的代码路径中存在内存泄漏,但管理员反映最近程序并未 更新、升级过,也没有进行什么特别操作。只好让服务带着-XX:+HeapDumpOnOutOfMemoryError 参数运行了一段时间。在最近一次溢出之后,管理员发回了堆转储快照,发现里面存在着大量的 org.jgroups.protocols.pbcast.NAKACK对象。 JBossCache是基于自家的JGroups进行集群间的数据通信,JGroups使用协议栈的方式来实现收发 数据包的各种所需特性自由组合,数据包接收和发送时要经过每层协议栈的up()和down()方法,其中 的NAKACK栈用于保障各个包的有效顺序以及重发。

由于信息有传输失败需要重发的可能性,在确认所有注册在GMS(Group Membership Service)的 节点都收到正确的信息前,发送的信息必须在内存中保留。而此MIS的服务端中有一个负责安全校验 的全局过滤器,每当接收到请求时,均会更新一次最后操作时间,并且将这个时间同步到所有的节点 中去,使得一个用户在一段时间内不能在多台机器上重复登录。在服务使用过程中,往往一个页面会 产生数次乃至数十次的请求,因此这个过滤器导致集群各个节点之间网络交互非常频繁。当网络情况
不能满足传输要求时,重发数据在内存中不断堆积,很快就产生了内存溢出。 这个案例中的问题,既有JBossCache的缺陷,也有MIS系统实现方式上的缺陷。JBoss-Cache官方 的邮件讨论组中讨论过很多次类似的内存溢出异常问题,据说后续版本也有了改进。而更重要的缺陷 是,这一类被集群共享的数据要使用类似JBossCache这种非集中式的集群缓存来同步的话,可以允许 读操作频繁,因为数据在本地内存有一份副本,读取的动作不会耗费多少资源,但不应当有过于频繁 的写操作,会带来很大的网络同步的开销。

堆外内存导致的溢出错误

这是一个学校的小型项目:基于B/S的电子考试系统,为了实现客户端能实时地从服务器端接收考 试数据,系统使用了逆向AJAX技术(也称为Comet或者Server Side Push),选用CometD 1.1.1作为服 务端推送框架,服务器是Jetty 7.1.4,硬件为一台很普通PC机,Core i5 CPU,4GB内存,运行32位 Windows操作系统。 测试期间发现服务端不定时抛出内存溢出异常,服务不一定每次都出现异常,但假如正式考试时 崩溃一次,那估计整场电子考试都会乱套。网站管理员尝试过把堆内存调到最大,32位系统最多到 1.6GB基本无法再加大了,而且开大了基本没效果,抛出内存溢出异常好像还更加频繁。加入-XX: +HeapDumpOnOutOfMemoryError参数,居然也没有任何反应,抛出内存溢出异常时什么文件都没有 产生。无奈之下只好挂着jstat紧盯屏幕,发现垃圾收集并不频繁,Eden区、Survivor区、老年代以及方 法区的内存全部都很稳定,压力并不大,但就是照样不停抛出内存溢出异常。

问题原因在于堆外内存,没有垃圾回收机制但是内存不足时就只能抛出异常。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值