JVM

JVM可以以下面这个博客为大纲,这个博客写的确实好
http://www.cnblogs.com/zuoxiaolong/category/508918.html

写在前面:
1.关系是包含关系:
JDK 包含 JRE 包含 JVM。。
要开发java程序的话那必须要有JDK,如果只是运行的话,那有JRE(JAVA运行时环境)就够了。
装JDK的时候,还会让你安装一下JRE的。。
关于为什么JDK里面还要JRE? 看看这个博客里面介绍:
《Java基础学习第一天——JVM简介与环境变量配置》https://blog.csdn.net/lutianfeiml/article/details/51088251
2.sun公司根据不同的操作系统,写了不同的JVM,所以java才能跨平台。
3.而且JVM是一种规范,其他公司包括个人也可以来写一个JVM,它定义好规范,大家来实现。我们现在用的最多的虚拟机就是hotspot。
4。配置环境变量。先配置JAVA_HOME,再配一个path。为什么? 见下图
path引用JAVA_HOME的值,最好别写死。灵活些。
JDK1.5以上的版本不用配classpath了,因为它会去当前路径下找相关的类了。。
这里写图片描述
这里写图片描述
这里写图片描述
5、JVM的跨平台性表现:一个编译好的jar包可以在不同平台上运行。 write once run anywhere

JVM:GC,类加载机制,内存

面试的时候问的多,工作中用的也比较多的有:3、5、6、7、。。。
那这7点,面试都会问。
这里写图片描述

1.1虚拟机介绍

系统虚拟机: 这个是面向操作系统的
比如vmware,主流的。虚拟云平台。
虚拟云平台:后端可能是一堆的物理机,但是前端提供的都是虚拟化出来的虚机。用户可以动态地去调整配置。 像这种的开源的框架比如:openstack。
vmware稍微扫盲一下:它有三种模式:一是桥接模式:虚拟机和本地在同一个网段(网关,掩码,DNS,都在一个网段,只是IP不一样)。如果是桥接和host的话用的就是VMnet1卡,NAT模式用的就是VMnet8卡。二是host-only(不能访问外网)、三是NAT(网络转换)共享主机的IP 。

程序虚拟机:这个是面向程序的。
有很多,比较典型的是JVM,JVM有很多种,现在用的最广泛的是HotPot虚拟机。。 它用来执行class文件。
这里写图片描述

JVM组成

主要分为9部分
这里写图片描述

3和7比较重要,问的多
1、类加载子系统:顾名思义,就是对class文件加载,识别
2、方法区(也称永久区):存放类的代码信息,static变量,常量池。
3、堆 :就是对象,所有的对象都是放到堆中。
4、直接内存:
5、栈:局部变量
6、本地方法栈:基本没怎么用。
7、垃圾收集系统(GC):里面的算法,常用的GC的机制,以什么为目标去设计的。
8、PC寄存器:存一些指针的,用来计数的。
9、执行引擎:就是执行class文件的
这里写图片描述
这里写图片描述

分步看:
堆:new的对象放在哪儿。
方法区(永久区):解决堆栈信息的产生,是一个先决条件。如何理解?
要new一个对象,(比如new User()的实例出来,它需要一个模版吧 )、JVM会去方法区里找到User模版再去实例一个对象,然后放到堆里面,然后对应user的地址就放到栈里面。
User u = new User(); 这个User是哪儿来的,它是一个class,那肯定是放在方法区中的。new操作的时候,就给对象u分配了一块内存。
总之:方法区中存放着好比是一个模版的东信息,要实例化对象就把对象放到堆里面,外面想使用那必须要一个引用,引用就放到栈里面,这就是堆栈方法区的关系。
这里写图片描述

堆栈永久区详细讲解:
java的堆是通过GC自动化管理的。 java堆里面是怎么划分的,详细解释下,这是面试问的多的。
老年对象:年龄很大的对象,存放了很久的对象。那年龄大小是如何划分的? 是根据垃圾收集器回收的次数去划分的。

这里写图片描述

堆:宏观的可以分为新生代和老年代
新生代又分三块,第一个是eden区(翻译成伊甸园,亚当夏娃出生的地方)。
java对象刚new出来的时候,这个对象是放在eden区的,经过了一次GC(也就是一次新生代回收)之后,如果对象还存活着(就是程序还用着这个对象),那这个对象就会出来进入到s0或者是s1区。
这个对象可能到s0区,也有可能到s1区,不可能两个区都有。
s0s1两个区域是大小相等并且可以相互转换角色的空间。为什么这么设计?
其实写java代码我们发现,很多对象其实也就使用那么一两次,那么在新生代的时候垃圾回收就太频繁了,需要回收的对象太多了。
举例子:一个程序中有100个对象,可能只有1到2个(比例是1%-2%)对象会一直存活15次以上。绝大多数对象都是在新生代生产和消亡。
因为新生代对象回收太频繁了,所以就用复制算法,去清空其中一个空间。复制不会包括eden区。
老年代:java默认是经过15次GC后,对象由新生代进入到老年代。老年代的对象不多,比如servlet,tomcat整个生命周期都存在的对象就会在老年代。

再看看栈:
不用深究,大致知道(局部变量表和帧数据区是存放一些打印的信息,临时变量存到操作数栈 )就行了。面试是不会深究栈的。。。
java每个线程是创建的时候 都会有一个栈。
看着下面的图:
补充一下:操作数栈:什么叫计算中间结果。就比如:冒泡的时候两数交换不是有个temp嘛,那个就是。
这里写图片描述

再看看方法区:
下图的溢出:比如你写了个很大的项目,四五百个类,但是方法区你就给个几兆的空间,那tomcat刚跑的时候就会报:方法区溢出。但是现在流行微服务,分布式,所以一台机器上一般不会让太多的类。 这里知道即可。
这里写图片描述

JVM参数讲解

这里写图片描述

大多数虚拟机参数都是为堆去服务的。

扫盲:
-XX : 对于系统级别(也就是JVM本身)的配置,比如配置日志信息 或者说配置JVM使用什么样的垃圾回收器。
非-XX :基本上都是对 应用层面上的配置。(比如给tomcat设置内存大小啊)
后面的加号、减号
+ : 表示启用
- : 表示禁用
如-XX: +PrintCommandLineFlags:配这个是打印出 你传给JVM具体的配置,简单来说就是你怎么配JVM的,给打印出来。
JVM给应用程序配的初始化内存大小,最大内存大小。(注意主语)

这里写图片描述

打印的信息解释
1.:新生代回收的情况,老年代【Tenured】回收的情况,永久区【Perm】回收的情况。经历的时常【Time】..
2.max memory:应用程序拥有的最大内存
free memory:还可以使用多少
total memory:已经使用了多少内存。


设置新生代内存大小(-Xmn)和各个区比例(-XX:SurvivorRatio )。
整个内存大小是一定的,新生代设置大了,老年代自然小了。新生代太大太小都不行。
这里写图片描述

具体实验见程序。。。。

public class Test02 {

    public static void main(String[] args) {

        //第一次配置
        //-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

        //第二次配置
        //-Xms20m -Xmx20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

        //第三次配置
        //-XX:NewRatio=老年代/新生代
        //-Xms20m -Xmx20m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

        byte[] b = null;
        //连续向系统申请10MB空间
        for(int i = 0 ; i <10; i ++){
            b = new byte[1*1024*1024];  //1M,循环10次。
        }
    }
}

OOM是比较可怕了,会引起严重的业务中断。
这里写图片描述

调试:生成的heap的dumping放在某个文件夹下。

public class Test03 {

    public static void main(String[] args) {

        //-Xms4m -Xmx4m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/Test03.dump
        //初始化和最多只分配了4M,但是程序要用5M,这个时候抛异常。内存溢出
        //堆内存溢出
        Vector v = new Vector();
        for(int i=0; i < 5; i ++){
            v.add(new Byte[1*1024*1024]);
        }

    }
}

3.5 栈配置
了解即可。
比如写一个无限递归,就会报错,原因在于这里。
-Xss 栈空间是一个固定的值,没有初始和最大值。
这里写图片描述


3.6 方法区
了解即可。
这里写图片描述


3.7 直接内存如何配
了解即可。
这里写图片描述

4、垃圾回收器。GC

GC垃圾回收并不是立刻马上地去执行,而是由JVM帮你去做这个事。
后面讲到的kafka,它是基于内存的,一断电就掉了。。

先看看几种算法:面试可能会问到。。
早期的两种,都不是特别好:
1。引用计数法:会有bug。java中复杂的时候,引用来引用去,这个时候这个算法会有bug
2.。标记清除法:会有内存碎片,空间不连续的问题。内存碎片的意思:假如有1M空间需要清除,但是GC只会清除0.99,那下次对象再存储的时候0.01(也就是内存碎片)就存不了数据了。这样会导致空间不连续。
现在的两种方式:分别用于现在JVM的新生代和老年代当中:
复制算法:看图片介绍
标记压缩:把所有的需要清除的对象压缩到内存的一端去了,解决了内存碎片的问题。。

为啥是两个不同的算法:
新生代为何用复制:因为新生代里的对象死的快,存活的少(比如100个对象就三四个是存活的),大部分都是要被GC的,所以需要大块的空间转换,进行整体的清空(并不是一个一个的清空),这样性能比较高。
老年代为何用标记压缩:因为老年代里的对象存活的概率很高,需要清空的只是很少一部分(可能100个才需要清空一两个),所以内存的操作就要尽量的最小化一些 来提高JVM性能。
这里写图片描述

看看分区算法
这里写图片描述
停顿:GC介入应用系统,导致应用系统停顿。
这里写图片描述

4.5 如何进入 — 15次后进入,15次一般别去改。
新生代无法装入的大对象,会直接进入到老年代,老年代也装不下,就直接内存溢出了。
大对象的大小也可以设置,比如设置成大于1M的时候,就直接扔到老年代去。

这里写图片描述
代码:

public class Test05 {

    public static void main(String[] args) {
        //初始的对象全部都在eden区,这个时候老年代区使用率是0%。
        //参数:-Xmx64M -Xms64M -XX:+PrintGCDetails
//      for(int i=0; i< 5; i++){
//          byte[] b = new byte[1024*1024];
//      }



        //测试进入老年代的对象
        //初始一个G,最大也是一个G,
        //参数:-Xmx1024M -Xms1024M -XX:+UseSerialGC(串行垃圾收集机制) -XX:MaxTenuringThreshold=15(进入老年代次数是15) 
        //-XX:+PrintGCDetails  打印详细信息
        //-XX:+PrintHeapAtGC
//      Map<Integer, byte[]> m = new HashMap<Integer, byte[]>();
//      for(int i =0; i <5 ; i++) {
//          byte[] b = new byte[1024*1024];
//          m.put(i, b);  //把数据放进map里,也就是map一直被引用着,内存不会释放。
//      }
//      
        //下面的代码涉及到6G数据的生产和消亡,而最大才1G,所以它会经历很多次的GC。所以下面for循环的目的是不断地去GC。
        //下面for循环的目的也是看一下,程序经过15次GC后,内存的变化。
//      for(int k = 0; k<20; k++) {
//          for(int j = 0; j<300; j++){
//              byte[] b = new byte[1024*1024];  //这里new出来就不用了,所以会涉及到GC
//          }
//      }


    }
}

DefNew:表示的是新生代。
这里写图片描述

4.6:关于TLAB,main方法里就是主线程。那每个线程里面都会有一个TLAB区,TLAB区是线程独有的。
讲volatile的时候讲到过,线程启动的时候,会先分配一块独立的内存空间,这个空间就是TLAB。它的作用见下图:
一般地正常写代码的时候,TLAB区是不需要你做任何事儿的。下面这些配置JVM都提供了默认配置。
TLAB区:本质上也是堆。只不过它是一个逻辑的概念,而不是一个物理的概念。物理层面上就是堆。意思是线程启动的时候,会给它分配一块区域,这块区域叫做TLAB。
这里写图片描述

4.7 new一个对象的时候,jvm流程图

这里写图片描述

4.8 写文档的技巧:
标题深度不要过深,3级足够。 标题简单,四到六个字。

5、咕泡学院JVM。

通俗讲解JVM最难理解的部分。
面试都容易问。
这里写图片描述

1、JVM认识

JVM还有一个功能,就是JMM内存管理。
JVM帮你做了内存管理的事情,程序员就专注于写业务逻辑即可。。 你new一个对象user出来,不需要关心在哪分配内存,不需要关心对象什么时候回收,JMM都帮你做了。这是好处。
坏处是,当出现内存溢出,内存泄漏的时候,就蒙蔽了。

2、JVM运行时数据区

一共五个部分。。。
指令这块都是 线程安全的。

1、程序计数器理解:
指令都要在CPU上去执行,CPU调度策略很复杂,比如有抢占式的。这个时候有个场景:
线程A在执行,线程B来了也要执行,那线程A就必须挂起一会儿。
那线程挂起一下之后要去恢复,它就要知道它恢复后要去干嘛。但是线程作为一个执行体是不会保存它在干嘛的,它只负责去执行,不负责保存。
那这个时候就要有一个地方去存储当前线程正在执行的字节码指令的地址,这就是程序计数器的概念和功能。
下图的线程A、线程B是一个帮助理解的逻辑图,不是实际存储图。
从图中就可以理解,程序计数器是线程私有的,它不会共享。
这里写图片描述

2、虚拟机栈:(缕清这逻辑关系)虚拟机栈也是线程独享的,也就是线程安全的。
栈 —》是数据结构 –》是用来存储数据的。 存什么数据呢? 看定义吧。。。
其实每一个方法都是由线程来执行的,比如执行main方法的时候,就是主线程去执行的。比如spring容器启动的时候,也是由一个主要的线程来启动的。
而上面又说过了,线程它只是一个执行体,和数据无关,它不存储数据。
那方法这块代码总得有个地方存吧,它是用虚拟机栈来存的。。
那具体怎么存的? 看看下面
往栈里放或者拿出来,都是以一个个单元来放或者拿的。这个单元就是栈帧。。
每个方法对应一个栈帧(下图紫色的区域)。因为它的概念就是存储当前线程运行方法时所需要的数据、指令、返回值。而线程一下只能运行一个方法,所以就是一个方法对应一个栈帧。

栈帧里面的结构又主要包含四个部分(实际包含更多):
局部变量表:这里是存放方法里面的局部变量的,所以在编译的时候就知道要分多大。
操作数栈:栈是用来存数据的,存操作数的。
这里写图片描述

插一个小知识点:
什么叫寻址范围?32位的机器最多的寻址是4G,因为2的32次方就等于4G。所以它表示不了比4G更大的地址了。加了也不识别。

栈的深度就是一个栈里面可以放多少个栈帧。。。

3、本地方法栈:
就是存放那些个native方法的
虚拟机栈是执行java代码需要的栈,那本地方法执行的时候也需要一个栈,就是本地方法栈。。。

4、方法区:
存什么东西的,见上图就行。。。。1.8之后把常量放到堆里面去了。。永久代变成meta space了。
为什么要变成meta space呢,因为永久代会溢出,1.8之后永久代就不会溢出了。
官网声明的是,meta space是永远不会溢出的,因为它的设计就像arraylist一样,可以无限扩展的。

稍稍对面做一个总结,看代码的时候:
方法区:类信息、常量(1.7+有变化)、静态变量。
堆:成员变量
方法执行的时候,会把方法的局部变量压到栈帧里面去。
—– 要有这样的思维,那代码有问题了,就知道是哪个部分出问题 了。。

这里写图片描述

5、堆 — 重要, 会涉及到内存模型。。
那堆的东西就是线程共享的了。。(图中方法区和堆都是共享的。。)
这里写图片描述

看看堆中的内存模型:(注意这个颜色的分类)
带着问题去学?为什么要分代,有什么好处?新生代里面为啥还要分代? 分代后为啥默认比例是8:1:1。
一个8M对象进来,排除大对象生命周期等因素,它会分配在哪里?? 答:分在新生代eden区。
~~~~~~~~如果是9M那直接进老年代了
8M分配在eden之后,再进来1M 呢? 8M直接进老年代,1M进eden区。

这里写图片描述

再举个例子:
一个8M对象分配在eden区了,又来一个1M对象分配在s0区,那这时候又来一个2M对象,这时候咋办呢? 就会触发一次Minor GC。
8M进入老年代,1M进入s1,2M进入eden区。

插一个知识点:static变量会有内存泄漏的风险,因为static变量、常量释放不了内存空间。
所谓内存泄漏:无法释放已申请的内存空间。。
内存溢出:程序申请内存时,内存不够。。
https://blog.csdn.net/ruiruihahaha/article/details/70270574
为什么要分代? 这个视频里说的,堆一共分配60M,那我用了50多M,才发生内存溢出,说明这种设计的空间利用率可以达到90%以上,这就是分代牛逼的地方。
这里写图片描述

GC ROOT 可以由哪些组成?
由静态变量,常量,虚拟机栈里局部变量表的一些字段。垃圾回收器就是为了避免Full GC的。
Minor GC ,Full GC触发条件???
https://blog.csdn.net/yhyr_ycy/article/details/52566105

6、老马的虚拟机调优

内容如下:
主要看看堆内存参数调整 – 工具很多,比如JConsole,VisualVM,eclipse的插件等等。
垃圾收集器参数调整
学完这些知识之后,再对tomcat进行调优。(调优是无监控、不调优)
这里写图片描述

1、java内存结构

一般地,eden和s0 s1的比例是8:1:1 。新生代和 tenured(老年代) 比例是 1:2(1:3)
为什么eden和s0是8:1,因为大多数的情况下第一次GC回收的对象是很多的。差不多80%左右。
但是具体的还是要看你的项目了。

琐碎知识记录:
1)简单说:一个线程一个栈,一个方法一个栈帧。。
一个方法对应着一个栈帧,不同方法里面的变量位于不同的栈帧里面,所以不会冲突。
2)永久区放什么? 放class文件的信息,静态变量,常量。。。
我们常说的,把class文件load到内存里去,load到哪去? 就是永久区。
3)我们常说的JVM优化,其实主要是优化堆。 堆是JAVA内存区域最大的地方。
这里写图片描述

2、GC如何确定垃圾

1)没有引用指向的对象就是垃圾吗?这个说法不严谨。因为内存中可能存在一对垃圾,或者环形垃圾。他们是有引用指向的,但是还是垃圾。 其实也不用说多么精确的定义,面试的时候画一图,说清楚就行。
如果面试的时候,能说出软引用,弱引用,虚引用,那也能加分。。。
2)GC roots对象都有哪些呢? 博客中有讲。 除此之外还有比如,main方法里一开始创建的那些对象可以作为root对象。还有比如一些classloader也是root对象。
这里写图片描述

3、如何回收–不同的垃圾收集算法

确定了什么是垃圾了,那就要对垃圾进行回收了。。。 这一块是面试特别喜欢问的。

1)标记清除算法 —- 缺点:最大的问题在于内存的碎片化。。。
如果来了一个大对象,发现没有这么大的内存可以存放,那就会触发一次Full GC。
这里写图片描述

2)复制算法 这个算法用到新生代s0 s1之间
新生代产生的垃圾是很多的,所以适合复制算法(一批一批对象进行复制)。
优点:回收的时候,一边复制到另一边。效率比较高,而且没有内存碎片。
缺点就是:非常的浪费内存,如果是4G内存,那么你只有2G可以用。
这里写图片描述

3)标记压缩算法 这个算法用在老年代里面
老年代产生的垃圾实际上是比较少的,压缩到内存的一端的垃圾比较少,所以适合标记压缩算法了。
缺点:效率也不是特别高。从效率上来说,标记/整理算法要低于复制算法。
这里写图片描述

4)JVM采用分代算法
这里写图片描述

垃圾收集器

补充一下:常用的虚拟机有:openJDK,hotspot,等
JDK1.8 hotspot默认的模式是server模式, server模式做了很多参数的优化。

垃圾收集器可以用不同的算法。。
有好几种垃圾收集器:
1、串行垃圾收集器。缺点:吞吐量小 。适合于小数据量 和 只有一个CPU。
2、并行垃圾收集器,JDK8中 默认用的是并行收集器。可以利用多核CPU。不过每次GC的时候,
并发量大,JVM会停顿,收集完之后JVM才可以继续执行。。
3、并发收集器: CMS Collector 。。停顿时间短。。
4、G1。。不仅停顿短,同时并发大。(它是2 和 3的折中选择 )

还是那句话:无监控,不调优。实际选用哪个垃圾收集器还是要看监控结果。。
这里写图片描述

java对象的分配

解读:
1、栈上分配是什么意思????
new出来的东西也可以放在栈上,如果new出来的对象特别小,JVM又开了栈上优化(server模式都是开了的,怎么开呢?就是打开逃逸分析DoEscapeAnalysis ),那这个对象就会放到栈上。
放在栈上的好处: 方法对应栈帧,只要方法一结束,那个栈帧就没了,都不用垃圾收集了,自己就收集了,所以栈上分配的效率特别高。。但是栈空间本来就小,所以只适合小对象。

如果栈上空间分配满了,不适合了。
2、第二步就是去找线程本地内存(这时应该想到volatile,每个线程执行的时候都有属于自己的那个工作内存,也就是线程本地内存)。 TLAB其实也是堆,使用了TLAB的时候,多个线程在eden区分配对象的时候就不需要加锁了,效率一下提高了。

补充下:线程本地内存是从eden区申请的,每个线程默认申请1%的空间给它自己专用。
TALB其实也是使用eden区的。记住。。

3、所以一个对象new出来,先放栈上,栈放不下就放本地内存,本地放不下就看看对象是不是太大了,适不适合放在老年代,如果不是特别大就放eden。

这一部分的东西,无需优化,无需调整,用JVM默认的即可。
这里写图片描述

调试的时候加参数:
上面的是args的参数,下面是虚拟机的参数。
下图的意思是 对象不在栈上分配,不在本地内存分配,都在堆的eden区分配。。(都是减号,禁用的意思 )
这里写图片描述

检测工具的使用,测堆和栈

1、让内存溢出。。进而用工具检测,是哪一步导致了内存溢出。 (会抛OOM OutOfMemeryyError)注意这里是error,不是exception
-XX:+HeapDumpOnOutOfMemeryError :有内存溢出错误的时候,把堆的信息给dump出来,dump到指定文件下。
dump文件怎么分析呢,有很多工具。
测试思路:不停去分配内存。

看dump文件的分析结果:发现是一个char[]数组占用了太多的内存。
这里写图片描述

2、测试栈的深度。。 (会抛Stack OverflowError ,栈溢出),注意这里是error,不是exception
测试思路:就用递归调用,递归调用就是调一个方法启一个栈帧,调一个启一个。栈帧也是耗空间的,栈帧一多,栈就满了。。。
参数:-Xss128k、(-Xss512k)
xss比较小,并发就好。为什么呢?很简单,总大小一样,你每个线程占的大小少,总的数量就多啊。
这里写图片描述

典型tomcat优化配置

Tomcat 启动命令行中的优化参数,就是 JVM 的优化 。Tomcat 首先跑在 JVM 之上的

-Xms4g,起始大小 给4g
-XX:+AggressiveOpts 侵略性的优化,什么意思?凡是虚拟机里能用到的优化,全都用上。
-XX:+UseBiasedLocking 偏执锁,什么意思呢,百度去。
永久区 最开始给它64M,如果不够用,后面最大给它300M。
这里有个经验:一般eclipse启不来,往往就是PermSize设置太小了,eclipse里面的类太多。
但是这个参数JDK1.8取消了,永久代变成元空间了。。
-XX:+。。最后一个是不让你显示调用GC。也就是system.gc()不启作用。。
注:system.gc()是让java运行垃圾收集器这个线程。这个参数有什么用呢,有时候程序中老是system.gc()会干扰你去进行JVM调优,这里把它屏蔽掉。。。
这里写图片描述

后面是一些复杂的参数:

这里写图片描述

配完之后进行tomcat性能测试:
如何测呢?简单说下
一个服务器上面跑了一个tomcat对外提供服务,怎么知道tomcat支持多大的并发访问,而且响应还比较快呢?
启好多个机器,每个机器上跑一万个线程,不停地去访问某一个页面,看它能不能撑得住。几万个并发一上来,看它的响应时间是不是拖长了。就是这样的一个测试。。。。。。

常用的自动化测试工具:loadrunner、selenium等,这些都是收费的。JMeter是免费的。
Jmeter测试方法:在一个线程组里面加几千个线程,去访问一个jsp页面。
测试的结果放到一个监听器里面。。然后运行。。
视频中的测试结果:
没有优化前,一秒钟最多支撑605次HTTP的访问(吞吐量 )
优化后,一秒钟最多支撑434次,差不多优化了40%–50%。 网上的文章测试显示的是差不多三倍的结果。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值