1。邻居A君唯品会在服务系统改造初期,一个对延迟比较敏感的应用,偶尔会出现一些超时,而zabbix在事发时是分钟级监控,dstat秒级监控服务器指标是正常,应用、数据库、缓存、网络也都正常,这是为什么呢?某日脑洞大开,将怀疑的目光转向后台运行的日志收集程序Flume,发现其GC狂奔,于是限制了GC线程数:修改前:15分钟内,173大于30ms的业务调用,大于50ms的业务调用23次修改后:246分钟内,大于30ms的业务调用41次,50ms以上的调用4次。这次比较容易查,因为我们新升级了服务器的监控系统,在两台机器上做个对比测试就可以了。只用了一晚上的时间,就基本查明了凶手。那么这个新升级的监控系统对主应用有什么影响呢?找出它与应用程序交互的部分。原来对于JVM的各种线程数信息和堆内存每一代的信息,每条数据都会启动一次JMXClient,所以每分钟有1秒获取7条数据,启动7个JMXClient.改进方法很简单。我们自定义了JMXClient,将7条数据合并为一条命令获取。此外,我们还定制了JMXClient的JVM参数,以最大限度地降低其启动时的噪音。3.反优化表明JVM是一个很好的运行服务器端应用程序的VM,但是它的体积有点大。如果你只是想经常运行一个Java写的脚本,或者运行一些监控、日志收集等辅助程序,通常推荐的JVM参数已经不适合了,需要去优化做一个安静的好邻居:Quick开始,小动作。成本低,节省CPU、内存和线程。低干扰,不干扰主应用的运行。4.从失败开始吸取经验,感觉用JDK自带的jmap和jstack一样的参数就可以了,多简单啊。运行的时候,运行jps-v,发现只有一个-Xms8m。还是不死心,又去找源码。JDK7在Makefile.launcher中,JDK8在CompileLaunchers.gmk中。原来都是8M,都是8M,没有其他参数。有同学从长期记忆中回忆起一个-client,这个选项貌似比较弱,但是在这个多核64位Linux服务器上基本无效。必须是-server,必须是-server。5.反优化的思路JVM与上述诉求有几个地方冲突:各种内存消耗各种后台线程JIT时CPU性能狂野,GC时CPU性能狂野。让我们从这几个方面入手。在开始折腾之前,先准备好测试手段:首先在工具脚本中添加GC日志参数,在GC日志中可以看到实际的启动参数,GC记录,最后每一代内存的占用情况的运行。-Xloggc:gc.log-XX:+PrintGCDetails-XX:+PrintGCDateStamps-XX:+PrintGCApplicationStoppedTime其次,运行pidstat-l1|grepxxx长时间密切监视进程的CPU消耗。***,jstack看线程。6、类加载和编译优化6.1-Xverify:none从优化Eclipse启动速度的经验来看,据说关闭Java类加载验证可以加快启动速度10%-15%,嗯,好吧,加。6.2设置编译级别JIT编译后的代码比解释执行字节码更快,节省CPU。比如vjtools中的vjtop,在没有编译的情况下,每次运行需要50%的单核CPU,执行一个检测周期需要75ms。编译后需要10%单核,15ms完成。但是编译本身需要CPU和额外的编译线程。如果脚本只运行一次,比如vjtools中的vjmxcli,建议不要进行JIT编译,编译后也不会使用,直接解释执行即可。禁用:-Djava.compiler=NONE如果脚本是用于密集计算的,比如vjtools中的vjmap,建议开启多层编译,一开始静态编译运行的方法,不等待方法执行被调用10,000次。JDK8默认开启多层编译,显式开启:-XX:+TieredCompilation。但是,开启多层编译也会导致程序前期的编译任务较多,对CPU的消耗较大。可以显式关闭多层编译-XX:-TieredCompilation来比较综合它带来的性能提升。运行时间的长短,以及额外的CPU开销来综合评价。6.3编译线程设置在24核服务器上,默认有4个C1编译线程和8个C2编译线程(多层编译下)。您可以将其设置为最小值-XX:CICompilerCount=2。6.4未来黑科技-AOTJIT确实不适合脚本,还是提前编译代码(Ahead-of-Time,AOT)比较好。在JDK9中,有一个由Hotspot编译组开发的实验性jaotc。另一个选择是GraalVM全家桶中的SubstrateVM,它支持JDK8。很高兴见到大家,但我还没有玩过。7.GC优化脚本通常不介意GC延迟。推荐使用吞吐量最高的串行收集算法-XX:+UseSerialGC,避免了其他GC算法需要的大量GC线程,绝对保证GC不影响主应用。如果你还想使用并行算法,你必须设置GC线程数。24核机器上YGC和CMSGC的默认线程数分别是18个和5个,以避免成为坏邻居。可以设置为:-XX:ParallelGCThreads=4-XX:ConcGCThreads=28。内存优化一、JVM堆内存默认的JVM初始内存大小在内存大的服务器上会比较大,必须设置。当-Xms不等于-Xmx时,自动扩容并没有想象中的智能合理。默认情况下,新生代只有堆大小的1/3,从脚本的角度看,新生代就是大头。建议根据GC日志的结果完全设置-Xms和-Xmx,使用-Xmn(新生代占多数)或-XX:NewRatio=1(一半一半)设置新的生成大小。其次,每个线程的内存由默认的1M改为256k:-xss256k对于其他***代来说,CodeCache的初始值还是比较合理的,如果没有看到特别浪费的情况不要着急。
