提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
一、Java 程序的执行过程
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World"); }
}
查看字节码
\classes>javap -v HelloWorld.class
或者安装插件
0 getstatic #2 <java/lang/System.out> // getstatic 获取静态字段的值
3 ldc #3 <Hello World> // ldc 常量池中的常量值入栈
5 invokevirtual #4 <java/io/PrintStream.println> // invokevirtual 运行时方法绑定调用方法
8 return //void 函数返回
总结:
Java 虚拟机采用基于栈的架构,其指令由操作码和操作数组成。这些 字节码指令 ,就叫作 opcode。其中, getstatic、ldc、invokevirtual、return 等,就是 opcode,可以看到是比较容易理解的。
JVM 就是靠解析这些 opcode 和操作数来完成程序的执行的。当我们使用 Java 命令运行 .class 文件的时候,实际上就相当于启动了一个 JVM 进程。
JVM 会翻译这些字节码,它有两种执行方式。
- 常见的就是解释执行,将 opcode + 操作数翻译成机器代码;
- 另外一种执行方式就是 JIT,也就是我们常说的即时编译,它会在一定条件下将字节码编译成机器码之后再执行
第2章 java虚拟机的内存管理
1.JVM整体架构
根据 JVM 规范,JVM 内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五个部分
JVM分为五大模块: 类装载器子系统 、 运行时数据区 、 执行引擎 、 本地方法接口 和 垃圾收集模块 。
- 类加载子系统:.class文件要想加载到内存中,必须经过加载(引导类加载器,扩展类加载器,应用类加载器),连接(校验,准备,解析)和初始化(给静态变量赋上初值为0的操作等)过程
- 运行时数据区:加载完成之后,数据就会进入到运行时数据区,在这里有
- PC程序计数器(里面有对应的指令编码:记录每一个线程在执行的每一个位置)
- STACK AREA(虚拟机栈):会有多个线程,也会有一些小操作对应栈帧(Stack Frame)进行对应的每一步操作
- HeapArea堆空间:存储对象的区域
- Method Area(方法区):用来加载类中的方法,静态变量
- 本地方法栈:为调用本地方法提供服务的
- 将字节码文件变成二进制文件就需要执行引擎
两种编译方式解释器和JIT编译器,不想要了可以放到垃圾回收器
2.JVM运行时内存(运行时数据区)
Java 虚拟机有自动内存管理机制,如果出现面的问题,排查错误就必须要了解虚拟机是怎样使用内存的
Java7和Java8内存结构的不同主要体现在方法区的实现(通常使用的Java SE都是由Sun JDK和OpenJDK所提供,该版本使用的VM就是HotSpot VM)
- JDK7 内存结构
- JDK8 的内存结构
区别:用元空间+直接内存替代方法区
jdk8详细
2.1 PC 程序计数器
一中记录了如何查看
2.2 虚拟机栈
1.设置内存大小
2.局部变量
5.操作栈
6.动态链接
Java虚拟机栈中,每个栈帧都包含一个指向运行时常量池中该栈所属方法的符号引用,持有这个引用的目的是为了支持方法调用过程中的动态链接(Dynamic Linking)。
7.方法返回地址
方法返回地址存放调用该方法的PC寄存器的值。一个方法的结束,有两种方式:正常地执行完成,出现未处理的异常非正常的退出。无论通过哪种方式退出,在方法退出后都返回到该方法被调用的位置。方法正常退出时,调用者的PC计数器的值作为返回地址,即调用该方法的指令的下一条指令的地址。而通过异常退出的,返回地址是要通过异常表来确定,栈帧中一般不会保存这部分信息。
无论方法是否正常完成,都需要返回到方法被调用的位置,程序才能继续进行。
异常的保存位置
2.4堆(JVM中最大的一块区域)
1.设置堆空间大小
-Xmx20m -Xms5m -XX:+PrintGCDetails
2.查看老年代年轻代
-Xmx600m -Xms600m -XX:NewRatio=4 -XX:SurvivorRitio=8
安装插件
运行代码观察
第3章 JVM加载机制详解
1.类装载子系统
要证明这一点很简单,写一个空的main函数,设置虚拟机参数为"-XX:+TraceClassLoading"来获取类加载信息,运行一下,可以看到加载了哪些类
XX:+TraceClassLoading
第4章 垃圾回收机制及算法
4.垃圾收集器
-XX:+PrintCommandLineFlags产看程序使用的默认JVM参数
1.使用方式:-XX:+UseSerialGC 【设置垃圾回收器Serial/Serial Old收 集器】
-XX:+PrintCommandLineFlags
-XX:+UseSerialGC
2.ParNew收集器
使用方式:-XX:+UseParNewGC
设置线程数: XX:ParllGCThreads
-XX:+UseParNewGC
XX:ParllGCThreads
第5章 常用指令与可视化调优工具
1.常用指令
1.1 jps
查看java进程及其相关的信息,如果你想找到一个java进程的
pid,那可以用jps命令替代linux中的ps命令了
jps [options] [hostid]
options参数解释:
-l : 显示进程id,显示主类全名或jar路径
-q : 显示进程id
-m : 显示进程id, 显示JVM启动时传递给main()的参数
-v : 显示进程id,显示JVM启动时显示指定的JVM参数
hostid : 主机或其他服务器ip
最常用示例:
jps -l 输出jar包路径,类全名
jps -m 输出main参数
jps -v 输出JVM参数
这三个参数可以组合
eg: jps -lmv
1.2 jinfo
jinfo是用来查看JVM参数和动态修改部分JVM参数的命令
pid必输
jinfo [option] <pid>
options参数解释:
- no options 输出所有的系统属性和参数
- -flag 打印指定名称的参数
- -flag [+|-] 打开或关闭参数
- -flag = 设置参数
- -flags 打印所有参数
- -sysprops 打印系统配置
打开GC日志参数
jinfo -flag PrintGCDetails 18076 查看是否打印GC详情
jinfo -flag +PrintGCDetails 18076 设置
jinfo -flag HeapDumpPath=d:/ 18076 堆生成dump文件的位置
查看JVM参数和系统配置
jinfo 11666
jinfo -flags 11666
jinfo -sysprops 11666
1.3 jstat
jstat命令是使用频率比较高的命令,主要用来查看JVM运行时的状态信息,包括内存状态、垃圾回收等。
jstat [option] VMID [interval] [count ]
其中VMID是进程id,interval是打印间隔时间(毫秒),count是打印次数(默认一直打印)
- option参数解释:
- -class class loader的行为统计
jinfo -flag -PrintGC 11666 jinfo -flag -PrintGCDetails 11666 - -compiler HotSpt JIT编译器行为统计
- -gc 垃圾回收堆的行为统计
- -gccapacity 各个垃圾回收代容量(young,old,perm)和他们相应的空间统计
- -gcutil 垃圾回收统计概述
- -gccause 垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因
- -gcnew 新生代行为统计
- -gcnewcapacity 新生代与其相应的内存空间的统计
- -gcold 年老代和永生代行为统计
- -gcoldcapacity 年老代行为统计
- -printcompilation HotSpot编译方法统计
1.4 jstack
jstack是用来查看JVM线程快照的命令,线程快照是当前JVM线程正在执行的方法堆栈集合。使用jstack命令可以定位线程出现长时间卡顿的原因,例如死锁,死循环等。jstack还可以查看程序崩溃时生成的core文件中的stack信
息。
jstack [options]
option参数解释:
- -F 当使用jstack 无响应时,强制输出线程堆栈。
- -m 同时输出java堆栈和c/c++堆栈信息(混合模式)
- -l 除了输出堆栈信息外,还显示关于锁的附加信息
cpu占用过高问题
1.使用Process Explorer工具找到cpu占用率较高的线程
2.在thread卡中找到cpu占用高的线程id 3.线程id转换成16进制
4.使用jstack -l 查看进程的线程快照
5.线程快照中找到指定线程,并分析代码
jstack检查死锁问题
打印结果
Found one Java-level deadlock:
1.5 jmap
jmap可以生成 java 程序的 dump 文件, 也可以查看堆内对象示例的统计信息、查看 ClassLoader 的信息以及finalizer 队列
jmap [option] (连接正在执行的进程)
option参数解释:
- 如果使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始 地址、映射大小以及共享对象文件的路径全称。
- -heap 打印java heap摘要
- -histo[:live] 打印堆中的java对象统计信息
- -clstats 打印类加载器统计信息
- -finalizerinfo 打印在f-queue中等待执行finalizer方法的对象
- -dump: 生成java堆的dump文件
dump-options:
1.live 只转储存活的对象,如果没有指定则转储所有对象
2.format=b 二进制格式
3.file= 转储文件到
map -dump:live,format=b,file=dump.bin 11666
1.6 jhat
jhat是用来分析jmap生成dump文件的命令,jhat内置了应用服务器,可以通过网页查看dump文件分析结果,jhat一般是用在离线分析上。
jhat [option][dumpfile]
option参数解释:
- -stack false: 关闭对象分配调用堆栈的跟踪
- -refs false: 关闭对象引用的跟踪
- -port : HTTP服务器端口,默认是7000 -debug : debug级别
- -version 分析报告版本
2. JVM常用工具
2.1.1 内存监控
Jconsole(Java Monitoring and Management Console)是从java5开始,在JDK中自带的java监控和管理控制台,用于对JVM中内存,线程和类等的监控,是一个基于JMX(java management extensions)的GUI性能监测工具。jconsole
使用jvm的扩展机制获取并展示虚拟机中运行的应用程序的性能和资源消耗等信息。直接在jdk/bin目录下点击jconsole.exe即可启动
jconsole
2.1.2 线程监控
/**
* @Author qch
* @Date 2021/8/12
* 死锁
*/
public class Demo8jconsole03 {
/*** 线程死锁等待演示 */
static class SynAddRunalbe implements Runnable {
int a, b;
public SynAddRunalbe(int a, int b) {
this.a = a;
this.b = b;
}
public void run() {
synchronized (Integer.valueOf(a)) { //-127~128是从对应缓存(常量池)里去拿
synchronized (Integer.valueOf(b)) {
System.out.println(a + b);
}
}
}
}
public static void main(String[] args) {
//a和b一样时就会发生死锁
for (int i = 0; i < 100; i++) {
new Thread(new SynAddRunalbe(1, 2)).start();
new Thread(new SynAddRunalbe(2, 1)).start();
}
}
}
编译运行, 在"线程"页签可查看"死锁"描述。这是因为1、2两个数值在Integer类的缓存常量池[-128, 127]范围内, 这样当多次调用Integer.valueOf()方法时, 不会再每次都创建对象, 而是直接返回缓存常量池中的对象。所以上面两个线程
的同步代码块中实际上只创建了两个锁对象, 且在某一时刻互相持有对方的锁, 即"死锁"现象。
2.2 VisualVM 可视化优化工具
jvisualVM
线程dump
堆dump
实例数-》类右击
死锁检测
第6章 GC日志分析
GC日志是一个很重要的工具,它准确记录了每一次的GC的执行时间和执行结果,通过分析GC日志可以优化堆设置和GC设置,或者改进应用程序的对象分配模式
1.GC日志参数
1.1 GC日志参数
参数 | 说明 |
---|---|
-XX:+PrintGC | 打印简单GC日志。 类似:-verbose:gc |
-XX:+PrintGCDetails | 打印GC详细信息 |
-XX:+PrintGCTimeStamps | 输出GC的时间戳(以基准时间的形式) |
-XX:+PrintGCDateStamps | 输出GC的时间戳(以日期的形式) |
-XX:+PrintHeapAtGC | 在进行GC的前后打印出堆的信息 |
-Xloggc:…/logs/gc.log | 指定输出路径收集日志到日志文件 |
1.2 常用垃圾收集器参数
参数 | 描述 |
---|---|
UseSerialGC | 虚拟机在运行在 Client 模式下的默认值,打开此开关后,使用Serial+Serial Old 收集器组合进行内存回收 |
UseParNewGC | 使用 ParNew + Serial Old 收集器组合进行内存回收 |
UseConcMarkSweepGC | 使用 ParNew + CMS + Serial Old 的收集器组合尽心内存回收,当 CMS 出现Concurrent Mode Failure 失败后会使用 Serial Old 作为备用收集器 |
UseParallelOldGC | 使用 Parallel Scavenge + Parallel Old 的收集器组合 |
UseParallelGC | 使用 Parallel Scavenge + Serial Old (PS MarkSweep)的收集器组合 |
SurvivorRatio | 新生代中 Eden 和任何一个 Survivor 区域的容量比值,默认为 8 |
PretenureSizeThreshold | 直接晋升到老年代对象的大小,单位是Byte UseAdaptiveSizePolicy 动态调整 Java 堆中各区域的大小以及进入老年代的年龄 |
ParallelGCThreads | 设置并行 GC 时进行内存回收的线程数 |
GCTimeRatio GC | 时间占总时间的比率,默认值为99,只在 Parallel Scavenge 收集器的时候生效 |
MaxGCPauseMillis | 设置 GC 最大的停顿时间,只在 Parallel Scavenge 收集器的时候生效 |
CMSInitiatingOccupancyFraction | 设置 CMS 收集器在老年代空间被使用多少后触发垃圾收集,默认是68%,仅在 CMS 收集器上生效 |
CMSFullGCsBeforeCompaction | 设置 CMS 收集器在进行多少次垃圾回收之后启动一次内存碎片整理 |
UseG1GC | 使用 G1 (Garbage First) 垃圾收集器 |
MaxGCPauseMillis | 设置最大GC停顿时间(GC pause time)指标(target). 这是一个软性指标(sox goal), JVM 会尽量去达成这个目标. |
G1HeapRegionSize | 使用G1时Java堆会被分为大小统一的的区(region)。此参数可以指定每个heap区的大小. 默认值将根据 heap size 算出最优解. 最小值为 1Mb, 最大值为 32Mb |
2.GC日志分析
[a(b)[c:d->e(f), g secs] h->i(j), k secs] [Times: user:l sys=m, real=n secs]
a: GC 或者是 Full GC
b: 用来说明发生这次 GC 的原因
c: 表示发生GC的区域,这里表示是新生代发生了GC,上面那个例子是因为在新生代中内存不够给新对象分配了,然后触发了 GC
d: GC 之前该区域已使用的容量
e: GC 之后该区域已使用的容量
f: 该内存区域的总容量
g: 表示该区域这次 GC 使用的时间
h: 表示 GC 前整个堆的已使用容量
i: 表示 GC 后整个堆的已使用容量
j: 表示 Java 堆的总容量
k: 表示 Java堆 这次 GC 使用的时间
l: 代表用户态消耗的 CPU 时间
m: 代表内核态消耗的 CPU 时间
n: 整个 GC 事件从开始到结束的墙钟时间(Wall Clock Time)
3.日志分析工具
3.3 GCeasy
在线分析工具 https://gceasy.io/index.jsp
https://gceasy.io/index.jsp
VM参数: -XX:SurvivorRatio=8 -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+PrintGCDetails -Xloggc:D://logs/gc.log
public class TestGCLog04 {
private static final int _1MB=1024*1024;
public static void main(String[] args) {
ArrayList<byte[]> list = new ArrayList<byte[]>();
byte[] arr = new byte[1024 * 100];
list.add(arr);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Java HotSpot(TM) 64-Bit Server VM (25.162-b12) for windows-amd64 JRE (1.8.0_162-b12), built on Dec 19 2017 20:00:03 by "java_re" with MS VC++ 10.0 (VS2010)
Memory: 4k page, physical 16454268k(6068260k free), swap 32906696k(20647740k free)
CommandLine flags: -XX:InitialHeapSize=104857600 -XX:MaxHeapSize=104857600 -XX:+PrintGC - XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers - XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
5.291: [GC (Allocation Failure)
[PSYoungGen: 27642K->3064K(30720K)] 27642K->24839K(99328K), 0.2011370 secs]
[Times: user=0.64 sys=0.01, real=0.20 secs]
Heap
PSYoungGen total 30720K, used 29578K [0x00000000fdf00000, 0x0000000100000000, 0x0000000100000000)
eden space 27648K, 95% used [0x00000000fdf00000,0x00000000ff8e4748,0x00000000ffa00000)
from space 3072K, 99% used [0x00000000ffa00000,0x00000000ffcfe1b0,0x00000000ffd00000)
to space 3072K, 0% used [0x00000000ffd00000,0x00000000ffd00000,0x0000000100000000)
ParOldGen total 68608K, used 21775K [0x00000000f9c00000, 0x00000000fdf00000, 0x00000000fdf00000) object space 68608K, 31% used [0x00000000f9c00000,0x00000000fb143d50,0x00000000fdf00000) Metaspace used 3947K, capacity 4568K, committed 4864K, reserved 1056768K class space used 434K, capacity 460K, committed 512K, reserved 1048576K
3.4 GCViewer
GCViewer是一个小工具,可以可视化展示 生成的详细GC输出。支持Sun / Oracle,IBM,HP和BEA的Java虚拟机。它
是GNU LGPL下发布的免费软件。
下载:https://sourceforge.net/projects/gcviewer/
第7章.JVM调优实战
虚拟机 | ip | 作用 |
---|---|---|
CenOs-1 | 192.168.142.135 | tomcat |
CenOs-4 | 192.168.142.132(docker,自动启动) | mysql |
1.tomcat与JVM调优
tomcat服务器在JavaEE项目中使用率非常高,所以在生产环境对tomcat的优化也变得非常重要了。 对于tomcat的优化,主要是从2个方面入手,一是,tomcat自身的配置,另一个是 tomcat所运行的jvm虚拟机的调优
2.安装tomcat部署项目
2.1下载并安装tomcat
https://tomcat.apache.org/download-80.cgi
2.2 配置tomcat8
tar -xvf apache-tomcat-8.5.70.tar.gz
修改配置文件,配置tomcat管理用户
cd apache-tomcat-8.5.70/conf/
vim tomcat-users.xml
<role rolename="manager"/>
<role rolename="manager-gui"/>
<role rolename="admin"/>
<role rolename="admin-gui"/>
<user username="tomcat" password="tomcat" roles="admin-gui,admin,manager-gui,manager"/>
关闭重启
[root@localhost bin]# jps
12007 Jps
11870 Bootstrap
## Bootstrap 为tomcat进程
[root@localhost bin]# ./startup.sh
访问
http://192.168.142.135:8080/
但是访问还是报错
解决方法:1. 配置可以访问Server Status
#如果是tomcat7,配置了tomcat用户就可以登录了,但是tomcat8不行,需要修改一个配置文件,否则访问会报403
vim webapps/manager/META-INF/context.xml
注释以下内容
<!-- <Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" /> -->
2.3 部署web项目
放到webapps下重启
3.使用Apache Jmeter进行测试
http://jmeter.apache.org/download_jmeter.cgi
解压 打开
view-》language->chinese
选项-》外观-》window
右击
1.添加线程数
添加线程组,使用线程模拟用户的并发
设置1000个线程,每个线程循环10次,也 就是tomcat会接收到10000个请求
2.添加http请求
3.添加监听器
4.调整tomcat参数进行优化(重要)
4.1 优化吞吐量
4.1.1 禁用AJP服务
什么是AJP呢? AJP(Apache JServer Protocol)是定向包协议 。WEB服务器和Servlet容器通过TCP连接来交互;为了节省SOCKET创建的昂贵代价,WEB服务器会尝试维护一个永久TCP连接到servlet容器,并且在多个请求和响应周期过程会重用连接。
Tomcat在 server.xml 中配置了两种连接器。
- 第一个连接器监听8080端口,负责建立HTTP连接。在通过浏览器访问Tomcat服务器的Web应用时,使用的就是这个连接器。
- 第二个连接器监听8009端口,负责和其他的HTTP服务器建立连接。在把Tomcat与其他HTTP服务器集成时,就需要用到这个连接器。AJP连接器可以通过AJP协议和一个web容器进行交互
Nginx+tomcat的架构,所以用不着AJP协议,所以把AJP连接器禁用。修改conf下的server.xml文件,将AJP服务禁用掉即可。
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
4.1.2 设置执行器(线程池)
频繁地创建线程会造成性能浪费,所以使用线程池来优化:
在tomcat中每一个用户请求都是一个线程,所以可以使用线程池提高性能。 修改server.xml文件
<!--将注释打开-->
<Executor name="tomcatThreadPool" namePrefix="catalina‐exec‐" maxThreads="500" minSpareThreads="50" prestartminSpareThreads="true" maxQueueSize="100"/>
<!--
参数说明:
maxThreads:最大并发数,默认设置 200,一般建议在 500 ~ 1000,根据硬件设施和业务来判断
minSpareThreads:Tomcat 初 始 化 时 创 建 的 线 程 数 , 默 认 设 置 25
prestartminSpareThreads: 在 Tomcat 初始化的时候就初始化 minSpareThreads 的参数值,如果不等于 true, minSpareThreads 的值就没啥效果了 maxQueueSize,最大的等待队列数,超过则拒绝请求
-->
#引用使得执行器生效
<Connector port="8080" executor="tomcatThreadPool" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
4.1.3 设置最大等待队列
默认情况下,请求发送到tomcat,如果tomcat正忙,那么该请求会一直等待。这样虽然 可以保证每个请求都能请求到,但是请求时间就会边长。
有些时候,我们也不一定要求请求一定等待,可以设置最大等待队列大小,如果超过就不等待了。这样虽然有些请求是失败的,但是请求时间会虽短。
<!‐‐最大等待数为100‐‐>
<Executor name="tomcatThreadPool" namePrefix="catalina‐exec‐" maxThreads="500" minSpareThreads="100" prestartminSpareThreads="true" maxQueueSize="100"/>
4.1.4 设置nio2的运行模式
tomcat的运行模式有3种:
- bio 默认的模式,性能非常低下,没有经过任何优化处理和支持.
- nio nio(new I/O),是Java SE 1.4及后续版本提供的一种新的I/O操作方式(即java.nio包及其子包)。Java nio是一个基于缓冲区、并能提供非阻塞I/O操作的Java API,因此nio 也被看成是non-blocking I/O的缩写。它拥有比传统I/O操作(bio)更好的并发运行性能。
- apr 安装起来最困难,但是从操作系统级别来解决异步的IO问题,大幅度的提高性能.
推荐使用nio,不过,在tomcat8中有最新的nio2,速度更快,建议使用nio2. 设置nio2.
<Connector executor="tomcatThreadPool" port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol" connectionTimeout="20000" redirectPort="8443" />
理论上nio2的效果会优惠nio,多次执行,发现设定为nio2对于提升吞吐量效果不是很明显。大家可以根据自己的测试情况选择合适的io模型。
5.调整JVM参数进行优化
测试通过jvm参数进行优化,为了测试一致性,依然将最大线程数设置为500, 启用nio2运行模式。
5.1 设置并行垃圾回收器
cd /tomcat/bin/catalina.sh
#年轻代、老年代均使用并行收集器,初始堆内存64M,最大堆内存512M
JAVA_OPTS="-XX:+UseParallelGC -XX:+UseParallelOldGC -Xms64m -Xmx512m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:../logs/gc.log"
/JAVA_OPTS 查找 按n是下一个
5.2 查看GC日志文件
sz gc.log 到GCeasy去分析
5.3调整年轻代大小
vi catalina.sh
JAVA_OPTS="-XX:+UseParallelGC -XX:+UseParallelOldGC -Xms512m -Xmx512m -XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:../logs/gc.log"
5.5 设置G1垃圾收集器
理论上而言,设置为G1垃圾收集器,性能是会提升的。但是会受制于多方面的影响,也不一定绝对有提升
#设置使用G1垃圾收集器最大停顿时间100毫秒,初始堆内存512m,最大堆内存512m
JAVA_OPTS="-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -Xms512m -Xmx512m -XX:+PrintGCDetails - XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:../logs/gc.log"