200字
Tomcat性能优化
2026-03-18
2026-03-18

Tomcat 性能优化

在目前流行的互联网架构中,Tomcat 在目前的网络编程中是举足轻重的,由于Tomcat的运行依赖于JVM,从虚拟机的角度把Tomcat的调整分为外部环境调优 JVM 和 Tomcat 自身调优两部分

JVM 组成

[root@t1 ~]#java -version
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)

JVM 组成

图片-JrKlsHZjVXBCgUphcRXLRfXMghMrwjfk.png

JVM 组成部分

  • 类加载子系统: 使用Java语言编写.java Source Code文件,通过javac编译成.class Byte Code文件。class loader类加载器将所需所有类加载到内存,必要时将类实例化成实例
  • 运行时数据区: 最消耗内存的空间,需要优化
  • 执行引擎: 包括JIT (JustInTimeCompiler)即时编译器, GC垃圾回收器
  • 本地方法接口: 将本地方法栈通过JNI(Java Native Interface)调用Native Method Libraries, 比如:C,C++库等,扩展Java功能,融合不同的编程语言为Java所用

JVM运行时数据区域由下面部分构成:

  • **Method Area (线程共享):**方法区是所有线程共享的内存空间,存放已加载的类信息(构造方法,接口定义),常量(final),静态变量(static), 运行时常量池等。但实例变量存放在堆内存中. 从JDK8开始此空间由永久代改名为元空间 metaspace
  • **heap (线程共享):**堆在虚拟机启动时创建,存放创建的所有对象信息。如果对象无法申请到可用内存将抛出OOM异常.堆是靠GC垃圾回收器管理的,通过-Xmx -Xms 指定最大堆和最小堆空间大小
  • **Java stack (线程私有):**Java栈是每个线程会分配一个栈,存放java中8大基本数据类型,对象引用实例的本地变量,方法参数和返回值等,基于FILO()(First In Last Out),每个方法为一个栈帧
  • **Program Counter Register (线程私有):**PC寄存器就是一个指针,指向方法区中的方法字节码,每一个线程用于记录当前线程正在执行的字节码指令地址。由执行引擎读取下一条指令.因为线程需要切换,当一个线程被切换回来需要执行的时候,知道执行到哪里了
  • **Native Method stack (线程私有):**本地方法栈为本地方法执行构建的内存空间,存放本地方法执行时的局部变量、操作数等。

所谓本地方法,使用native 关健字修饰的方法,比如:Thread.sleep方法. 简单的说是非Java实现的方法,例如操作系统的C编写的库提供的本地方法,Java调用这些本地方法接口执行。但是要注意,本地方法应该避免直接编程使用,因为Java可能跨平台使用,如果用了Windows API,换到了Linux平台部署就有了问题

虚拟机

目前Oracle官方使用的是HotSpot, 它最早由一家名为"Longview Technologies"公司设计,使用了很多优秀的设计理念和出色的性能,1997年该公司被SUN公司收购。后来随着JDK一起发布了HotSpotVM。目前HotSpot是最主要的 JVM。

安卓程序需要运行在JVM上,而安卓平台使用了Google自研的Java虚拟机——Dalvid,适合于内存、处理器能力有限系统。

GC (Garbage Collection) 垃圾收集器

在堆内存中如果创建的对象不再使用,仍占用着内存,此时即为垃圾.需要即使进行垃圾回收,从而释放内存空间给其它对象使用

其实不同的开发语言都有垃圾回收问题,C,C++需要程序员人为回收,造成开发难度大,容易出错等问题,但执行效率高,而JAVA和Python中不需要程序员进行人为的回收垃圾,而由JVM或相关程序自动回收垃圾,减轻程序员的开发难度,但可能会造成执行效率低下

堆内存里面经常创建、销毁对象,内存也是被使用、被释放。如果不妥善处理,一个使用频繁的进程,可能会出现虽然有足够的内存容量,但是无法分配出可用内存空间,因为没有连续成片的内存了,内存全是碎片化的空间。

所以需要有合适的垃圾回收机制,确保正常释放不再使用的内存空间,还需要保证内存空间尽可能的保持一定的连续

对于垃圾回收,需要解决三个问题

  • 哪些是垃圾要回收
  • 怎么回收垃圾
  • 什么时候回收垃圾

Garbage 垃圾确定方法

  • 引用计数: 每一个堆内对象上都与一个私有引用计数器,记录着被引用的次数,引用计数清零,该对象所占用堆内存就可以被回收。循环引用的对象都无法将引用计数归零,就无法清除。Python中即使用此种方式

  • 根搜索(可达)算法 Root Searching

图片-XqRWWnGdmMbMbrktmQUpInVEWLvVqyvx.png

垃圾回收基本算法

标记-清除 Mark-Sweep

分垃圾标记阶段和内存释放两个阶段。

  • 标记阶段,找到所有可访问对象打个标记。清理阶段,遍历整个堆
  • 对未标记对象(即不再使用的对象)逐一进行清理。

图片-lMRAEpMyCVkYnLawkAoNqMkErDfsCbfS.png

特点:

优点:算法简单

缺点:标记-清除最大的问题会造成内存碎片,但是不浪费空间,效率较高(如果对象较多时,逐一删除效率也会受到影响)

标记-压缩 (压实)Mark-Compact

分垃圾标记阶段和内存整理两个阶段。

  • 标记阶段,找到所有可访问对象打个标记。
  • 内存清理阶段时,整理时将对象向内存一端移动,整理后存活对象连续的集中在内存一端。

图片-KtDQBsOMIXIJMDMKqyhAQeAoEwkZJgpZ.png

特点:

标记-压缩算法好处是整理后内存空间连续分配,有大段的连续内存可分配,没有内存碎片。

缺点是内存整理过程有消耗**,效率相对低下**

复制 Copying

图片-QjEpRTmLjfyqmiFItPyCXEpmQGxYiDpF.png

先将可用内存分为大小相同两块区域A和B,每次只用其中一块,比如A。当A用完后,则将A中存活的对象复制到B。复制到B的时候连续的使用内存,最后将A一次性清除干净。

特点

好处是没有碎片,复制过程中保证对象使用连续空间,且一次性清除所有垃圾,所以即使对象很多,收回效率也很高

缺点是比较浪费内存,只能使用原来一半内存,因为内存对半划分了,复制过程毕竟也是有代价。

多种算法总结

没有最好的算法,在不同场景选择最合适的算法

  • 效率: 复制算法>标记清除算法> 标记压缩算法

  • 内存整齐度: 复制算法=标记压缩算法> 标记清除算法

  • 内存利用率: 标记压缩算法=标记清除算法>复制算法

STW

对于大多数垃圾回收算法而言,GC线程工作时,停止所有工作的线程,称为Stop The World。GC 完成时,恢复其他工作线程运行。这也是JVM运行中最头疼的问题。

分代堆内存GC策略

上述垃圾回收算法都有优缺点,能不能对不同数据进行区分管理,不同分区对数据实施不同回收策略,分而治之。

堆内存分代

将heap内存空间分为三个不同类别: 年轻代、老年代、持久代

图片-MnihePQBSoBCwPLpICMnFolhDZfRckhA.png

Heap堆内存分为

  • **年轻代Young:**Young Generation
    • 伊甸园区eden: 只有一个,刚刚创建的对象
    • **幸存(存活)区Servivor Space:**有2个幸存区,一个是from区,一个是to区。大小相等、地位相同、可互换。
      • from 指的是本次复制数据的源区
      • to 指的是本次复制数据的目标区
  • **老年代Tenured:**Old Generation, 长时间存活的对象

默认空间大小比例:

默认JVM试图分配最大内存的总内存的1/4,初始化默认总内存为总内存的1/64,年青代中heap的1/3,老年代占2/3

图片-uPlnpflpjaLPnqCLFpHlcucpzhYnXDXV.png

**永久代:**JDK1.7之前使用, 即Method Area方法区,保存JVM自身的类和方法,存储JAVA运行时的环境信息,JDK1.8后 改名为 MetaSpace,此空间不存在垃圾回收,关闭JVM会释放此区域内存,此空间物理上不属于heap内存,但逻辑上存在于heap内存

  • 永久代必须指定大小限制,字符串常量JDK1.7存放在永久代,1.8后存放在heap中
  • MetaSpace 可以设置,也可不设置,无上限

规律: 一般情况99%的对象都是临时对象

范例: 在tomcat 状态页可以看到以下的内存分代

图片-VirFOUtWggTqnMSznhpBckAABCwLZVeZ.png

范例: 查看JVM内存分配情况

[root@centos8 ~]#cat Heap.java
public class Heap {
    public static void main(String[] args){
        //返回JVM试图使用的最大内存,字节单位
        long max = Runtime.getRuntime().maxMemory();
        //返回JVM初始化总内存
        long total = Runtime.getRuntime().totalMemory();
        
        System.out.println("max="+max+"字节\t"+(max/(double)1024/1024)+"MB");
        System.out.println("total="+total+"字节\t"+(total/(double)1024/1024)+"MB");
    }
}

[root@centos8 ~]#javac Heap.java
[root@centos8 ~]#java -classpath . Heap
ax=243269632字节 232.0MB
total=16252928字节 15.5MB

#JDK-8的执行结果
[root@centos8 ~]#java -XX:+PrintGCDetails Heap
max=243269632字节 232.0MB
total=16252928字节 15.5MB
Heap
def new generation total 4928K, used 530K [0x00000000f1000000,
0x00000000f1550000, 0x00000000f6000000)
eden space 4416K, 12% used [0x00000000f1000000, 0x00000000f1084a60,
0x00000000f1450000)
from space 512K, 0% used [0x00000000f1450000, 0x00000000f1450000,
0x00000000f14d0000)
to space 512K, 0% used [0x00000000f14d0000, 0x00000000f14d0000,
0x00000000f1550000)
tenured generation total 10944K, used 0K [0x00000000f6000000,
0x00000000f6ab0000, 0x0000000100000000)
the space 10944K, 0% used [0x00000000f6000000, 0x00000000f6000000,
0x00000000f6000200, 0x00000000f6ab0000)
Metaspace used 2525K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 269K, capacity 386K, committed 512K, reserved 1048576K

[root@centos8 ~]#echo "scale=2;(4928+10944)/1024" |bc
15.50
#说明年轻代+老年代占用了所有heap空间, Metaspace实际不占heap空间,逻辑上存在于Heap

#JDK-11的执行结果
[root@ubuntu2204 ~]#java -version
java version "11.0.19" 2023-04-18 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.19+9-LTS-224)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.19+9-LTS-224, mixed mode)
[root@ubuntu2204 ~]#java -XX:+PrintGCDetails Heap
[0.002s][warning][gc] -XX:+PrintGCDetails is deprecated. Will use -Xlog:gc*
instead.
[0.012s][info ][gc,heap] Heap region size: 1M
[0.027s][info ][gc ] Using G1
[0.027s][info ][gc,heap,coops] Heap address: 0x00000000e1a00000, size: 486 MB,
Compressed Oops mode: 32-bit
max=509607936字节 486.0MB
total=33554432字节 32.0MB
[0.331s][info ][gc,heap,exit ] Heap
[0.332s][info ][gc,heap,exit ] garbage-first heap total 32768K, used 1024K
[0x00000000e1a00000, 0x0000000100000000)
[0.332s][info ][gc,heap,exit ] region size 1024K, 2 young (2048K), 0
survivors (0K)
[0.332s][info ][gc,heap,exit ] Metaspace used 4823K, capacity 4894K,
committed 5120K, reserved 1056768K
[0.332s][info ][gc,heap,exit ] class space used 406K, capacity 424K,
committed 512K, reserved 1048576K

年轻代回收 Minor GC

  1. 起始时,所有新建对象(特大对象直接进入老年代)都出生在eden,当eden满了,启动GC。这个称为Young GC 或者 Minor GC。
  2. 标记eden存活对象,然后将存活对象复制到s0(假设本次是s0,也可以是s1,它们可以调换),eden剩余所有空间都清空。GC完成。
  3. 继续新建对象,当eden再次满了,启动GC
  4. 先同时标记eden和s0中存活对象,然后将存活对象复制到s1。将eden和s0清空,此次GC完成
  5. 继续新建对象,当eden满了,启动GC
  6. 标记eden和s1中存活对象,然后将存活对象复制到s0。将eden和s1清空,此次GC完成

以后就重复上面的步骤。

通常场景下,大多数对象都不会存活很久,而且创建活动非常多,新生代就需要频繁垃圾回收。

但是,如果一个对象一直存活,它最后就在from、to来回复制,如果from区中对象复制次数达到阈值(默认15次,CMS为6次,可通过java的选项 -XX:MaxTenuringThreshold=N 指定),就直接复制到老年代。

老年代回收 Major GC

进入老年代的数据较少,所以老年代区被占满的速度较慢,所以垃圾回收也不频繁。

如果老年代也满了,会触发老年代GC,称为Old GC或者 Major GC

由于老年代对象一般来说存活次数较长,所以较常采用标记-压缩算法。

当老年代满时,会触发 Full GC,即对所有"代"的内存进行垃圾回收

Minor GC比较频繁,Major GC较少。但一般Major GC时,由于老年代对象也可以引用新生代对象,所以先进行一次Minor GC,然后在Major GC会提高效率。可以认为回收老年代的时候完成了一次FullGC。

所以可以认为 MajorGC = FullGC

GC 触发条件

图片-ceTkLWWvTdpJPNyVyBCNonLGbcApqfnr.png

**Minor GC 触发条件:**当eden区满了触发

Full GC 触发条件:

  • 老年代满了
  • System.gc()手动调用。不推荐

年轻代:

  • 存活时长低
  • 适合复制算法

老年代:

  • 区域大,存活时长高
  • 适合标记压缩算法

Minor GC 可能会引起短暂的STW暂停。当进行 Minor GC 时,为了确保安全性,JVM 需要在某些特定的点上暂停所有应用程序的线程,以便更新一些关键的数据结构。这些暂停通常是非常短暂的,通常在毫秒级别,并且很少对应用程序的性能产生显著影响。

Major GC的暂停时间通常会比Minor GC的暂停时间更长,因为老年代的容量通常比年轻代大得多。这意味着在收集和整理大量内存时,需要更多的时间来完成垃圾收集操作。

尽管Major GC会引起较长的STW暂停,但JVM通常会尽量优化垃圾收集器的性能,以减少这些暂停对应用程序的影响。例如,通过使用并行或并发垃圾收集算法,可以减少STW时间,并允许一部分垃圾收集工作与应用程序的线程并发执行

Java 内存调整相关参数

JVM 内存常用相关参数

Java 命令行参考文档: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

图片-bphXAUFOTWKwAoMsRyOGsGNhWdjpLTIV.png

帮助:man java

选项分类

  • -选项名称 此为标准选项,所有HotSpot都支持
  • -X选项名称 此为稳定的非标准选项
  • -XX:选项名称 非标准的不稳定选项,下一个版本可能会取消
参数说明举例
-Xms设置应用程序初始使用的堆内存大小(年轻代 + 老年代)-Xms2g
-Xmx设置应用程序能获得的最大堆内存。早期 JVM 不建议超过 32G,否则内存管理效率下降-Xmx4g
-XX:NewSize设置初始新生代大小-XX:NewSize=128m
-XX:MaxNewSize设置最大新生代内存空间-XX:MaxNewSize=256m
-Xmn同时设置 -XX:NewSize-XX:MaxNewSize,代替两者-Xmn1g
-XX:NewRatio以比例方式设置新生代和老年代-XX:NewRatio=2new:old=1:2
-XX:SurvivorRatio以比例方式设置 Eden 和 Survivor (S0 或 S1)-XX:SurvivorRatio=6Eden:S0:S1=6:1:1
-Xss设置每个线程私有的栈空间大小,依据具体线程大小和数量-Xss256k

范例: 查看java的选项帮助

查看java命令标准选项
[root@centos ~]#java
Usage: java [-options] class [args...]
(to execute a class)
or java [-options] -jar jarfile [args...]
(to execute a jar file)
where options include:
-d32 use a 32-bit data model if available
-d64 use a 64-bit data model if available
-server to select the "server" VM
The default VM is server.
-cp <class search path of directories and zip/jar files>
-classpath <class search path of directories and zip/jar files>
A : separated list of directories, JAR archives,
and ZIP archives to search for class files.
-D<name>=<value>
set a system property
-verbose:[class|gc|jni]
enable verbose output
-version print product version and exit
-version:<value>
Warning: this feature is deprecated and will be removed
in a future release.
require the specified version to run
-showversion print product version and continue
-jre-restrict-search | -no-jre-restrict-search
Warning: this feature is deprecated and will be removed
in a future release.
include/exclude user private JREs in the version search
-? -help print this help message
-X print help on non-standard options #非标准选项
-ea[:<packagename>...|:<classname>]
-enableassertions[:<packagename>...|:<classname>]
enable assertions with specified granularity
-da[:<packagename>...|:<classname>]
-disableassertions[:<packagename>...|:<classname>]
disable assertions with specified granularity
-esa | -enablesystemassertions
enable system assertions
-dsa | -disablesystemassertions
disable system assertions
-agentlib:<libname>[=<options>]
load native agent library <libname>, e.g. -agentlib:hprof
see also, -agentlib:jdwp=help and -agentlib:hprof=help
-agentpath:<pathname>[=<options>]
load native agent library by full pathname
-javaagent:<jarpath>[=<options>]
load Java programming language agent, see java.lang.instrument
-splash:<imagepath>
show splash screen with specified image
See http://www.oracle.com/technetwork/java/javase/documentation/index.html for
more details.
[root@t1 ~]#

#查看java的非标准选项
[root@centos8 ~]#java -X
-Xmixed mixed mode execution (default)
-Xint interpreted mode execution only
-Xbootclasspath:<directories and zip/jar files separated by :>
set search path for bootstrap classes and resources
-Xbootclasspath/a:<directories and zip/jar files separated by :>
append to end of bootstrap class path
-Xbootclasspath/p:<directories and zip/jar files separated by :>
prepend in front of bootstrap class path
-Xdiag show additional diagnostic messages
-Xnoclassgc disable class garbage collection
-Xincgc enable incremental garbage collection
-Xloggc:<file> log GC status to a file with time stamps
-Xbatch disable background compilation
-Xms<size> set initial Java heap size
-Xmx<size> set maximum Java heap size
-Xss<size> set java thread stack size
-Xprof output cpu profiling data
-Xfuture enable strictest checks, anticipating future default
-Xrs reduce use of OS signals by Java/VM (see documentation)
-Xcheck:jni perform additional checks for JNI functions
-Xshare:off do not attempt to use shared class data
-Xshare:auto use shared class data if possible (default)
-Xshare:on require using shared class data, otherwise fail.
-XshowSettings show all settings and continue
-XshowSettings:all
show all settings and continue
-XshowSettings:vm show all vm related settings and continue
-XshowSettings:properties
show all property settings and continue
-XshowSettings:locale
show all locale related settings and continue
The -X options are non-standard and subject to change without notice.

#查看所有不稳定选项的当前生效值
[root@centos8 ~]#java -XX:+PrintFlagsFinal
[Global flags]
intx ActiveProcessorCount = -1
{product}
uintx AdaptiveSizeDecrementScaleFactor = 4
{product}
uintx AdaptiveSizeMajorGCDecayTimeScale = 10
{product}
uintx AdaptiveSizePausePolicy = 0
{product}
uintx AdaptiveSizePolicyCollectionCostMargin = 50
{product}
uintx AdaptiveSizePolicyInitializingSteps = 20
{product}
uintx AdaptiveSizePolicyOutputInterval = 0
{product}
uintx AdaptiveSizePolicyWeight = 10
{product}
uintx AdaptiveSizeThroughPutPolicy = 0
{product}
.......

#查看所有不稳定选项的默认值
[root@centos8 ~]#java -XX:+PrintFlagsInitial
[Global flags]
intx ActiveProcessorCount = -1
{product}
uintx AdaptiveSizeDecrementScaleFactor = 4
{product}
uintx AdaptiveSizeMajorGCDecayTimeScale = 10
{product}
uintx AdaptiveSizePausePolicy = 0
{product}
uintx AdaptiveSizePolicyCollectionCostMargin = 50
{product}
uintx AdaptiveSizePolicyInitializingSteps = 20
{product}
.......

#查看当前命令行的使用的选项设置
[root@centos8 ~]#java -XX:+PrintCommandLineFlags
-XX:InitialHeapSize=15598528 -XX:MaxHeapSize=249576448 -
XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
-XX:+UseParallelGC
#上面的-XX:+UseParallelGC 说明当前使用Parallel Scavenge + Parallel Old

范例: 查看和指定 JVM 内存分配

#默认JVM试图分配最大内存的总内存的1/4,初始化默认总内存为总内存的1/64
[root@centos8 ~]#cat Heap.java
public class Heap {
    public static void main(String[] args){
        //返回JVM试图使用的最大内存,字节单位
        long max = Runtime.getRuntime().maxMemory();
        //返回JVM初始化总内存
        long total = Runtime.getRuntime().totalMemory();
        System.out.println("max="+max+"字节\t"+(max/(double)1024/1024)+"MB");
        System.out.println("total="+total+"字节\t"+(total/(double)1024/1024)+"MB");
    }
}

#编译生成class文件
[root@centos8 ~]#javac Heap.java

#通过$CLASSPATH指定类文件路径,否则无法找到类,也可以通过 java -cp /path指定类路径
[root@centos8 ~]#echo $CLASSPATH
/usr/local/jdk/lib/:/usr/local/jdk/jre/lib/

[root@centos8 ~]#cp Heap.class /usr/local/jdk/lib

#查看当前内存默认值
[root@centos8 ~]#java -XX:+PrintGCDetails Heap

max=243269632字节 232.0MB
total=16252928字节 15.5MB
Heap
def new generation total 4928K, used 530K [0x00000000f1000000,
0x00000000f1550000, 0x00000000f6000000)
eden space 4416K, 12% used [0x00000000f1000000, 0x00000000f1084a60,
0x00000000f1450000)
from space 512K, 0% used [0x00000000f1450000, 0x00000000f1450000,
0x00000000f14d0000)
to space 512K, 0% used [0x00000000f14d0000, 0x00000000f14d0000,
0x00000000f1550000)
tenured generation total 10944K, used 0K [0x00000000f6000000,
0x00000000f6ab0000, 0x0000000100000000)
the space 10944K, 0% used [0x00000000f6000000, 0x00000000f6000000,
0x00000000f6000200, 0x00000000f6ab0000)
Metaspace used 2525K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 269K, capacity 386K, committed 512K, reserved 1048576K

#指定内存空间
[root@centos8 ~]#java -Xms1024m -Xmx1024m -XX:+PrintGCDetails Heap
max=1037959168字节 989.875MB
total=1037959168字节 989.875MB
Heap
def new generation total 314560K, used 11185K [0x00000000c0000000,
0x00000000d5550000, 0x00000000d5550000)
eden space 279616K, 4% used [0x00000000c0000000, 0x00000000c0aec408,
0x00000000d1110000)
from space 34944K, 0% used [0x00000000d1110000, 0x00000000d1110000,
0x00000000d3330000)
to space 34944K, 0% used [0x00000000d3330000, 0x00000000d3330000,
0x00000000d5550000)
tenured generation total 699072K, used 0K [0x00000000d5550000,
0x0000000100000000, 0x0000000100000000)
the space 699072K, 0% used [0x00000000d5550000, 0x00000000d5550000,
0x00000000d5550200, 0x0000000100000000)
Metaspace used 2525K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 269K, capacity 386K, committed 512K, reserved 1048576K

#以下计算结果和max一样,说明Metaspace逻辑存在,但物理上并不属于heap空间
[root@centos8 ~]#echo '(314560+699072)*1024'|bc
1037959168

范例: 查看 OOM

[root@centos8 ~]#cat HeapOom2.java
import java. util. Random;
    public class HeapOom2 {
    public static void main(String[] args) {
    String str = "I am ayaka";
    while (true){
        str += str + new Random().nextInt(88888888); //生成0到88888888之间的随机数字
        }
    }
}

[root@centos8 ~]#javac -cp . HeapOom2.java


[root@centos8 ~]#java -Xms100m -Xmx100m -XX:+PrintGCDetails -cp . HeapOom2
[GC (Allocation Failure) [DefNew: 27213K->2940K(30720K), 0.0026366 secs] 27213K-
>5624K(99008K), 0.0027124 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 24949K->0K(30720K), 0.0044922 secs] 27633K-
>8307K(99008K), 0.0045959 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [DefNew: 16550K->0K(30720K), 0.0037276 secs] 24857K-
>19041K(99008K), 0.0037898 secs] [Times: user=0.00 sys=0.01, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 21468K->0K(30720K), 0.0111687 secs] 40509K-
>40509K(99008K), 0.0112437 secs] [Times: user=0.00 sys=0.01, real=0.01 secs]
[GC (Allocation Failure) [DefNew: 21951K->0K(30720K), 0.0084048 secs] 62460K-
>61977K(99008K), 0.0084641 secs] [Times: user=0.00 sys=0.01, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 21468K->21468K(30720K), 0.0000177 secs]
[Tenured: 61977K->35141K(68288K), 0.0068683 secs] 83445K->35141K(99008K),
[Metaspace: 2479K->2479K(1056768K)], 0.0069358 secs] [Times: user=0.01 sys=0.00,
real=0.01 secs]
[Full GC (Allocation Failure) [Tenured: 35141K->32444K(68288K), 0.0091498 secs]
35141K->32444K(99008K), [Metaspace: 2479K->2479K(1056768K)], 0.0091975 secs]
[Times: user=0.01 sys=0.00, real=0.01 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at
java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.jav
a:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674)
at java.lang.StringBuilder.append(StringBuilder.java:208)
at HeapOom2.main(HeapOom2.java:6)
Heap
def new generation total 30720K, used 1048K [0x00000000f9c00000,
0x00000000fbd50000, 0x00000000fbd50000)
eden space 27328K, 3% used [0x00000000f9c00000, 0x00000000f9d06158,
0x00000000fb6b0000)
from space 3392K, 0% used [0x00000000fba00000, 0x00000000fba00000,
0x00000000fbd50000)
to space 3392K, 0% used [0x00000000fb6b0000, 0x00000000fb6b0000,
0x00000000fba00000)
tenured generation total 68288K, used 32444K [0x00000000fbd50000,
0x0000000100000000, 0x0000000100000000)
the space 68288K, 47% used [0x00000000fbd50000, 0x00000000fdcff248,
0x00000000fdcff400, 0x0000000100000000)
Metaspace used 2510K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 268K, capacity 386K, committed 512K, reserved 1048576K

JDK 工具监控使用情况

注意: jvisualvm 从 jdk-8u361版本后被取消

案例1: jvisualvm 工具

范例: 指定参数运行Java程序

#java -cp . -Xms512m -Xmx1g HelloWorld
#测试用java程序
javac HelloWorld.java
java -classpath . -Xms512m -Xmx1g HelloWorld

[root@tomcat ~]#cat HelloWorld.java
public class HelloWorld extends Thread {
    public static void main(String[] args) {
        try {
        	while (true) {
                    Thread.sleep(2000);
                    System.out.println("hello ayaka);
        	}
        } catch (InterruptedException e) {
        	e.printStackTrace();
        }
    }
}

#编译为字节码.class文件
[root@tomcat ~]#javac HelloWorld.java
[root@tomcat ~]#ll HelloWorld.class
-rw-r--r-- 1 root root 590 Jul 13 16:31 HelloWorld.class
[root@tomcat ~]#file HelloWorld.class
HelloWorld.class: compiled Java class data, version 52.0 (Java 1.8)

#指定路径运行class文件
[root@tomcat ~]#java -cp . -Xms512m -Xmx1g HelloWorld
hello ayaka
hello ayaka
hello ayaka

#或者用下面方法指定CLASS文件的搜索路径
[root@tomcat ~]#echo $CLASSPATH
/usr/local/jdk/lib/:/usr/local/jdk/jre/lib/
[root@tomcat ~]#mv HelloWorld.class /usr/local/jdk/lib/

#分别执行多次观察
[root@tomcat ~]#java HelloWorld
[root@tomcat ~]#java -Xms256m -Xmx512m HelloWorld
[root@tomcat ~]#java -Xms128m -Xmx512m -XX:NewSize=64m -XX:MaxNewSize=200m
HelloWorld
hello ayaka

[root@tomcat ~]#jps
21299 Main
21418 Jps
21407 HelloWorld

#将Linux的图形工具显示到windows桌面
#方法1
#注意:先在windows上开启Xwindows Server,如Xmanager
[root@tomcat ~]#export DISPLAY=10.0.0.1:0.0
#方法2:使用 MobaXterm 连接
[root@tomcat ~]#yum -y install xorg-x11-xauth xorg-x11-fonts-* xorg-x11-font-utils xorg-x11-fonts-Type1

#运行图形工具
[roottomcat ~]#which jvisualvm
/usr/local/jdk/bin/jvisualvm
[root@tomcat ~]#jvisualvm
案例2: 使用 jvisualvm的 Visual GC 插件

范例: 使用 jvisualvm的 Visual GC 插件 观察 java程序的OOM

[root@centos8 ~]#cat HeapOom.java
import java.util.ArrayList;
import java.util.List;

public class HeapOom {
    public static void main(String[] args) {
        List<byte[]> list =new ArrayList<byte[]>();
        int i = 0;
        boolean flag = true;
        while(flag){
            try{
                i++;
                list.add(new byte[1024* 1024]);//每次增加一个1M大小的数组对象
                Thread.sleep(1000);
            }catch(Throwable e){
                e.printStackTrace();
                flag =false;
                System.out.println("count="+i);//记录运行的次数
            }
        }
    }
}

安装VirtualGC插件

运行上面的OOM程序,重新运行jvisualm, 可以看到多了一下VisualGC页标

[root@centos8 ~]#java -Xms100m -Xmx200m HeapOom

当OOM时退出JAVA程序时

[root@centos8 ~]#java HeapOom
java.lang.OutOfMemoryError: Java heap space
at HeapOom.main(HeapOom.java:12)
count=229
Jprofiler定位OOM的问题原因

JProfiler是一款功能强大的Java开发分析工具,它可以快速的帮助用户分析出存在的错误,软件还可对需要的显示类进行标记,包括了内存的分配情况和信息的视图等

JProfiler官网:http://www.ej-technologies.com/products/jprofiler/overview.html

范例: 安装jprofiler工具定位OOM原因和源码问题的位置

#安装OpenJDK
[root@centos8 ~]#dnf -y install java-1.8.0-openjdk-devel

[root@centos8 ~]#cat HeapOom.java
import java.util.ArrayList;
import java.util.List;

public class HeapOom {
    public static void main(String[] args) {
        List<byte[]> list =new ArrayList<byte[]>();
        int i = 0;
        boolean flag = true;
        while(flag){
            try{
                i++;
                list.add(new byte[1024* 1024]);//每次增加一个1M大小的数组对象
                Thread.sleep(1000);
            }catch(Throwable e){
                e.printStackTrace();
                flag =false;
                System.out.println("count="+i);//记录运行的次数
            }
        }
    }
}

[root@centos8 ~]#javac HeapOom.java
[root@centos8 ~]#java -cp . -Xms5m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError HeapOom
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid96271.hprof ...
Heap dump file created [8134070 bytes in 0.031 secs]
java.lang.OutOfMemoryError: Java heap space
at HeapOom.main(HeapOom.java:12)
count=8

[root@centos8 ~]#cat HeapOom2.java
import java. util. Random;
public class HeapOom2 {
    public static void main(String[] args) {
        String str = "I am lao wang";
        while (true){
        str += str + new Random().nextInt(88888888);
        }
    }
}

[root@centos8 ~]#javac HeapOom2.java
[root@centos8 ~]#java -cp . -Xms5m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError HeapOom2
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid96339.hprof ...
Heap dump file created [4925877 bytes in 0.016 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at
java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.jav
a:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674)
at java.lang.StringBuilder.append(StringBuilder.java:208)
at HeapOom2.main(HeapOom2.java:6)

下载并安装Jprofiler

下载并安装JProfiler(傻瓜式安装略)后,双击打开前面生成的两个文件java_pid96271.hprof和ava_pid96339,可以看到下面显示,从中分析OOM原因

Tomcat的JVM参数设置

默认不指定,-Xmx大约使用了1/4的内存,当前本机内存指定约为1G。

在bin/catalina.sh中增加一行

......
# OS specific support. $var _must_ be set to either true or false.
#添加下面一行
JAVA_OPTS="-server -Xms128m -Xmx512m -XX:NewSize=48m -XX:MaxNewSize=200m"

cygwin=false
darwin=false
........
-server:VM运行在server模式,为在服务器端最大化程序运行速度而优化
-client:VM运行在Client模式,为客户端环境减少启动时间而优化

重启 Tomcat 观察

[root@tomcat ~]#ps aux|grep tomcat
tomcat 22194 5.1 15.1 2497008 123480 ? Sl 13:31 0:03
/usr/local/jdk/jre/bin/java -
Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -
Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -server -
Xmx512m -Xms128m -XX:NewSize=48m -XX:MaxNewSize=200m -
Djdk.tls.ephemeralDHKeySize=2048 -
Djava.protocol.handler.pkgs=org.apache.catalina.webresources -
Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -
Dignore.endorsed.dirs= -classpath
/usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar -
Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -
Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap
start

浏览器访问server status页面,可以看到页面

垃圾收集方式

工作模式不同:指的是GC线程和工作线程是否一起运行

  • 独占垃圾回收器:只有GC在工作,STW 一直进行到回收完毕,工作线程才能继续执行
  • 并发垃圾回收器:让GC线程垃圾回收某些阶段可以和工作线程一起进行,如:标记阶段并行,回收阶段仍然串行

回收线程数:指的是GC线程是否串行或并行执行

  • 串行垃圾回收器:一个GC线程完成回收工作
  • 并行垃圾回收器:多个GC线程同时一起完成回收工作,充分利用CPU资源

JVM原理详解插图5

调整策略

对JVM调整策略应用极广

  • 在WEB领域中Tomcat等
  • 在大数据领域Hadoop生态各组件
  • 在消息中间件领域的Kafka等
  • 在搜索引擎领域的ElasticSearch、Solr等

注意: 在不同领域和场景对JVM需要不同的调整策略

  • 减少 STW 时长,串行变并行
  • 减少 GC 次数,要分配合适的内存大小

一般情况下,大概可以使用以下原则:

  • 客户端或较小程序,内存使用量不大,可以使用串行回收
  • 对于服务端大型计算,可以使用并行回收
  • 大型WEB应用,用户端不愿意等待,尽量少的STW,可以使用并发回收

垃圾回收器

常用垃圾回收器

图片-OryiwUNkSKUcfUidhlDzclOADBCWPxLl.png

按分代设置不同垃圾回收器

新生代

  • 新生代串行收集器Serial:单线程、独占式串行,采用复制算法,简单高效但会造成STW

  • 新生代并行回收收集器PS(Parallel Scavenge):多线程并行、独占式,会产生STW, 使用复制算法关注调整吞吐量,此收集器关注点是达到一个可控制的吞吐量

    • 吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾收集时间),比如虚拟机总共运行100分钟,其中垃圾回收花掉1分钟,那吞吐量就是99%。

    • 高吞吐量可以高效率利用CPU时间,尽快完成运算任务,主要适合在后台运算而不需要太多交互的任务。

    • 除此之外,Parallel Scavenge 收集器具有自适应调节策略,它可以将内存管理的调优任务交给虚拟机去完成。自适应调节策略也是Parallel Scavenge与 ParNew 收集器的一个重要区别。

      此为默认的新生代的垃圾回收器

      和ParNew不同,PS不可以和老年代的CMS组合

  • 新生代并行收集器ParNew:就是Serial 收集器的多线程版,将单线程的串行收集器变成了多线程并行、独占式,使用复制算法,相当于PS的改进版

    • 经常和CMS配合使用,关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,适合需要与用户交互的程序,良好的响应速度能提升用户体验

老年代:

  • 老年代串行收集器Serial Old:Serial Old是Serial收集器的老年代版本,单线程、独占式串行,回收算法使用标记压缩
  • 老年代并行回收收集器Parallel Old:多线程、独占式并行,回收算法使用标记压缩,关注调整吞吐量
    • Parallel Old收集器是Parallel Scavenge 收集器的老年代版本,这个收集器是JDK1.6之后才开始提供,从HotSpot虚拟机的垃圾收集器的图中也可以看出,Parallel Scavenge 收集器无法与CMS收集器配合工作,因为一个是为了吞吐量,一个是为了客户体验(也就是暂停时间的缩短)
    • 此为默认的新老年代的垃圾回收器
  • CMS (Concurrent Mark Sweep并发标记清除算法) 收集器
    • 在某些阶段尽量使用和工作线程一起运行,减少STW时长(200ms以内), 提升响应速度,是互联网服务端BS系统上较佳的回收算法
    • 分为4个阶段:初始标记、并发标记、重新标记、并发清除,在初始标记、重新标记时需要STW。
    • 初始标记:此过程需要STW(Stop The Word),只标记一下GC Roots能直接关联到的对象,速度很快。
    • 并发标记:就是GC Roots进行扫描可达链的过程,为了找出哪些对象需要收集。这个过程远远慢于初始标记,但它是和用户线程一起运行的,不会出现STW,所有用户并不会感受到。
    • 重新标记:为了修正在并发标记期间,用户线程产生的垃圾,这个过程会比初始标记时间稍微长一点,但是也很快,和初始标记一样会产生STW。
    • 并发清理:在重新标记之后,对现有的垃圾进行清理,和并发标记一样也是和用户线程一起运行的,耗时较长(和初始标记比的话),不会出现STW。
    • 由于整个过程中,耗时最长的并发标记和并发清理都是与用户线程一起执行的,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
以下收集器不再按明确的分代单独设置
  • G1(Garbage First)收集器
    • 是最新垃圾回收器,从JDK1.6实验性提供,JDK1.7发布,其设计目标是在多处理器、大内存服务器端提供优于CMS收集器的吞吐量和停顿控制的回收器。JDK9将G1设为默认的收集器,建议 JDK9版本以后使用。
    • 基于标记压缩算法,不会产生大量的空间碎片,有利于程序的长期执行。
    • 分为4个阶段:初始标记、并发标记、最终标记、筛选回收。并发标记并发执行,其它阶段STW只有GC线程并行执行。
    • G1收集器是面向服务端的收集器,它的思想就是首先回收尽可能多的垃圾(这也是Garbage-First名字的由来)
    • G1能充分的利用多CPU,多核环境下的硬件优势,使用多个CPU来缩短STW停顿的时间(10ms以内)。
    • 可预测的停顿:这是G1相对于CMS的另一大优势,G1和CMS一样都是关注于降低停顿时间,但是G1能够让使用者明确的指定在一个M毫秒的时间片段内,消耗在垃圾收集的时间不得超过N毫秒。
    • 通过此选项指定: +UseG1GC
  • ZGC收集器: 减少STW时长(1ms以内), 可以PK C++的效率,目前实验阶段
  • Shenandoah收集器: 和ZGC竞争关系,目前实验阶段
  • Epsilon收集器: 调试 JDK 使用,内部使用,不用于生产环境

JVM 1.8 默认的垃圾回收器:PS + ParallelOld,所以大多数都是针对此进行调优

垃圾收集器设置

优化调整Java 相关参数的目标: 尽量减少FullGC和STW

通过以下选项可以单独指定新生代、老年代的垃圾收集器

  • -server 指定为Server模式,也是默认值,一般使用此工作模式
  • -XX:+UseSerialGC
    • 运行在Client模式下,新生代是Serial, 老年代使用SerialOld
  • XX:+UseParNewGC
    • 新生代使用ParNew,老年代使用SerialOld
  • -XX:+UseParallelGC
    • 运行于server模式下,新生代使用Parallel Scavenge, 老年代使用 Parallel Old,此为JVM8默认值,关注吞吐量
  • XX:+UseParallelOldGC
    • 新生代使用Paralell Scavenge, 老年代使用Paralell Old,和上面-XX:+UseParallelGC 相同
    • -XX:ParallelGCThreads=N,在关注吞吐量的场景使用此选项增加并行线程数
  • -XX:+UseConcMarkSweepGC
    • 新生代使用ParNew, 老年代优先使用CMS,备选方式为Serial Old
    • 响应时间要短,停顿短使用这个垃圾收集器
    • -XX:CMSInitiatingOccupancyFraction=N,N为0-100整数表示达到老年代的大小的百分比多少触发回收
      • 默认68
    • -XX:+UseCMSCompactAtFullCollection 开启此值,在CMS收集后,进行内存碎片整理
    • -XX:CMSFullGCsBeforeCompaction=N 设定多少次CMS后,进行一次内存碎片整理
    • -XX:+CMSParallelRemarkEnabled 降低标记停顿

范例:

-XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5
  • -XX:+UseG1GC
    • 使用GC1的垃圾器器,是JVM11的默认值

范例: JVM11 的默认的垃圾回收器

[root@ubuntu2204 ~]#java -version
java version "11.0.17" 2022-10-18 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.17+10-LTS-269)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.17+10-LTS-269, mixed mode)
[root@ubuntu2204 ~]#java -XX:+PrintCommandLineFlags 2> /dev/null
-XX:G1ConcRefinementThreads=2 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=31811840 -XX:MaxHeapSize=508989440 -XX:+PrintCommandLineFlags
-XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC

范例: JVM8 的默认的垃圾回收器

[root@ubuntu2204 ~]#java -version
java version "1.8.0_351"
Java(TM) SE Runtime Environment (build 1.8.0_351-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.351-b10, mixed mode)
[root@ubuntu2204 ~]#java -XX:+PrintCommandLineFlags 2> /dev/null
-XX:InitialHeapSize=31811904 -XX:MaxHeapSize=508990464 -
XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
-XX:+UseParallelGC

范例:查看默认垃圾回收器

#Linux
[root@rocky8 ~]#java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=31964352 -XX:MaxHeapSize=511429632 -
XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
-XX:+UseParallelGC
java version "1.8.0_301"
Java(TM) SE Runtime Environment (build 1.8.0_301-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.301-b09, mixed mode)

#Windows
C:\Users\wang>java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=1071317952 -XX:MaxHeapSize=17141087232 -
XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
-XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version "1.8.0_281"
Java(TM) SE Runtime Environment (build 1.8.0_281-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.281-b09, mixed mode)

范例:查看垃圾回收器

#-XX:+UseSerialGC表示Serial+SerialOld
[root@rocky8 ~]#java -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseSerialGC -cp . HeapOom2
[GC (Allocation Failure) [DefNew: 2376K->320K(3072K), 0.0004583 secs] 2376K-
>657K(9920K), 0.0004984 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 2374K->0K(3072K), 0.0003771 secs] 2711K-
>1257K(9920K), 0.0003866 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 2447K->0K(3072K), 0.0004045 secs] 3704K-
>2857K(9920K), 0.0004243 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 1643K->0K(3072K), 0.0001604 secs] 4500K-
>4457K(9920K), 0.0001758 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 1600K->0K(3072K), 0.0002896 secs][Tenured:
6057K->2856K(6848K), 0.0009171 secs] 6057K->2856K(9920K), [Metaspace: 2480K-
>2480K(1056768K)], 0.0012276 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 1647K->1647K(3072K), 0.0000053 secs][Tenured:
6056K->2056K(6848K), 0.0006369 secs] 7703K->2056K(9920K), [Metaspace: 2480K-
>2480K(1056768K)], 0.0006801 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [DefNew: 1600K->1600K(3072K), 0.0000057 secs][Tenured:
5256K->5256K(6848K), 0.0005282 secs] 6856K->5256K(9920K), [Metaspace: 2480K-
>2480K(1056768K)], 0.0005636 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [Tenured: 5256K->5042K(6848K), 0.0008440 secs]
5256K->5042K(9920K), [Metaspace: 2480K->2480K(1056768K)], 0.0008589 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at
java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.jav
a:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674)
at java.lang.StringBuilder.append(StringBuilder.java:208)
at HeapOom2.main(HeapOom2.java:6)
Heap
def new generation total 3072K, used 105K [0x00000000ff600000,
0x00000000ff950000, 0x00000000ff950000)
eden space 2752K, 3% used [0x00000000ff600000, 0x00000000ff61a5c8,
0x00000000ff8b0000)
from space 320K, 0% used [0x00000000ff900000, 0x00000000ff900000,
0x00000000ff950000)
to space 320K, 0% used [0x00000000ff8b0000, 0x00000000ff8b0000,
0x00000000ff900000)
tenured generation total 6848K, used 5042K [0x00000000ff950000,
0x0000000100000000, 0x0000000100000000)
the space 6848K, 73% used [0x00000000ff950000, 0x00000000ffe3cac8,
0x00000000ffe3cc00, 0x0000000100000000)
Metaspace used 2514K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 268K, capacity 386K, committed 512K, reserved 1048576K

#-XX:+UseParNewGC表示ParNew+SerialOld
[root@rocky8 ~]#java -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseParNewGC -cp . HeapOom2
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 (Allocation Failure) [ParNew: 2373K->265K(3072K), 0.0008588 secs] 2373K-
>664K(9920K), 0.0008885 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [ParNew: 2316K->99K(3072K), 0.0007617 secs] 2716K-
>1559K(9920K), 0.0007802 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 2543K->24K(3072K), 0.0006585 secs] 4002K-
>3082K(9920K), 0.0006758 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 1665K->6K(3072K), 0.0004363 secs] 4722K-
>4660K(9920K), 0.0004525 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 1603K->2K(3072K), 0.0005977 secs][Tenured:
6252K->2652K(6848K), 0.0009806 secs] 6258K->2652K(9920K), [Metaspace: 2480K-
>2480K(1056768K)], 0.0015996 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 1645K->1645K(3072K), 0.0000045 secs][Tenured:
5847K->1853K(6848K), 0.0006388 secs] 7492K->1853K(9920K), [Metaspace: 2480K-
>2480K(1056768K)], 0.0006629 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 1597K->0K(3072K), 0.0004854 secs][Tenured:
5048K->5048K(6848K), 0.0006832 secs] 6646K->5048K(9920K), [Metaspace: 2480K-
>2480K(1056768K)], 0.0011950 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [Tenured: 5048K->5035K(6848K), 0.0006889 secs]
5048K->5035K(9920K), [Metaspace: 2480K->2480K(1056768K)], 0.0007030 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at
java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.jav
a:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674)
at java.lang.StringBuilder.append(StringBuilder.java:208)
at HeapOom2.main(HeapOom2.java:6)
Heap
par new generation total 3072K, used 105K [0x00000000ff600000,
0x00000000ff950000, 0x00000000ff950000)
eden space 2752K, 3% used [0x00000000ff600000, 0x00000000ff61a5c8,
0x00000000ff8b0000)
from space 320K, 0% used [0x00000000ff8b0000, 0x00000000ff8b0000,
0x00000000ff900000)
to space 320K, 0% used [0x00000000ff900000, 0x00000000ff900000,
0x00000000ff950000)
tenured generation total 6848K, used 5035K [0x00000000ff950000,
0x0000000100000000, 0x0000000100000000)
the space 6848K, 73% used [0x00000000ff950000, 0x00000000ffe3af00,
0x00000000ffe3b000, 0x0000000100000000)
Metaspace used 2514K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 268K, capacity 386K, committed 512K, reserved 1048576K


#-XX:+UseParallelGC表示PS+ParOld 此为默认值
[root@rocky8 ~]#java -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseParallelGC -cp . HeapOom2
[GC (Allocation Failure) [PSYoungGen: 1953K->491K(2560K)] 1953K->755K(9728K),
0.0009754 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2512K->492K(2560K)] 2775K->755K(9728K),
0.0005462 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2512K->96K(2560K)] 4359K->2735K(9728K),
0.0006484 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 888K->96K(2560K)] 5111K->4319K(9728K),
0.0004700 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 96K->96K(2560K)] 4319K->4319K(9728K),
0.0004402 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 96K->0K(2560K)] [ParOldGen: 4223K-
>2632K(7168K)] 4319K->2632K(9728K), [Metaspace: 2480K->2480K(1056768K)],
0.0020238 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) --[PSYoungGen: 1624K->1624K(2560K)] 7424K-
>7424K(9728K), 0.0004027 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 1624K->0K(2560K)] [ParOldGen: 5800K-
>1839K(7168K)] 7424K->1839K(9728K), [Metaspace: 2480K->2480K(1056768K)],
0.0018894 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 1584K->0K(1536K)] 6591K->5007K(8704K),
0.0004222 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] 5007K->5007K(9216K),
0.0003842 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] [ParOldGen: 5007K-
>5007K(7168K)] 5007K->5007K(9216K), [Metaspace: 2480K->2480K(1056768K)],
0.0016387 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] 5007K->5007K(9216K),
0.0003614 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] [ParOldGen: 5007K-
>4994K(7168K)] 5007K->4994K(9216K), [Metaspace: 2480K->2480K(1056768K)],
0.0017426 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at
java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.jav
a:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674)
at java.lang.StringBuilder.append(StringBuilder.java:208)
at HeapOom2.main(HeapOom2.java:6)
Heap
PSYoungGen total 2048K, used 41K [0x00000000ffd00000, 0x0000000100000000,
0x0000000100000000)
eden space 1024K, 4% used
[0x00000000ffd00000,0x00000000ffd0a518,0x00000000ffe00000)
from space 1024K, 0% used
[0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
to space 1024K, 0% used
[0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
ParOldGen total 7168K, used 4994K [0x00000000ff600000, 0x00000000ffd00000,
0x00000000ffd00000)
object space 7168K, 69% used
[0x00000000ff600000,0x00000000ffae0a98,0x00000000ffd00000)
Metaspace used 2514K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 268K, capacity 386K, committed 512K, reserved 1048576K
#-XX:+UseParallelOldGC表示PS+ParOld
[root@rocky8 ~]#java -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseParallelOldGC
-cp . HeapOom2
[GC (Allocation Failure) [PSYoungGen: 1933K->486K(2560K)] 1933K->748K(9728K),
0.0009954 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2482K->486K(2560K)] 2744K->748K(9728K),
0.0005175 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2482K->96K(2560K)] 4308K->2703K(9728K),
0.0006469 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 878K->96K(2560K)] 5049K->4267K(9728K),
0.0005057 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 96K->96K(2560K)] 4267K->4267K(9728K),
0.0004297 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 96K->0K(2560K)] [ParOldGen: 4171K-
>2602K(7168K)] 4267K->2602K(9728K), [Metaspace: 2480K->2480K(1056768K)],
0.0020167 secs] [Times: user=0.00 sys=0.01, real=0.00 secs]
[GC (Allocation Failure) --[PSYoungGen: 1604K->1604K(2560K)] 7334K-
>7334K(9728K), 0.0004226 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 1604K->0K(2560K)] [ParOldGen: 5729K-
>1819K(7168K)] 7334K->1819K(9728K), [Metaspace: 2480K->2480K(1056768K)],
0.0018280 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 1563K->0K(1536K)] 6511K->4947K(8704K),
0.0004604 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] 4947K->4947K(9216K),
0.0003834 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] [ParOldGen: 4947K-
>4947K(7168K)] 4947K->4947K(9216K), [Metaspace: 2480K->2480K(1056768K)],
0.0012999 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] 4947K->4947K(9216K),
0.0003402 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] [ParOldGen: 4947K-
>4934K(7168K)] 4947K->4934K(9216K), [Metaspace: 2480K->2480K(1056768K)],
0.0018222 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at
java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.jav
a:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674)
at java.lang.StringBuilder.append(StringBuilder.java:208)
at HeapOom2.main(HeapOom2.java:6)
Heap
PSYoungGen total 2048K, used 41K [0x00000000ffd00000, 0x0000000100000000,
0x0000000100000000)
eden space 1024K, 4% used
[0x00000000ffd00000,0x00000000ffd0a518,0x00000000ffe00000)
from space 1024K, 0% used
[0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
to space 1024K, 0% used
[0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
ParOldGen total 7168K, used 4934K [0x00000000ff600000, 0x00000000ffd00000,
0x00000000ffd00000)
object space 7168K, 68% used
[0x00000000ff600000,0x00000000ffad1a08,0x00000000ffd00000)
Metaspace used 2514K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 268K, capacity 386K, committed 512K, reserved 1048576K

#-XX:+UseConcMarkSweepGC表示ParNew+CMS
[root@rocky8 ~]#java -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC -cp . HeapOom2
[GC (Allocation Failure) [ParNew: 2365K->273K(3072K), 0.0004709 secs] 2365K-
>674K(9920K), 0.0005774 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 2324K->100K(3072K), 0.0008058 secs] 2726K-
>1563K(9920K), 0.0008263 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 2543K->25K(3072K), 0.0004260 secs] 4006K-
>3085K(9920K), 0.0004505 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 1665K->6K(3072K), 0.0004862 secs] 4726K-
>4664K(9920K), 0.0005126 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (CMS Initial Mark) [1 CMS-initial-mark: 4657K(6848K)] 6261K(9920K),
0.0001252 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-mark-start]
[CMS-concurrent-mark: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00
secs]
[CMS-concurrent-preclean-start]
[CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00,
real=0.00 secs]
[GC (Allocation Failure) [ParNew: 1603K->2K(3072K), 0.0005197 secs][CMS
(concurrent mode failure): 6255K->2655K(6848K), 0.0010552 secs] 6261K-
>2655K(9920K), [Metaspace: 2480K->2480K(1056768K)], 0.0016009 secs] [Times:
user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 1644K->1644K(3072K), 0.0000147 secs][CMS:
5850K->1857K(6848K), 0.0007565 secs] 7495K->1857K(9920K), [Metaspace: 2480K-
>2480K(1056768K)], 0.0007937 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 1597K->0K(3072K), 0.0001449 secs][CMS: 5052K-
>5052K(6848K), 0.0007624 secs] 6649K->5052K(9920K), [Metaspace: 2480K-
>2480K(1056768K)], 0.0009314 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [CMS: 5052K->5039K(6848K), 0.0007938 secs] 5052K-
>5039K(9920K), [Metaspace: 2480K->2480K(1056768K)], 0.0008031 secs] [Times:
user=0.00 sys=0.00, real=0.00 secs]
[GC (CMS Initial Mark) [1 CMS-initial-mark: 5039K(6848K)] 5039K(9920K),
0.0001806 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-mark-start]
[CMS-concurrent-mark: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00
secs]
[CMS-concurrent-preclean-start]
[CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00,
real=0.00 secs]
[GC (CMS Final Remark) [YG occupancy: 50 K (3072 K)][Rescan (parallel) ,
0.0001153 secs][weak refs processing, 0.0000026 secs][class unloading, 0.0000639
secs][scrub symbol table, 0.0001370 secs][scrub string table, 0.0000589 secs][1
CMS-remark: 5039K(6848K)] 5089K(9920K), 0.0004434 secs] [Times: user=0.00
sys=0.00, real=0.00 secs]
[CMS-concurrent-sweep-start]
[CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00
secs]
[CMS-concurrent-reset-start]
[CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00
secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at
java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.jav
a:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674)
at java.lang.StringBuilder.append(StringBuilder.java:208)
at HeapOom2.main(HeapOom2.java:6)
Heap
par new generation total 3072K, used 105K [0x00000000ff600000,
0x00000000ff950000, 0x00000000ff950000)
eden space 2752K, 3% used [0x00000000ff600000, 0x00000000ff61a5e0,
0x00000000ff8b0000)
from space 320K, 0% used [0x00000000ff8b0000, 0x00000000ff8b0000,
0x00000000ff900000)
to space 320K, 0% used [0x00000000ff900000, 0x00000000ff900000,
0x00000000ff950000)
concurrent mark-sweep generation total 6848K, used 245K [0x00000000ff950000,
0x0000000100000000, 0x0000000100000000)
Metaspace used 2514K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 268K, capacity 386K, committed 512K, reserved 1048576K

范例: 查看默认模式

[root@centos8 ~]#java |& grep '-server'
-server to select the "server" VM
The default VM is server.
[root@centos8 ~]#tail -n 2 /usr/local/jdk/jre/lib/amd64/jvm.cfg
-server KNOWN
-client IGNORE

范例: 指定垃圾回收设置

#将参数加入到bin/catalina.sh中,重启观察Tomcat status。老年代已经使用CMS
[root@tomcat ~]#vim /usr/local/tomcat/bin/catalina.sh
......
# OS specific support. $var _must_ be set to either true or false.
JAVA_OPTS="-server -Xmx512m -Xms128m -XX:NewSize=48m -XX:MaxNewSize=200m -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5"

cygwin=false
darwin=false
os400=false

.......
[root@tomcat ~]#systemctl restart tomcat
[root@tomcat ~]#ps aux |grep tomcat
tomcat 22093 2.2 15.2 3018616 148448 ? Sl 17:51 0:06
/usr/local/jdk/bin/java -
Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -
Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -server -
Xmx512m -Xms128m -XX:NewSize=48m -XX:MaxNewSize=200m -XX:+UseConcMarkSweepGC -
XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5 -
Djdk.tls.ephemeralDHKeySize=2048 -
Djava.protocol.handler.pkgs=org.apache.catalina.webresources -
Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -
Dignore.endorsed.dirs= -classpath
/usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar -
Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -
Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap
start
root 22158 0.0 0.1 12108 1084 pts/1 S+ 17:56 0:00 grep --
color=auto tomcat

开启垃圾回收输出统计信息,适用于调试环境的相关选项

  • -XX:+PrintGC 输出GC信息
  • -XX:+PrintGCDetails 输出GC详细信息
  • -XX:+PrintGCTimeStamps 与前两个组合使用,在信息上加上一个时间戳
  • -XX:+PrintHeapAtGC 生成GC前后椎栈的详细信息,日志会更大

注意: 以上适用调试环境,生产环境请移除这些参数,否则有非常多的日志输出

JAVA参数总结

参数名称含义默认值备注说明
-Xms初始堆大小物理内存的 1/64 (<1GB)默认情况下,当空余堆内存小于 40% 时,JVM 就会增大堆直到 -Xmx 的最大限制。可通过 MinHeapFreeRatio 调整。
-Xmx最大堆大小物理内存的 1/4 (<1GB)默认情况下,当空余堆内存大于 70% 时,JVM 会减少堆直到 -Xms 的最小限制。可通过 MaxHeapFreeRatio 调整。
-Xmn年轻代大小 (1.4 or later)注意: 此处大小是 eden + 2 survivor space。整个堆大小 = 年轻代大小 + 老年代大小 + 持久代大小。增大年轻代后,将减小老年代大小。Sun 官方推荐配置为整个堆的 3/8。
-XX:NewSize设置年轻代大小 (for 1.3/1.4)
-XX:MaxNewSize年轻代最大值 (for 1.3/1.4)
-XX:PermSize设置持久代 (perm gen) 初始值物理内存的 1/64仅适用于 JDK 8 之前的版本。
-XX:MaxPermSize设置持久代最大值物理内存的 1/4仅适用于 JDK 8 之前的版本。
-Xss每个线程的堆栈大小JDK 5.0 以后每个线程堆栈大小为 1M,以前为 256K。减小此值能生成更多线程,但操作系统对一个进程内的线程数有限制(经验值 3000~5000 左右)。如果栈不是很深,128k 够用,大应用建议使用 256k。对性能影响较大,需严格测试。
参数名称含义默认值/备注
-XX:ThreadStackSize线程栈大小 (Thread Stack Size)0 表示使用默认。Linux amd64 默认为 1024 KB;Sparc 为 512;Solaris x86 为 320。
-XX:NewRatio年轻代与老年代的比值(除去持久代)默认为 4,表示年轻代:老年代 = 1:4,年轻代占整个堆的 1/5。若设置了 -Xmn 则无需设置此项。
-XX:SurvivorRatioEden 区与 Survivor 区的大小比值默认为 8,表示 Eden:S0:S1 = 8:1:1。一个 Survivor 区占整个年轻代的 1/10。
-XX:LargePageSizeInBytes内存页的大小设置过大会影响 Perm(持久代)的大小。示例值为 128m。
-XX:+UseFastAccessorMethods原始类型的快速优化针对 primitive 类型的优化。
-XX:+DisableExplicitGC关闭 System.gc() 调用注意: 开启此项后,代码中的手动 GC 请求将被忽略。需严格测试。
-XX:MaxTenuringThreshold垃圾最大年龄(晋升阈值)如果设为 0,年轻代对象不经过 Survivor 区直接进入老年代。设置较大值则增加在年轻代存活时间,增加被回收概率。仅在串行 GC 时有效。
-XX:+AggressiveOpts加快编译启用一系列激进的编译器优化。
-XX:+UseBiasedLocking锁机制的性能改善启用偏向锁,减少无竞争情况下的同步开销。
-Xnoclassgc禁用类垃圾回收卸载类会带来额外开销,此项可禁用该功能。
-XX:SoftRefLRUPolicyMSPerMB每兆堆空闲空间中软引用的存活时间软引用对象在最后一次被引用后保留的时间。默认值为 1000(即 1 秒/MB)。
参数名称含义默认值备注说明
-XX:PretenureSizeThreshold对象超过多大时直接在旧生代分配0单位为字节。注意: 新生代采用 Parallel Scavenge 收集器时该参数无效。另一种直接在旧生代分配的情况是大的数组对象,且数组中无外部引用对象。
-XX:TLABWasteTargetPercentTLAB 占 Eden 区的百分比1%TLAB (Thread Local Allocation Buffer) 是线程本地分配缓冲区,用于提高对象分配效率。
-XX:+CollectGen0FirstFull GC 时是否首先进行 YGC (Young GC)false设置为 true 可以在 Full GC 之前先清理年轻代,有助于减少 Full GC 的工作量和停顿时间。

并行收集器相关参数

参数名称含义默认值备注说明
-XX:+UseParallelGCFull GC 采用 parallel MSC选择垃圾收集器为并行收集器。此配置仅对年轻代有效。在此配置下,年轻代使用并行收集,而老年代仍旧使用串行收集。
-XX:+UseParNewGC设置年轻代为并行收集可与 CMS 收集器同时使用。JDK 5.0 以上 JVM 会根据系统配置自行设置,通常无需再手动设置。
-XX:ParallelGCThreads并行收集器的线程数此值最好配置与处理器(CPU)核心数目相等。同样适用于 CMS。
-XX:+UseParallelOldGC年老代垃圾收集方式为并行收集 (Parallel Compacting)这是自 Java 6 开始出现的参数选项。
-XX:MaxGCPauseMillis每次年轻代垃圾回收的最长时间 (最大暂停时间目标)如果无法满足此时间,JVM 会自动调整年轻代大小以满足此值。
-XX:+UseAdaptiveSizePolicy自动选择年轻代区大小和相应的 Survivor 区比例开启后,并行收集器会自动调整各区比例,以达到最低响应时间或目标收集频率。建议使用并行收集器时一直打开。
-XX:GCTimeRatio设置垃圾回收时间占程序运行时间的百分比公式为 1 / (1 + n)
-XX:+ScavengeBeforeFullGCFull GC 前调用 YGCtrue在进行 Full GC 之前先跑一次年轻代回收,有助于减少 Full GC 的工作量和停顿时间。

CMS相关参数

参数名称含义默认值备注说明
-XX:+UseConcMarkSweepGC使用 CMS 内存收集测试中配置这个以后,-XX:NewRatio=4 的配置可能失效,所以此时年轻代大小最好用 -Xmn 设置。
-XX:+AggressiveHeap试图使用大量的物理内存长时间大内存使用的优化,能检查计算资源(内存,处理器数量)。至少需要 256MB 内存(在 1.4.1 4CPU 的机器上已显示有提升)。
-XX:CMSFullGCsBeforeCompaction多少次后进行内存压缩由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次 GC 以后对内存空间进行压缩、整理。
-XX:+CMSParallelRemarkEnabled降低标记停顿启用并行备注,减少在备注阶段(Remark)的暂停时间。
-XX:+UseCMSCompactAtFullCollection在 FULL GC 的时候,对年老代的压缩CMS 默认不会移动内存,容易产生碎片导致内存不够用。增加这个参数是个好习惯,可能会影响性能,但是可以消除碎片。
-XX:+UseCMSInitiatingOccupancyOnly使用手动定义初始化定义开始 CMS 收集禁止 HotSpot 自行根据启发式策略触发 CMS GC。
-XX:CMSInitiatingOccupancyFraction=70使用 CMS 作为垃圾回收,使用 70% 后开始 CMS 收集92设置老年代空间占用达到多少百分比时触发 CMS。调低此值可以预留更多空间给并发收集期间产生的对象。
参数名称含义默认值备注说明
-XX:+UseConcMarkSweepGC使用 CMS 内存收集开启此配置后,-XX:NewRatio=4 可能失效,建议显式使用 -Xmn 设置年轻代大小。
-XX:CMSInitiatingPermOccupancyFraction设置 Perm Gen 使用达到多少比率时触发92针对持久代(Perm Gen)的触发阈值。
-XX:+CMSIncrementalMode设置为增量模式用于单 CPU 情况。通过周期性地暂停 GC 线程,让出 CPU 给应用线程(现代多核机器已不推荐使用)。
-XX:+CMSClassUnloadingEnabled允许类卸载配合 CMS 使用,允许回收持久代中不再使用的类元数据。

辅助信息

参数说明输出形式举例
-XX:+PrintGC打印 GC 基本信息[GC 118250K->113543K(130112K), 0.0094143 secs]
-XX:+PrintGCDetails打印 GC 详细日志(包含各代内存变化)[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] ...]
-XX:+PrintGCTimeStamps打印 GC 发生的时间戳可与 PrintGC 混合使用,输出如 11.851: [GC ...]
-XX:+PrintGCApplicationStoppedTime打印垃圾回收期间程序暂停的时间Total time for which application threads were stopped: 0.0468229 seconds
-XX:+PrintGCApplicationConcurrentTime打印每次 GC 前程序未中断的执行时间Application time: 0.5291524 seconds
-XX:+PrintHeapAtGC打印 GC 前后详细的堆栈信息(详细的堆快照文本内容)
-Xloggc:filename把相关日志信息记录到文件以便分析与上述几个配合使用,保存到指定文件
参数 (Option)说明 (Description)输出示例/备注 (Output/Note)
-XX:+PrintGC打印 GC 的简要信息输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs] [Full GC 121376K->10414K(130112K), 0.0650971 secs]
-XX:+PrintClassHistogram在打印类直方图之前先进行垃圾回收Garbage collects before printing the histogram.
-XX:+PrintTLAB查看 TLAB(Thread Local Allocation Buffer)空间的使用情况查看 TLAB 空间的使用情况
-XX:+PrintTenuringDistribution查看每次 minor GC 后新的存活周期的阈值查看每次 minor GC 后新的存活周期的阈值

JVM相关工具

JVM 工具概述

$JAVA_HOME/bin下

命令说明
jps查看所有 JVM 进程
jinfo查看进程的运行环境参数,主要是 JVM 命令行参数
jstat对 JVM 应用程序的资源和性能进行实时监控
jstack查看所有线程的运行状态
jmap查看 JVM 占用物理内存的状态
jhat+UseParNew(注:通常用于分析堆转储文件,此处图片说明较简略)
jconsole图形工具
jvisualvm图形工具,JDK 8u361 版本后被取消

jps

JVM 进程状态工具

格式

jps:Java virutal machine Process Status tool,
jps [-q] [-mlvV] [<hostid>]
-q:静默模式;
-v:显示传递给jvm的命令行参数;
-m:输出传入main方法的参数;
-l:输出main类或jar完全限定名称;
-v:显示通过flag文件传递给jvm的参数;
[<hostid>]:主机id,默认为localhost;

范例:

#显示java进程
[root@tomcat ~]#jps
22357 Jps
21560 Main
21407 HelloWorld

#详细列出当前Java进程信息
[root@tomcat ~]#jps -l -v
21560 org.netbeans.Main -Djdk.home=/usr/local/jdk -
Dnetbeans.default_userdir_root=/root/.visualvm -
Dnetbeans.dirs=/usr/local/jdk/lib/visualvm/visualvm:/usr/local/jdk/lib/visualvm/
profiler: -Dnetbeans.home=/usr/local/jdk/lib/visualvm/platform -Xms24m -Xmx256m
-Dsun.jvmstat.perdata.syncWaitMs=10000 -Dsun.java2d.noddraw=true -
Dsun.java2d.d3d=false -Dnetbeans.keyring.no.master=true -
Dplugin.manager.install.global=false --add-exports=java.desktop/sun.awt=ALL-
UNNAMED --add-exports=jdk.jvmstat/sun.jvmstat.monitor.event=ALL-UNNAMED --add-
exports=jdk.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED --add-
exports=java.desktop/sun.swing=ALL-UNNAMED --add-
exports=jdk.attach/sun.tools.attach=ALL-UNNAMED --add-modules=java.activation -
XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -
XX:HeapDumpPath=/root/.visualvm/8u131/var/log/heapdump.hprof
22270 sun.tools.jps.Jps -
Denv.class.path=/usr/local/jdk/lib/:/usr/local/jdk/jre/lib/ -
Dapplication.home=/usr/local/jdk1.8.0_241 -Xms8m
21407 HelloWorld -Xms256m -Xmx512m

jinfo

输出给定的java进程的所有配置信息

格式

jinfo [option] <pid>
-flags:打印 VM flags
-sysprops:to print Java system properties
-flag <name>:to print the value of the named VM flag

范例:

#先获得一个java进程ID,然后jinfo
[root@tomcat ~]#jps
22357 Jps
21560 Main
21407 HelloWorld
[root@tomcat ~]#jinfo 21407
Attaching to process ID 21407, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.241-b07
Java System Properties:
java.runtime.name = Java(TM) SE Runtime Environment
java.vm.version = 25.241-b07
sun.boot.library.path = /usr/local/jdk1.8.0_241/jre/lib/amd64
java.vendor.url = http://java.oracle.com/
java.vm.vendor = Oracle Corporation
path.separator = :
java.rmi.server.randomIDs = true
file.encoding.pkg = sun.io
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
sun.os.patch.level = unknown
sun.java.launcher = SUN_STANDARD
user.country = US
user.dir = /data
java.vm.specification.name = Java Virtual Machine Specification
java.runtime.version = 1.8.0_241-b07
java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment
os.arch = amd64
java.endorsed.dirs = /usr/local/jdk1.8.0_241/jre/lib/endorsed
java.io.tmpdir = /tmp
line.separator =
java.vm.specification.vendor = Oracle Corporation
os.name = Linux
sun.jnu.encoding = UTF-8
java.library.path = /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
java.specification.name = Java Platform API Specification
java.class.version = 52.0
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
os.version = 4.18.0-147.el8.x86_64
user.home = /root
user.timezone = Asia/Shanghai
java.awt.printerjob = sun.print.PSPrinterJob
file.encoding = UTF-8
java.specification.version = 1.8
user.name = root
java.class.path = /usr/local/jdk/lib/:/usr/local/jdk/jre/lib/
java.vm.specification.version = 1.8
sun.arch.data.model = 64
sun.java.command = HelloWorld
java.home = /usr/local/jdk1.8.0_241/jre
user.language = en
java.specification.vendor = Oracle Corporation
awt.toolkit = sun.awt.X11.XToolkit
java.vm.info = mixed mode
java.version = 1.8.0_241
java.ext.dirs = /usr/local/jdk1.8.0_241/jre/lib/ext:/usr/java/packages/lib/ext
sun.boot.class.path =
/usr/local/jdk1.8.0_241/jre/lib/resources.jar:/usr/local/jdk1.8.0_241/jre/lib/rt
.jar:/usr/local/jdk1.8.0_241/jre/lib/sunrsasign.jar:/usr/local/jdk1.8.0_241/jre/
lib/jsse.jar:/usr/local/jdk1.8.0_241/jre/lib/jce.jar:/usr/local/jdk1.8.0_241/jre
/lib/charsets.jar:/usr/local/jdk1.8.0_241/jre/lib/jfr.jar:/usr/local/jdk1.8.0_24
1/jre/classes
java.vendor = Oracle Corporation
file.separator = /
java.vendor.url.bug = http://bugreport.sun.com/bugreport/
sun.io.unicode.encoding = UnicodeLittle
sun.cpu.endian = little
sun.cpu.isalist =
VM Flags:
Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=268435456 -
XX:MaxHeapSize=536870912 -XX:MaxNewSize=178913280 -XX:MinHeapDeltaBytes=196608 -
XX:NewSize=89456640 -XX:OldSize=178978816 -XX:+UseCompressedClassPointers -
XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps
Command line: -Xms256m -Xmx512m

[root@tomcat ~]#jinfo -flags 21407
Attaching to process ID 21407, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.241-b07
Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=268435456 -
XX:MaxHeapSize=536870912 -XX:MaxNewSize=178913280 -XX:MinHeapDeltaBytes=196608 -
XX:NewSize=89456640 -XX:OldSize=178978816 -XX:+UseCompressedClassPointers -
XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps
Command line: -Xms256m -Xmx512m

jstat

输出指定的java进程的统计信息

格式:

jstat -help|-options
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
[<interval> [<count>]]
interval:时间间隔,单位是毫秒;
count:显示的次数;

#返回可用统计项列表
# jstat -options
-class:class loader
-compiler:JIT
-gc:gc
-gccapacity:统计堆中各代的容量
-gccause:
-gcmetacapacity
-gcnew:新生代
-gcnewcapacity
-gcold:老年代
-gcoldcapacity
-gcutil
-printcompilation

S0C: Current survivor space 0 capacity (kB).
S1C: Current survivor space 1 capacity (kB).
S0U: Survivor space 0 utilization (kB).
S1U: Survivor space 1 utilization (kB).
EC: Current eden space capacity (kB).
EU: Eden space utilization (kB).
OC: Current old space capacity (kB).
OU: Old space utilization (kB).
MC: Metaspace capacity (kB).
MU: Metacspace utilization (kB).
CCSC: Compressed class space capacity (kB).
CCSU: Compressed class space used (kB).
YGC: Number of young generation garbage collection events.
YGCT: Young generation garbage collection time.
FGC: Number of full GC events.
FGCT: Full garbage collection time.
GCT: Total garbage collection time.
TT: Tenuring threshold.
MTT: Maximum tenuring threshold.
DSS: Desired survivor size (kB)

范例:

[root@tomcat ~]#jstat -gc 21407
S0C S1C S0U S1U EC EU OC OU MC MU
CCSC CCSU YGC YGCT FGC FGCT GCT
8704.0 8704.0 1563.6 0.0 69952.0 58033.0 174784.0 0.0 9344.0
8830.0 1152.0 1013.0 2 0.050 0 0.000 0.050

S0C:S0区容量
YGC:新生代的垃圾回收次数
YGCT:新生代垃圾回收消耗的时长;
FGC:Full GC的次数
FGCT:Full GC消耗的时长
GCT:GC消耗的总时长

#3次,一秒一次
[root@tomcat ~]#jstat -gcnew 21407 1000 3
S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT
8704.0 8704.0 1563.6 0.0 15 15 4352.0 69952.0 62794.3 2 0.050
8704.0 8704.0 1563.6 0.0 15 15 4352.0 69952.0 62794.3 2 0.050
8704.0 8704.0 1563.6 0.0 15 15 4352.0 69952.0 63074.5 2 0.050

jstack

程序员常用堆栈情况查看工具

jstack:查看指定的java进程的线程栈的相关信息

格式

jstack [-l] <pid>
jstack -F [-m] [-l] <pid>
-l:long listings,会显示额外的锁信息,因此,发生死锁时常用此选项
-m:混合模式,既输出java堆栈信息,也输出C/C++堆栈信息
-F:当使用"jstack -l PID"无响应,可以使用-F强制输出信息

范例:

#先获得一个java进程PID,然后jinfo
[root@tomcat ~]#jstack -l 21407
2020-02-15 13:49:56
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.241-b07 mixed mode):
"RMI TCP Connection(4)-10.0.0.101" #16 daemon prio=9 os_prio=0
tid=0x00007f279c29e800 nid=0x5753 runnable [0x00007f279b181000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
......

jmap

Memory Map, 用于查看堆内存的使用状态

#查看进程堆内存情况
[root@tomcat ~]#jmap -heap 21407
Attaching to process ID 21407, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.241-b07

using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 536870912 (512.0MB)
NewSize = 89456640 (85.3125MB)
MaxNewSize = 178913280 (170.625MB)
OldSize = 178978816 (170.6875MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
New Generation (Eden + 1 Survivor Space):
capacity = 80543744 (76.8125MB)
used = 4893096 (4.666419982910156MB)
free = 75650648 (72.14608001708984MB)
6.075078903707283% used
Eden Space:
capacity = 71630848 (68.3125MB)
used = 3301800 (3.1488418579101562MB)
free = 68329048 (65.16365814208984MB)
4.609466580655306% used
From Space:
capacity = 8912896 (8.5MB)
used = 1591296 (1.517578125MB)
free = 7321600 (6.982421875MB)
17.85386029411765% used
To Space:
capacity = 8912896 (8.5MB)
used = 0 (0.0MB)
free = 8912896 (8.5MB)
0.0% used
tenured generation:
capacity = 178978816 (170.6875MB)
used = 0 (0.0MB)
free = 178978816 (170.6875MB)
0.0% used
4926 interned Strings occupying 389832 bytes.

jhat

Java Heap Analysis Tool 堆分析工具

格式

jmap [option] <pid>
#查看堆空间的详细信息:
jmap -heap <pid>

范例:

[root@t1 ~]#jmap -heap 96334
Attaching to process ID 96334, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.251-b08
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 251658240 (240.0MB)
NewSize = 5570560 (5.3125MB)
MaxNewSize = 83886080 (80.0MB)
OldSize = 11206656 (10.6875MB)

#查看堆内存中的对象的数目:
jmap -histo[:live] <pid>
		live:只统计活动对象;
	
#保存堆内存数据至文件中,而后使用jvisualvm或jhat进行查看:
jmap -dump:<dump-options> <pid>
dump-options:
live dump only live objects; if not specified, all objects in the heap are

dumped.
format=b binary format
file=<file> dump heap to <file>

jconsole 和 JMX

图形化工具,可以用来查看Java进程信息

JMX(Java Management Extensions,即Java管理扩展)是一个为JAVA应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。

JMX最常见的场景是监控Java程序的基本信息和运行情况,任何Java程序都可以开启JMX,然后使用JConsole或Visual VM进行预览。

#为Java程序开启JMX很简单,只要在运行Java程序的命令后面指定如下命令即可
java \
-Dcom.sun.management.jmxremote \ #启用远程监控JMX
-Djava.rmi.server.hostname=10.0.0.100 \ #指定自已监听的主机的IP,不支持0.0.0.0
-Dcom.sun.management.jmxremote.port=12345 \ #指定监听的PORT
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-jar app.jar|app.war

在 tomcat 开启远程 JMX 支持 Zabbix 监控,如下配置

vim /usr/local/tomcat/bin/catalina.sh
CATALINA_OPTS="$CATALINA_OPTS
-Dcom.sun.management.jmxremote #启用远程监控JMX
-Dcom.sun.management.jmxremote.port=XXXXX #默认启动的JMX端口号,要和zabbix添加主机时候的端口一致即可
-Dcom.sun.management.jmxremote.authenticate=false #不使用用户名密码
-Dcom.sun.management.jmxremote.ss1=false #不使用ssl认证
-Djava.rmi.server.hostname=<JAVA主机IP>" #tomcat主机自己的IP地址,不要写zabbix服务器的地址

范例: 开启远程JMX功能

[root@tomcat ~]#vim /usr/local/tomcat/bin/catalina.sh
.......
# -----------------------------------------------------------------------------
CATALINA_OPTS="$CATALINA__OPTS \
-Dcom.sun.management.jmxremote \
-Djava.rmi.server.hostname=10.0.0.101 \
-Dcom.sun.management.jmxremote.port=12345 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false"

# OS specific support. $var _must_ be set to either true or false.
.......
[root@tomcat ~]#systemctl restart tomcat
[root@tomcat ~]#ss -ntl
State Recv-Q Send-Q Local Address:Port Peer
Address:Port
LISTEN 0 128 0.0.0.0:22
0.0.0.0:*
LISTEN 0 100 127.0.0.1:25
0.0.0.0:*
LISTEN 0 128 [::]:22
[::]:*
LISTEN 0 50 *:12345
*:*
LISTEN 0 100 [::1]:25
[::]:*
LISTEN 0 50 *:38563
*:*
LISTEN 0 1 [::ffff:127.0.0.1]:8005
*:*
LISTEN 0 50 *:40613
*:*
LISTEN 0 100 *:8080
*:*

Tomcat 性能优化常用配置

内存空间优化

JAVA_OPTS="-server -Xms4g -Xmx4g -XX:NewSize= -XX:MaxNewSize= "

-server:服务器模式
-Xms:堆内存初始化大小
-Xmx:堆内存空间上限
-XX:NewSize=:新生代空间初始化大小
-XX:MaxNewSize=:新生代空间最大值

生产案例:

[root@centos8 ~]#vim /usr/local/tomcat/bin/catalina.sh
JAVA_OPTS="-server -Xms4g -Xmx4g -Xss512k -Xmn1g -XX:CMSInitiatingOccupancyFraction=65 -XX:+AggressiveOpts -XX:+UseBiasedLocking -XX:+DisableExplicitGC -XX:MaxTenuringThreshold=10 -XX:NewRatio=2 -XX:PermSize=128m -XX:MaxPermSize=512m -XX:CMSFullGCsBeforeCompaction=5 -XX:+ExplicitGCInvokesConcurrent -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods"
#一台tomcat服务器并发连接数不高,生产建议分配物理内存通常4G到8G较多,如果需要更多连接,一般会利用虚拟化技术实现多台tomcat

线程池调整

[root@centos8 ~]#vim /usr/local/tomcat/conf/server.xml
......
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" maxThreads="2000"/>
......

常用属性:

  • connectionTimeout :连接超时时长,单位ms
  • maxThreads:最大线程数,默认200
  • minSpareThreads:最小空闲线程数
  • maxSpareThreads:最大空闲线程数
  • acceptCount:当启动线程满了之后,等待队列的最大长度,默认100
  • URIEncoding:URI 地址编码格式,建议使用 UTF-8
  • enableLookups:是否启用客户端主机名的DNS反向解析,缺省禁用,建议禁用,就使用客户端IP就行
  • compression:是否启用传输压缩机制,建议 "on",CPU和流量的平衡
    • compressionMinSize:启用压缩传输的数据流最小值,单位是字节
    • compressableMimeType:定义启用压缩功能的MIME类型text/html, text/xml, text/css,text/javascript

评论