Chapter VI JVM tuning and performance optimization insight notes

VM tuning essence: not significantly improve system performance, not that you tune the performance will be able to upgrade several times or ten times, JVM tuning, the main tune is stable. If your system has a frequent garbage collection, this time the system is unstable, so we need to JVM tuning, adjusting the frequency of garbage collection.

A, the GC tuning principles

Tuning principles :

  • Most applications do not require java GC tuning
  • Most need to GC tuning of parameters is not a problem, the problem is the code
  • In actual use, GC analysis of much more than the case of optimized code GC parameter optimization;
  • GC tuning is a last resort

Objective :

GC time is small enough, the small enough number GC.

Full GC cycle occurs sufficiently long, a reasonable time, preferably not occur.

Note :

If the following indicators are met, the general does not require GC:

  • Minor GC execution time of less than 50ms;
  • Minor GC performed infrequently, for about 10 seconds;
  • Full GC execution time of less than 1s;
  • Full GC frequency is not performed frequently, not less than 10 minutes 1 second;

1, tuning step

1.1, log analysis

1) monitoring the state of GC

JVM using a variety of tools, view the current log, analyze the current JVM parameters, and analyze the current heap memory snapshots and gc log, based on the actual execution time of each memory division and regional GC, whether that be optimized.

 2) analysis, determine the need to optimize

If you set reasonable parameters, the system does not log a timeout occurs, the frequency is not high GC, GC time-consuming is not high, so there is no need for GC optimization; if more than 1-3 seconds GC or GC frequently, it must be optimized;

3) adjusting the type and memory allocation GC

If the memory allocation too large or too small, or the use of slow collector GC, these parameters should be adjusted with priority, and go first to a machine or several beta, and then comparing the machine and optimized performance is not optimized machine contrast, and targeted to make a final choice;

4) continuous analysis and adjustment

Through continuous testing and trial and error, and analysis to find the most suitable parameters

5) full application parameters

If you find the most suitable parameters, these parameters will be applied to all servers, and follow up.

1.2, read GC logs

The main focus of recovery efficiency MinorGC and FullGC (recovery and recycling before the size comparison), the recovery time.

-XX: + UseSerialGC way:

A parameter -Xms5m -Xmx5m -XX: + PrintGCDetails -XX: + UseSerialGC example:

[DefNew: 1855K->1855K(1856K), 0.0000148 secs][Tenured: 2815K->4095K(4096K), 0.0134819 secs] 4671K

DefNew indicates the collector type, and illustrates the new generation occurs in the collection; 1855K-> 1855K ​​(1856K) represents a new generation occupied prior to recovery 1855K, recovered occupied 1855K, the size of the new generation of 1856K; 0.0000148 secs consuming recovery showed new generation; Tenured show collection occurred in the old years, 2815K-> 4095K (4096K), 0.0134819 secs: meaning with the new generation, the final size of 4671K specified heap.

-XX: + UseParNewGC way:

Collector parameter becomes -XX: + UseParNewGC, log becomes:

[ParNew: 1856K->1856K(1856K), 0.0000107 secs][Tenured: 2890K->4095K(4096K), 0.0121148 secs]

Collector parameter becomes -XX: + UseParallelGC or UseParallelOldGC, log becomes:

[PSYoungGen: 1024K->1022K(1536K)] [ParOldGen: 3783K->3782K(4096K)] 4807K->4804K(5632K),

-XX: + UseConcMarkSweepGC way:

CMS collector and the collector G1 will be significantly related words

-XX: + UseG1GC way:

2, the GC tuning combat

2.1, the project started GC optimization

Open log analysis -XX: + PrintGCDetails found to have multiple GC include FullGC

The first step: Adjust Metadata space -XX: MetaspaceSize = 64m:

PSYoungGen appeared four times, FullGC disappear.

The second step: to reduce the number of Minor gc, increase the parameter -Xms500m

GC reduced to 2 times , FullGC disappear.

The third step: to reduce the number of Minor gc, tuning parameters -Xms1000m

GC reduced to 2 times , FullGC disappear (indicating an increase heap space has no effect).

The fourth step: increasing the proportion of the new generation, increase the parameter -Xmn900m

GC reduced to 1 .

Step five: to increase the new generation, adjust parameters -Xms2000m -Xmn1800m  

GC reduced to 0 times .

NOTE: If you continue to increase the memory space, still can not avoid GC, no need to adjust so big .

2.2, the project runs GC optimization

Use jmeter simultaneous access to three interfaces, index, time, noblemetal

Use 40 threads, 250 times cycle pressure test, observe the concurrent

Note: JMeter performance testing can refer to the following documents

https://blog.csdn.net/u012111923/article/details/80705141

2.3, thread group Detailed parameters:

1. The number of threads: number of virtual users. A virtual user occupies one process or thread. How many virtual users set up where the number of threads is set.

2. Ramp-Up Period (in seconds) to prepare a long time: the number of virtual users to set how long it takes all started. If the number of threads is 10, when preparing length of 2, you need 2 seconds to start 10 threads, that is, five threads per start.

3 cycles: each thread transmission request times. If the number of threads 10, 100 cycles, each thread 100 requests the transmission. The total number of requests is 10 * 100 = 1000. If you check the "Always", then all threads will always send a request to opt out of running a script.

4. Delay Thread creation until needed: when you need to create a thread until the delay.

5. Scheduler: start time thread group start and end time (the scheduler configuration, the number of cycles required to always check)

Duration (seconds): test duration, will cover the end of time

Start delay (seconds): Test delayed start time, it will cover the start-up time

Start Time: Test start time, start delay will cover it. When the start time has passed, just manually test the current time will be covered by it.

End time: end of the test period, the duration will cover it.

Because the interface debugging needs, we are temporarily using the default settings, and then come back to the real configuration of performance tests to be back.

2.4 , the polymerization report parameters Detailed:

  • Label: JMeter of each element (e.g. HTTP Request) has a Name attribute, shown here is the value of the Name attribute.
  • #Samples: the number of requests - indicates this test have issued a total number of requests, if the analog 10 users, each 10 iterations, this display 100.
  • Average: Average response time - the default is the average response time of a single Request, when using the Controller Transaction, in Transaction Displays the average response time.
  • Median: the median response time is 50% of users.
  •  90% Line: 90% response time for users.
  • Min: minimum response time.
  • Max: maximum response time.
  • Error%: error rate - error requests / total requests.
  • Throughput: Throughput - the number of requests represented default completed per second (Request per Second), when using a Transaction Controller, can also represent a similar number of LoadRunner Transaction per Second.
  • KB / Sec: second server receives the amount of data, equivalent to the LoadRunner Throughput / Sec.

In general, data performance tests, we need to focus on are: #Samples requests, Average average response time, Min minimum response time, Max maximum response time, Error% error rate and Throughput throughput.

Method 1: Use a single-threaded GC

-XX:+UseSerialGC

Analysis: performed a total of 30,000 times the request, it takes 7 seconds, the throughput of 4080.52 / sec

Second way: using multithreaded GC :

-XX:+UseParNewGC

Analysis: performed a total of 30,000 times the request, it takes 5 seconds, the throughput of 4287.55 / sec

Three ways: using the CMS :

-XX:+UseConcMarkSweepGC

分析:总共执行30000次请求,耗时7秒,吞吐量为4205.8/秒

方式四:使用G1

-XX:+UseG1GC

分析:总共执行30000次请求,耗时7秒,吞吐量为4291.23/秒

方式五:代码问题影响GC结果

一行代码导致频繁GC,吞吐量下降很快(同样适用:-XX:+UseG1GC

@Service
public class FillMemory extends Thread{
   private static Logger logger = LoggerFactory.getLogger(FillMemory.class);
   private List<byte[]> listByte = new LinkedList<>();
   @Override
   public void run() {
      while(!Thread.currentThread().isInterrupted()) {
         listByte.clear();
         logger.info("开始填充..........");
         for(int i=0;i<500;i++) {
            byte[] bytes = new byte[1*1024*1024];
            listByte.add(bytes);
         }
         logger.info("..........填充完毕");
         try {
            Thread.sleep(100);
         } catch (InterruptedException e) {
         }        
      }
   }
 @PostConstruct
   public void init() {
      this.start();
   }
}

执行结果:

分析:总共执行30000次请求,耗时8秒,吞吐量为3455.4/秒。

3、推荐策略

3.1、新生代大小选择

  • 响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择),在此种情况下,新生代收集发生的频率也是最小的,同时减少到达老年代的对象。
  • 吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度,因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。
  • 避免设置过小:当新生代设置过小时会导致:1)YGC次数更加频繁 2)可能导致YGC对象直接进入旧生代,如果此时旧生代满了,会触发FullGC。

3.2老年代大小选择

响应时间优先的应用:老年代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片,高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:并发垃圾收集信息、持久代并发收集次数、传统GC信息、花在新生代和老年代回收上的时间比例。

吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的新生代和一个较小的老年代。原因是这样可以尽可能回收掉大部分短期对象,减少中期的对象,而老年代尽存放长期存活对象。

调优是个很复杂、很细致的过程,要根据实际情况调整,不同的机器、不同的应用、不同的性能要求调优的手段都是不同的,即使是jvm参数也是如此,比如说性能有关的操作系统工具,和操作系统本身相关的所谓大页机制,都需要平时去积累,去观察,去实践。

4逃逸分析

是JVM所做的最激进的优化,最好不要调整相关的参数。

牵涉到的JVM参数: 

-XX:+DoEscapeAnalysis:启用逃逸分析(默认打开)

-XX:+EliminateAllocations:标量替换(默认打开)

-XX:+UseTLAB 本地线程分配缓冲(默认打开)

如果是逃逸分析出来的对象可以在栈上分配的话,那么该对象的生命周期就跟随线程了,就不需要垃圾回收,如果是频繁的调用此方法则可以得到很大的性能提高。

5常用的性能评价/测试指标

一个web应用不是一个孤立的个体,它是一个系统的部分,系统中每一部分都会影响整个系统的性能

5.1、响应时间

提交请求和返回该请求的响应之间使用的时间,一般比较关注平均响应时间。

常用操作的响应时间列表:

操作

响应时间

打开一个站点

几秒

数据库查询一条记录(有索引)

十几毫秒

机械磁盘一次寻址定位

4毫秒

从机械磁盘顺序读取1M数据

2毫秒

从SSD磁盘顺序读取1M数据

0.3毫秒

从远程分布式换成Redis读取一个数据

0.5毫秒

从内存读取1M数据

十几微妙

Java程序本地方法调用

几微妙

网络传输2Kb数据

1微妙

5.2、并发数

同一时刻,对服务器有实际交互的请求数。

和网站在线用户数的关联:1000个同时在线用户数,可以估计并发数在5%到15%之间,也就是同时并发数在50~150之间。

5.3、吞吐量

对单位时间内完成的工作量(请求)的量度

5.4、关系

系统吞吐量和系统并发数以及响应时间的关系:

理解为高速公路的通行状况:

吞吐量是每天通过收费站的车辆数目(可以换算成收费站收取的高速费),

并发数是高速公路上的正在行驶的车辆数目,

响应时间是车速。

车辆很少时,车速很快。但是收到的高速费也相应较少;随着高速公路上车辆数目的增多,车速略受影响,但是收到的高速费增加很快;

随着车辆的继续增加,车速变得越来越慢,高速公路越来越堵,收费不增反降;

如果车流量继续增加,超过某个极限后,任务偶然因素都会导致高速全部瘫痪,车走不动,当然后也收不着,而高速公路成了停车场(资源耗尽)。

二、常用的性能优化手段

避免过早优化不应该把大量的时间耗费在小的性能改进上,过早考虑优化是所有噩梦的根源。

所以,我们应该编写清晰,直接,易读和易理解的代码,真正的优化应该留到以后,等到性能分析表明优化措施有巨大的收益时再进行。过早优化,不表示我们就可以随便写代码,还是需要注重编写高效优雅的代码。

进行系统性能测试

所有的性能调优,都有应该建立在性能测试的基础上,直觉很重要,但是要用数据说话,可以推测,但是要通过测试求证;寻找系统瓶颈,分而治之,逐步优化。

性能测试后,对整个请求经历的各个环节进行分析,排查出现性能瓶颈的地方,定位问题,分析影响性能的的主要因素是什么?内存、磁盘IO、网络、CPU,还是代码问题?架构设计不足?或者确实是系统资源不足?

1、前端优化常用手段

1.1、浏览器/App

  • 减少请求数
  • 合并CSS,Js,图片, 
  • 生产服务器提供的all的js文件
  • http中的keep-alive(http1.1中默认开启)包括nginx

1)使用客户端缓冲:

静态资源文件(css、图标等)缓存在浏览器中,有关的属性Cache-Control(相对时间)和Expires

如果文件发生了变化,需要更新,则通过改变文件名来解决。

2)启用压缩

浏览器(zip),压缩率80%以上。

减少网络传输量,但会给浏览器和服务器带来性能的压力,需要权衡使用。

3)资源文件加载顺序

css放在页面最上面,js放在最下面。这样页面的体验才会比较好。

浏览器会加载完CSS才会对页面进行渲染。

JS只要加载后就会立刻执行(有些JS可能执行时间比较长)。

4)减少Cookie传输

cookie包含在每次的请求和响应中,因此哪些数据写入cookie需要慎重考虑(静态资源不需要放入cookie)

5)友好的提示非技术手段

有时候在前端给用户一个提示,就能收到良好的效果。毕竟用户需要的是不要不理他。

1.2、CDN加速

CDN,又称内容分发网络,本质是一个缓存,而且是将数据缓存在用户最近的地方。无法自行实现CDN的时候,可以根据经济实力考虑商用CDN服务。

1.3、反向代理缓存

将静态资源文件缓存在反向代理服务器上,一般是Nginx。

1.4、WEB组件分离

将js,css和图片文件放在不同的域名下。可以提高浏览器在下载web组件的并发数。因为浏览器在下载同一个域名的的数据存在并发数限制。

2、应用服务性能优化

2.1、缓存

网站性能优化第一定律:优先考虑使用缓存优化性能

优先原则:缓存离用户越近越好

2.2、缓存的基本原理和本质

缓存是将数据存在访问速度较高的介质中。可以减少数据访问的时间,同时避免重复计算。

2.3、合理使用缓的准则

频繁修改的数据,尽量不要缓存,读写比2:1以上才有缓存的价值。

缓存一定是热点数据。

应用需要容忍一定时间的数据不一致。

缓存可用性问题,一般通过热备或者集群来解决。 

2.3、分布式缓存与一致性哈希

以集群的方式提供缓存服务,有两种实现;

1)需要更新同步的分布式缓存,所有的服务器保存相同的缓存数据,带来的问题就是,缓存的数据量受限制,其次,数据要在所有的机器上同步,代价很大。

2)每台机器只缓存一部分数据,然后通过一定的算法选择缓存服务器。常见的余数hash算法存在当有服务器上下线的时候,大量缓存数据重建的问题。所以提出了一致性哈希算法。

一致性哈希

  • 首先求出服务器(节点)的哈希值,并将其配置到0~2的32次方的圆(continuum)上。
  • 然后采用同样的方法求出存储数据的键的哈希值,并映射到相同的圆上。
  • 然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。如果超过232仍然找不到服务器,就会保存到第一台服务器上。

一致性哈希算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。

数据倾斜:

一致性哈希算法在服务节点太少时,容易因为节点分部不均匀而造成数据倾斜问题,此时必然造成大量数据集中到Node A上,而只有极少量会定位到Node B上。为了解决这种数据倾斜问题,一致性哈希算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器ip或主机名的后面增加编号来实现。例如,可以为每台服务器计算三个虚拟节点,于是可以分别计算 “Node A#1”、“Node A#2”、“Node A#3”、“Node B#1”、“Node B#2”、“Node B#3”的哈希值,于是形成六个虚拟节点:同时数据定位算法不变,只是多了一步虚拟节点到实际节点的映射,例如定位到“Node A#1”、“Node A#2”、“Node A#3”三个虚拟节点的数据均定位到Node A上。这样就解决了服务节点少时数据倾斜的问题。在实际应用中,通常将虚拟节点数设置为32甚至更大,因此即使很少的服务节点也能做到相对均匀的数据分布。

3集群

可以很好的将用户的请求分配到多个机器处理,对总体性能有很大的提升

3.1、异步

同步和异步,阻塞和非阻塞

同步和异步关注的是结果消息的通信机制

同步:同步的意思就是调用方需要主动等待结果的返回

异步:异步的意思就是不需要主动等待结果的返回,而是通过其他手段比如,状态通知,回调函数等。

阻塞和非阻塞主要关注的是等待结果返回调用方的状态

阻塞:是指结果返回之前,当前线程被挂起,不做任何事

非阻塞:是指结果在返回之前,线程可以做一些其他事,不会被挂起。

1)同步阻塞:同步阻塞基本也是编程中最常见的模型,打个比方你去商店买衣服,你去了之后发现衣服卖完了,那你就在店里面一直等,期间不做任何事(包括看手机),等着商家进货,直到有货为止,这个效率很低,jdk里的BIO就属于 同步阻塞。

2)同步非阻塞:同步非阻塞在编程中可以抽象为一个轮询模式,你去了商店之后,发现衣服卖完了,这个时候不需要傻傻的等着,你可以去其他地方比如奶茶店,买杯水,但是你还是需要时不时的去商店问老板新衣服到了吗。jdk里的NIO就属于 同步非阻塞

3)异步阻塞:异步阻塞这个编程里面用的较少,有点类似你写了个线程池,submit然后马上future.get(),这样线程其实还是挂起的。有点像你去商店买衣服,这个时候发现衣服没有了,这个时候你就给老板留给电话,说衣服到了就给我打电话,然后你就守着这个电话,一直等着他响什么事也不做。这样感觉的确有点傻,所以这个模式用得比较少。

4)异步非阻塞:好比你去商店买衣服,衣服没了,你只需要给老板说这是我的电话,衣服到了就打。然后你就随心所欲的去玩,也不用操心衣服什么时候到,衣服一到,电话一响就可以去买衣服了。jdk里的AIO就属于异步。

3.2、常见异步的手段

  • Servlet异步:servlet3中才有,支持的web容器在tomcat7和jetty8以后。
  • 多线程
  • 消息队列

4程序级别

4.1、代码级别

一个应用的性能归根结底取决于代码是如何编写的。

选择合适的数据结构

选择ArrayList和LinkedList对我们的程序性能影响很大,为什么?因为ArrayList内部是数组实现,存在着不停的扩容和数据复制。

4.2、选择更优的算法

举个例子,如何判断一个数是否为n的多少次方

//如何判断一个数是否为2的多少次方
public static void main(String[] args) throws  Exception{
    int n =2;
    Scanner scanner=new Scanner(System.in);
    System.out.println("请输入需要计算的数:");
    while(scanner.hasNext()){//控制台输入
        int input =scanner.nextInt();
        while (true){ //循环 求余数
            if(input ==n){
                System.out.println("是("+n+")的次方");
                break;
            }
            if(input%n !=0){
                System.out.println("不是("+n+")的次方");
                break;
            }else{
                input = input/2;
            }
        }
        if((input&(input-1)) ==0){
            System.out.println("是("+n+")的次方");
        }else{
            System.out.println("不是("+n+")的次方");
        }
    }
}

4.3、编写更少的代码

同样正确的程序,小程序比大程序要快,这点无关乎编程语言。

4.4、并发编程

4.5、资源的复用

目的是减少开销很大的系统资源的创建和销毁,比如数据库连接,网络通信连接,线程资源等等。

  • 单例模式
  • 池化技术

三、存储性能优化

1、尽量使用SSD

2、定时清理数据或者按数据的性质分开存放

3、结果集处理

4、用setFetchSize控制jdbc每次从数据库中返回多少数据。

四、总结

调优是个很复杂、很细致的过程,要根据实际情况调整,不同的机器、不同的应用、不同的性能要求调优的手段都是不同的。也没有一个放之四海而皆准的配置或者公式。即使是jvm参数也是如此,再比如说性能有关的操作系统工具,和操作系统本身相关的所谓大页机制,都需要平时去积累,去观察,去实践。

Guess you like

Origin blog.csdn.net/m0_37661458/article/details/91576694