今でもあなたは私の光丶

JVM优化(4)

GC日志分析

GC日志是一个很重要的工具,它准确记录了每一次的GC的执行时间和执行结果,通过分析GC日志可以优化堆设置 和GC设置,或者改进应用程序的对象分配模式。

GC日志参数

不同的垃圾收集器,输出的日志格式各不相同,但也有一些相同的特征。熟悉各个常用垃圾收集器的GC日志,是 进行JVM调优的必备一步。 解析GC日志,首先需要收集日志,常用的有以下JVM参数用来打印输出日志信息:

GC日志参数

例如,使用如下参数启动:

  • -Xms28m
  • -Xmx28m
    //开启记录GC日志详细信息(包括GC类型、各个操作使用的时间),并且在程序运行结束打印出JVM的内存占用情况
  • -XX:+PrintGCDetails
  • -XX:+PrintGCDateStamps
  • -XX:+UseGCLogFileRotation 开启滚动生成日志
  • -Xloggc:E:/logs/gc.log

常用垃圾收集器参数

GC日志分析

日志的含义

GC 日志理解起来十分简单,因为日志本来就是要给开发人员看的,所以设计的很直观。
举个例子,我们来分别说明各个部分所代表的含义:

[GC (Allocation Failure) [PSYoungGen: 6146K->904K(9216K)] 6146K->5008K(19456K), 0.0038730 secs] [Times: user=0.08 sys=0.00, real=0.00 secs]

将上面 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)

使用 ParNew + Serial Old 的组合进行内存回收

设置JVM参数

-Xms20M -Xmx20M -Xmn10M -XX:+UseParNewGC -XX:+PrintGCDetails -XX:SurvivorRatio=8

测试代码

public class TestGCLog01 {
private static final int _1MB = 1024*1024;
/**
* VM参数:
* 1. -Xms20M -Xmx20M -Xmn10M -XX:+UseParNewGC
-XX:+PrintGCDetails -XX:SurvivorRatio=8
*/
public static void testAllocation() {
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[2*_1MB];
allocation2 = new byte[2*_1MB];
allocation3 = new byte[2*_1MB];
allocation4 = new byte[4*_1MB]; //出现一次 Minor GC
}
}

打印结果

[GC (Allocation Failure) [ParNew: 6146K->753K(9216K), 0.0065877 secs] 6146K->4849K(19456K),
0.0092108 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
Heap
par new generation total 9216K, used 7220K [0x00000000fec00000, 0x00000000ff600000,
0x00000000ff600000)
eden space 8192K, 78% used [0x00000000fec00000, 0x00000000ff250ea0, 0x00000000ff400000)
from space 1024K, 73% used [0x00000000ff500000, 0x00000000ff5bc488, 0x00000000ff600000)
to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
tenured generation total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000,
0x0000000100000000)
the space 10240K, 40% used [0x00000000ff600000, 0x00000000ffa00020, 0x00000000ffa00200,
0x0000000100000000)
Metaspace used 3362K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 369K, capacity 388K, committed 512K, reserved 1048576K
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old
collector is deprecated and will likely be removed in a future release

结果分析

通过上面的GC日志我们可以看出一开始出现了 MinorGC, 引起GC的原因是 内存分配失败 ,因为分配allocation的时 候,Eden区已经没有足够的区域来分配了,所以发生来本次 MinorGC ,经过 MinorGC 之后新生代的已使用容量从 6146K->753K,然而整个堆的内存总量却几乎没有减少,原因就是,由于发现新生代没有可以回收的对象,所以不 得不使用内存担保将allocation1~3 三个对象提前转移到老年代。此时再在 Eden 区域为 allocation 分配 4MB 的空 间,因此最后我们发现 Eden 区域占用了 4MB,老年代占用了 6MB

使用 Parallel Scavenge + Parallel Old 的组合进行内存回收

设置参数:

-Xms20M -Xmx20M -Xmn10M -XX:+UseParallelGC -XX:+PrintGCDetails -XX:SurvivorRatio=8

测试代码

package com.lagou.unit5;
public class TestGCLog02 {
private static final int _1MB = 1024*1024;
/**
* VM参数:
* 2. -Xms20M -Xmx20M -Xmn10M -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+PrintGCDetails
-XX:SurvivorRatio=8
*/
public static void testAllocation() {
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[2*_1MB];
allocation2 = new byte[2*_1MB];
allocation3 = new byte[2*_1MB];
allocation4 = new byte[4*_1MB]; //出现一次 Minor GC
}
public static void main(String[] args) {
testAllocation();
}
}

执行结果

[GC (Allocation Failure) [PSYoungGen: 6146K->872K(9216K)] 6146K->4976K(19456K), 0.0138583 secs]
[Times: user=0.06 sys=0.00, real=0.01 secs]
Heap
PSYoungGen total 9216K, used 7339K [0x00000000ff600000, 0x0000000100000000,
0x0000000100000000)
eden space 8192K, 78% used [0x00000000ff600000,0x00000000ffc50e68,0x00000000ffe00000)
from space 1024K, 85% used [0x00000000ffe00000,0x00000000ffeda020,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 10240K, used 4104K [0x00000000fec00000, 0x00000000ff600000,
0x00000000ff600000)
object space 10240K, 40% used [0x00000000fec00000,0x00000000ff002020,0x00000000ff600000)
Metaspace used 3420K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 371K, capacity 388K, committed 512K, reserved 1048576K

结果分析

大对象回收分析

大对象直接进入老年代 虚拟机提供一个参数 -XX:PretenureSizeThreshold 用来设置直接在老年代分配的 对象的大小,如果对象大于这个值就会直接在老年代分配。这样做的目的是避免在 Eden 区及两个 Survivor 区之间发生大量的内存复制。

参数:

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+UseParNewGC -XX:+PrintGCDetails - XX:PretenureSizeThreshold=3145728

测试代码

package com.lagou.unit5;
public class TestGCLog03 {
private static final int _1MB = 1024 * 1024;
/**
* VM参数:(参数序号对应实验序号)
* -Xms20M -Xmx20M -Xmn10M -XX:+UseParNewGC -XX:+PrintGCDetails -
XX:PretenureSizeThreshold=3145728
*/
public static void testPreteureSizeThreshold() {
byte[] allocation;
allocation = new byte[4 * _1MB];
}
public static void main(String[] args) {
testPreteureSizeThreshold();
}
}

执行结果

Heap
par new generation total 9216K, used 2214K [0x00000000fec00000, 0x00000000ff600000,
0x00000000ff600000)
eden space 8192K, 27% used [0x00000000fec00000, 0x00000000fee29be8, 0x00000000ff400000)
from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
tenured generation total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000,
0x0000000100000000)
the space 10240K, 40% used [0x00000000ff600000, 0x00000000ffa00010, 0x00000000ffa00200,
0x0000000100000000)
Metaspace used 3430K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 373K, capacity 388K, committed 512K, reserved 1048576K

结果分析:
通过上面的堆的内存占用情况很容易看出我们分配的4MB大小的对象直接被放到了老年代

日志分析工具

日志工具简介

GC日志可视化分析工具GCeasy和GCviewer。通过GC日志可视化分析工具,我们可以很方便的看到JVM各个分代的内 存使用情况、垃圾回收次数、垃圾回收的原因、垃圾回收占用的时间、吞吐量等,这些指标在我们进行JVM调优的 时候是很有用的。

  • GCeasy是一款在线的GC日志分析器,可以通过GC日志分析进行内存泄露检测、GC暂停原因分析、JVM配置建 议优化等功能,而且是可以免费使用
    在线分析工具 https://gceasy.io/index.jsp
  • GCViewer是一款实用的GC日志分析软件,免费开源使用,你需要安装jdk或者java环境才可以使用。软件为GC 日志分析人员提供了强大的功能支持,有利于大大提高分析效率

测试准备

编写代码生成gc.log日志准备分析

package com.lagou.unit5;
import java.util.ArrayList;
public class TestGCLog04 {
private static final int _1MB = 1024 * 1024;
/**
* -Xms100M -Xmx100M -XX:SurvivorRatio=8 -XX:+PrintGCDetails -Xloggc:D://logs/gc.log
*/
public static void main(String[] args) {
ArrayList<byte[]> list = new ArrayList<byte[]>();
for (int i = 0; i < 500; i++) {
byte[] arr = new byte[1024 * 100];
list.add(arr);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

在d:/logs/gc.log 生成日志

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

GCeasy

这是一个在线分析日志的工具,主要功能是免费的,存在部分收费,地址:https://gceasy.io/
把上篇博客生成的日志文件,上传分析,就会接到可视化界面

jvm堆:

Allocated:各部分分配大小
Peak:峰值内存使用量

关键绩效指标:

吞吐量:93.769%,运行应用程序的时间/(GC时间的比值+运行应用程序的时间)

平均GC停顿时间
最大GC停顿时间
GC停顿持续时间范围:时间范围、GC数量、百分百

交互式图表:

左边菜单有很多:
GC之前的堆、GC之后的堆、GC持续时间、GC停顿持续时间、回收的内存字节、Young区内存变化、Old区内存变 化、Metaspace内存变化、分配对象大小、对象从Young到Old内存大小变化
后序的内容有:GC统计信息、Minor GC/Full GC信息、内存泄漏、GC的原因等等,所以这个工具的功能真的很强大
我们可以对比一下,Parallel、CMS、G1的GC效率

GCViewer

GCViewer是一个小工具,可以可视化展示 生成的详细GC输出。支持Sun / Oracle,IBM,HP和BEA的Java虚拟机。它 是GNU LGPL下发布的免费软件。

下载:https://sourceforge.net/projects/gcviewer/

使用简介:

java -jar gcviewer-1.37-SNAPSHOT.jar

打开之后,点击File->Open File打开我们的GC日志,可以看到如下图,图标是可以放大缩小的,主要内容就是红线 圈住的部分,里面的内容跟上面的GCeasy的比较类似,具体的可以看下GitHub中的描述。

JVM调优实战

tomcat与JVM调优

tomcat服务器在JavaEE项目中使用率非常高,所以在生产环境对tomcat的优化也变得非常重要了。 对于tomcat的 优化,主要是从2个方面入手,一是,tomcat自身的配置,另一个是 tomcat所运行的jvm虚拟机的调优。

安装tomcat部署项目

下载并安装tomcat

官网

配置tomcat8

启动解压tomcat并配置

tar -xvf apache-tomcat-8.5.59.tar.gz
cd apache-tomcat-8.5.59/conf/
#修改配置文件,配置tomcat管理用户
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"/>
:wq 保存退出

配置可以访问Server Status

#如果是tomcat7,配置了tomcat用户就可以登录了,但是tomcat8不行,需要修改一个配置文件,否则访问会报403
vim webapps/manager/META-INF/context.xml
<Context antiResourceLocking="false" privileged="true" >
<CookieProcessor className="org.apache.tomcat.util.http.Rfc6265CookieProcessor"
sameSiteCookies="strict" />
<!-- <Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" /> -->
<Manager sessionAttributeValueClassNameFilter="java\.lang\.
(?:Boolean|Integer|Long|Number|String)|org\.apache\.catalina\.filters\.CsrfPreventionFilter\$LruCache(?:\$1)?|java\.util\.(?:Linked)?HashMap"/>
</Context>
:wq 保存退出

开始访问tomcat

部署web项目

将lg-visualization.war 部署到tomcat中

数据库连接192.168.80.100 节点下的库

访问项目

使用Apache Jmeter进行测试

将web应用部署到服务器之后,使用jmeter对其进行压力测试。
Apache Jmeter是开源的压力测试工具,我们借助于此工具进行测试,将测试出tomcat 的吞吐量等信息。
下载地址:

下载安装jmeter

使用步骤

设置中文:

设置外观主题

添加线程组,使用线程模拟用户的并发

设置1000个线程,每个线程循环10次,也 就是tomcat会接收到10000个请求。

添加Http请求

添加监控

查看结果:

平均响应时间,单位毫秒: 1845
错误率: 0.18%
吞吐量:459.5/sec

调整tomcat参数进行优化

优化吞吐量

禁用AJP服务

什么是AJP呢? AJP(Apache JServer Protocol)是定向包协议 。WEB服务器和Servlet容器通过TCP连接来交互;为 了节省SOCKET创建的昂贵代价,WEB服务器会尝试维护一个永久TCP连接到servlet容器,并且在多个请求和响应周 期过程会重用连接。

Tomcat在 server.xml 中配置了两种连接器。

  1. 第一个连接器监听8080端口,负责建立HTTP连接。在通过浏览器访问Tomcat服务器的Web应用时,使用的就 是这个连接器。
  2. 第二个连接器监听8009端口,负责和其他的HTTP服务器建立连接。在把Tomcat与其他HTTP服务器集成时, 就需要用到这个连接器。AJP连接器可以通过AJP协议和一个web容器进行交互

Nginx+tomcat的架构,所以用不着AJP协议,所以把AJP连接器禁用。修改conf下的server.xml文件,将AJP服务禁用 掉即可。

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

重启tomcat,查看效果。 可以看到AJP服务以及不存在了。

设置执行器(线程池)

频繁地创建线程会造成性能浪费,所以使用线程池来优化:
在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,最大的等待队列数,超过则拒绝请求
-->
<!-- A "Connector" represents an endpoint by which requests are received
and responses are returned. Documentation at :
Java HTTP Connector: /docs/config/http.html
Java AJP Connector: /docs/config/ajp.html
APR (HTTP/AJP) Connector: /docs/apr.html
Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
-->
<Connector port="8080" executor="tomcatThreadPool" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- A "Connector" using the shared thread pool-->

在页面中显示最大线程数为-1,这个是正常的,仅仅是显示的问题,实际使用的指定的值
通过设置线程池,调整线程池相关的参数进行测试tomcat的性能。
设置:最大线程数为500,初始为50

<Executor name="tomcatThreadPool" namePrefix="catalina‐exec‐" maxThreads="500"
minSpareThreads="50" prestartminSpareThreads="true"/>

吞吐量为702次/秒,性能有所提升。

设置最大等待队列

默认情况下,请求发送到tomcat,如果tomcat正忙,那么该请求会一直等待。这样虽然 可以保证每个请求都能请求 到,但是请求时间就会边长。
有些时候,我们也不一定要求请求一定等待,可以设置最大等待队列大小,如果超过就不等待了。这样虽然有些请 求是失败的,但是请求时间会虽短。

<!‐‐最大等待数为100‐‐>
<Executor name="tomcatThreadPool" namePrefix="catalina‐exec‐" maxThreads="500"
minSpareThreads="100"
prestartminSpareThreads="true" maxQueueSize="100"/>

思考是不是线程数设置的越高越好

测试1:设置线程数如下

maxThreads="500" minSpareThreads="50"

测试2:设置线程数如下

maxThreads="2000" minSpareThreads="100"

测试3:设置线程数如下

maxThreads="5000" minSpareThreads="500"

结论:并不是设置线程数越多吞吐量越好。因此需要根据我们具体的场景进行响应的调试,选取一个较优的 参数。

设置nio2的运行模式

tomcat的运行模式有3种:

  1. bio 默认的模式,性能非常低下,没有经过任何优化处理和支持.
  2. 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)更好的并发运行性能。
  3. apr 安装起来最困难,但是从操作系统级别来解决异步的IO问题,大幅度的提高性能. 推荐使用nio,不过,在 tomcat8中有最新的nio2,速度更快,建议使用nio2. 设置nio2:
<Connector executor="tomcatThreadPool" port="8080"
protocol="org.apache.coyote.http11.Http11Nio2Protocol"
connectionTimeout="20000" redirectPort="8443" />

tomcat8之前的版本用的是BIO,推荐使用NIO,tomcat8中有最新的NIO2,速度更快,建议使用NIO2。

将最大线程设置为500进行测试:

<Executor name="tomcatThreadPool" namePrefix="catalina‐exec‐"
maxThreads="500" minSpareThreads="100" prestartminSpareThreads="true" maxQueueSize="100"/>
<!‐‐ 设置nio2 ‐‐>
<Connector executor="tomcatThreadPool" port="8080"
protocol="org.apache.coyote.http11.Http11Nio2Protocol"
connectionTimeout="20000"
redirectPort="8443" />

查看结果

理论上nio2的效果会优惠nio,多次执行,发现设定为nio2对于提升吞吐量效果不是很明显。大家可以根据自己的测试 情况选择合适的io模型。

调整JVM参数进行优化

测试通过jvm参数进行优化,为了测试一致性,依然将最大线程数设置为500, 启用nio2运行模式。

设置并行垃圾回收器

#年轻代、老年代均使用并行收集器,初始堆内存64M,最大堆内存512M
JAVA_OPTS="-XX:+UseParallelGC -XX:+UseParallelOldGC -Xms64m -Xmx512m -XX:+PrintGCDetails -
XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:../logs/gc.log"

展示测试结果:

查看gc日志文件 将gc.log文件上传到gceasy.io查看gc中是否存在问题。

vi catalina.sh:

将jvm参数添加进去--设置年轻代和老年代的垃圾收集器均为ParallelGC并行垃圾收集器。不设置jdk8默认也是使用 ParallelGC:

修改完catalina.sh文件后,重启tomcat:

sh startup.sh && tail -f ../logs/catalina.out

gc日志存放目录: apache-tomcat-8.5.34/logs/gc.log

测试结果比较接近,因为jdk8默认使用的也是ParallelGC垃圾收集器。

查看GC日志文件

将apache-tomcat-8.5.34/logs/gc.log 文件上传到gceasy.io查看gc中是否存在问题:

问题一: 年轻代和老年代空间大小分配不合理, 具体如下图

问题二: 0-100事件范围内执行MinorGC 太多

从图中可以看到0-100 100-200毫秒的gc 发生了9次和4次, 时间短,频率高,说明年轻代空间分配不合理,我们可以尝试 多给年轻代分配空间,减少Minor GC 频率, 降低Pause GC事件,提高吞吐量.

问题三:下图中我们也能看到问题, Minor GC 发生了 14 次, Full GC 发生了2次。 Pause time 事件也较长。

调整年轻代大小

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

从测试结果来看,吞吐量以及响应时间均有提升。

查看GC日志

效果:吞吐量保持在97%以上,同时Minor GC次数明显减少,停顿次数减少

设置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"

vi cataliina.sh文件:

由上图可以看到吞吐量上升, GC执行次数降低. 由上图可以看到吞吐量上升, GC执行次数降低. 由上图可以看到吞吐量上升, GC执行次数降低.