JVM组成、GC回收机制、算法、JVM常见启动参数、JAVA出现OOM,如何解决、tomcat优化方法
性能测试之JVM组成以及GC回收
我们会围绕以下几点来展开关于性能测试方面的话题
- JVM组成
- GC垃圾回收
- JVM常见启动参数
- JAVA程序出现OOM,如何解决
- tomcat的优化方法
JVM的组成

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开始此空间由永久代改名为元空间
- 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平台部署就有了问题
GC (Garbage Collection) 垃圾收集器
在堆内存中如果创建的对象不再使用,仍占用着内存,此时即为垃圾.需要即使进行垃圾回收,从而释放内存空间给其它对象使用
其实不同的开发语言都有垃圾回收问题,C,C++需要程序员人为回收,造成开发难度大,容易出错等问题,但执行效率高,而JAVA和Python中不需要程序员进行人为的回收垃圾,而由JVM或相关程序自动回收垃圾,减轻程序员的开发难度,但可能会造成执行效率低下
堆内存里面经常创建、销毁对象,内存也是被使用、被释放。如果不妥善处理,一个使用频繁的进程,可能会出现虽然有足够的内存容量,但是无法分配出可用内存空间,因为没有连续成片的内存了,内存全是碎片化的空间。
所以需要有合适的垃圾回收机制,确保正常释放不再使用的内存空间,还需要保证内存空间尽可能的保持一定的连续
对于垃圾回收,需要解决三个问题
-
哪些是垃圾要回收
-
怎么回收垃圾
-
什么时候回收垃圾
Garbage 垃圾确定方法
- 引用计数: 每一个堆内对象上都与一个私有引用计数器,记录着被引用的次数,引用计数清零,该对象所占用堆内存就可以被回收。循环引用的对象都无法将引用计数归零,就无法清除。Python中即使用此种方式
- 根搜索(可达)算法 Root Searching

垃圾回收基本算法
-
标记-清除 Mark-Sweep
分垃圾标记阶段和内存释放阶段。标记阶段,找到所有可访问对象打个标记。清理阶段,遍历整个堆,
对未标记对象(即不再使用的对象)逐一进行清理。


标记-清除最大的问题:会造成内存碎片,但是不浪费空间,效率较高高(如果对象较多,逐一删除效率也会影响)
-
标记-压缩(压实)Mark-Compact
分垃圾标记阶段和内存整理阶段。标记阶段,找到所有可访问对象打个标记。内存清理阶段时,整理时将对象向内存一端移动,整理后存活对象连续的集中在内存一端。

标记-压缩算法好处是整理后内存空间连续分配,有大段的连续内存可分配,没有内存碎片。缺点是内存整理过程有消耗,效率相对低下
-
复制 Copying
先将可用内存分为大小相同两块区域A和B,每次只用其中一块,比如A。当A用完后,则将A中存活的对象复制到B。复制到B的时候连续的使用内存,最后将A一次性清除干净。
缺点是比较浪费内存,只能使用原来一半内存,因为内存对半划分了,复制过程毕竟也是有代价。
好处是没有碎片,复制过程中保证对象使用连续空间,且一次性清除所有垃圾,所以效率很高
-
多种算法总结
没有最好的算法,在不同场景选择最合适的算法
-
效率: 复制算法>标记清除算法> 标记压缩算法
-
内存整齐度: 复制算法=标记压缩算法> 标记清除算法
-
内存利用率: 标记压缩算法=标记清除算法>复制算法
-
-
STW
对于大多数垃圾回收算法而言,GC线程工作时,停止所有工作的线程,称为Stop The World。GC 完成时,恢复其他工作线程运行。这也是JVM运行中最头疼的问题。
6. 堆内存分代
将heap内存空间分为三个不同类别:年轻代、老年代、持久代


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

永久代:JDK1.7之前使用, 即Method Area方法区,保存JVM自身的类和方法,存储JAVA运行时的环境信息, JDK1.8后 改名为 MetaSpace,此空间不存在垃圾回收,关闭JVM会释放此区域内存,此空间物理上不属于heap内存,但逻辑上存在于heap内存
-
永久代必须指定大小限制,字符串常量JDK1.7存放在永久代,1.8后存放在heap中
-
MetaSpace 可以设置,也可不设置,无上限
规律 一般情况99%的对象都是临时对象
范例: 在tomcat 状态页可以看到以下的内存分代

范例查看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
max=243269632字节 232.0MB
total=16252928字节 15.5MB
[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
#默认JVM试图分配最大内存的总内存的1/4,初始化默认总内存为总内存的1/64
年轻代回收Minor GC
-
起始时,所有新建对象(特大对象直接进入老年代)都出生在eden,当eden满了,启动GC。这个称为Young GC 或者 Minor GC。
-
先标记eden存活对象,然后将存活对象复制到s0(假设本次是s0,也可以是s1,它们可以调换),eden剩余所有空间都清空。GC完成。
-
继续新建对象,当eden再次满了,启动GC。
-
先同时标记eden和s0中存活对象,然后将存活对象复制到s1。将eden和s0清空,此次GC完成
-
继续新建对象,当eden满了,启动GC。
-
先标记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会提高效率。可以认为回收老年代的时候完成了一次Full
GC。
所以可以认为 MajorGC = FullGC
GC触发条件

Minor GC 触发条件:当eden区满了触发
Full GC 触发条件:
老年代满了
System.gc()手动调用。不推荐
年轻代
存活时长低
适合复制算法
老年代
区域大,存活时长高
适合标记压缩算法
JVM内存常用相关参数
Java 命令行参考文档: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

帮助:man java
选项分类
-
选项名称 此为标准选项,所有HotSpot都支持
-
-X选项名称 此为稳定的非标准选项
-
-XX选项名称 非标准的不稳定选项,下一个版本可能会取消

范例: 查看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 lao wang";
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
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(傻瓜式安装略)后,双击打开前面生成的两个文件java_pid96271.hprof和
java_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页面,可以看到以下页面

JVM工具概述

此处不多概述
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" />
......
常用属性:
- 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/javascrip

浙公网安备 33010602011771号