Java虚拟机调优实例

高性能硬件部署策略

一台高性能的服务器,指定了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就可以方便的进行虚拟机参数调整了。

  1. 通过让jvm运行于服务器模式。
  2. 升级JDK版本
  3. 设置好永久代的大小(这里已经废了)
  4. 取消字节码验证,可选
  5. 调整堆大小
  6. 屏蔽掉系统的gc
  7. 更换对应的垃圾收集器

最后对应的参数调整为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-Xms4g
-Xmx4g
-Xmn1g
-XX:PermSize=768m
-XX:MaxPermSize=768m
-XX:ReservedCodeCacheSize=1024m
-XX:+CMSParallelRemarkEnabled
-XX:+CMSClassUnloadingEnabled
-XX:+UseCompressedOops
-Dfile.encoding=UTF-8
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50
-ea
-Djdk.http.auth.tunneling.disabledSchemes=""
-XX:+HeapDumpOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow
-XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
-XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
-Dide.no.platform.update=true
-Dsun.io.useCanonPrefixCache=false
-Djdk.attach.allowAttachSelf=true
-Dkotlinx.coroutines.debug=off
-Djdk.module.illegalAccess.silent=true
-XX:CICompilerCount=2