高性能硬件部署策略
一台高性能的服务器,指定了java堆的大小为12G,用来作为网站。但是发现效果不理想,因为代码编写的较差,所以很多大文档序列化后的对象一直存在于堆中,而文件的序列化后的对象肯定特别大,所以直接放到老年代里面,导致full GC频繁出现,而一次full GC`需要消耗大概10s的时间,这对一个网站来说是不太适合的。
由于这里讲的是虚拟机的问题,所以暂且先把程序写的差这个问题扔到一边,想想看怎么从虚拟机入手解决这个问题。首先我们需要知道,full GC是必不可少的,但是网站来说大内存肯定也是必要的,也就是一次full GC肯定要花不少时间。那么怎么办呢?降低GC的频率,这样我可以每天在深夜的时候GC或者说干脆一周一次,这样效率就会好的多。但是之前也说了,这是因为代码编写的问题,所以这个方法其实对于这个案例不太好。
最后采用的方法是:部署多个32位的JDK逻辑集群,每个进程是2G内存,然后分配给堆1.5G,并且搭建一个Apache服务来作为前端的均衡器。这样就不会导致发生GC的时候等待时间过长。
简单理解集群:你把你的服务器完完全全复制多份,这样它们就构成了一个集群。
集群同步导致内存溢出
一开始共享数据使用的是数据库,但是读写竞争很激烈,性能不佳。之后用了JBossCache做了一个全局缓存(JBoss Cache是针对Java应用的企业级集群解决方案,其目的是通过缓存需要频繁访问的Java对象,提高应用的可用性并大幅度提升应用的整体性能),但是出现了内存溢出的问题。所以让服务带着记录日志的选项运行了一段时间,然后发生了溢出,分析了heapdump文件,发现其中存在了大量的org.jgroups.protocols.pbcast.NAKACK的对象,这主要是因为信息可能会发送失败,所以需要在所有节点都收到正确的信息前,把发送的信息放在内存里面,这就导致出现了这个问题。
这个问题是因为网络连接不通畅,导致数据需要重发,重发的数据在内存中不断堆积,就产生了内存溢出。
使用外部命令导致CPU占用高
java可以执行shell的命令,通过Runtime.getRuntime().exec()即可,但是JVM是通过克隆一个和自己一模一样的进程,然后让这个进程去执行外部的命令,最后再推出这个进程。显然如果频繁执行外部的命令,那样系统的压力会很大,所以少用这个命令,而是使用java的API去实现。
其它的一些案例
有一些事因为外部的接口出错导致的问题,只需要修复外部接口响应过慢即可。
有一个是因为数据结构不对,Map<Long, Long>耗费了太多资源。
有一个是java swing的问题,现在早就没人用java写桌面程序了,跳过。
还有一个是因为循环的时候,到安全点时间过长导致的停顿。这个很有意思,默认是用int作为索引的循环不会建立安全点,而使用long作为索引的循环会建立安全点。
IDEA调优实例
书中是eclipse,我用的是IDEA,所以我仿照书中的方法对我的idea进行了优化。
我的设备是mac book pro 2018乞丐版,内存16G。
idea打开Help - edit custom VM options就可以方便的进行虚拟机参数调整了。
- 通过让jvm运行于服务器模式。
- 升级JDK版本
- 设置好永久代的大小(这里已经废了)
- 取消字节码验证,可选
- 调整堆大小
- 屏蔽掉系统的gc
- 更换对应的垃圾收集器
最后对应的参数调整为:
1 | -Xms4g |