web服务 · 2023年9月14日 0

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平台部署就有了问题

二、堆内存分代

Heap堆内存分为

年轻代YoungYoung Generation

伊甸园区eden: 只有一个,刚刚创建的对象

幸存(存活)Servivor   Space:有2个幸存区,一个是from区,一个是to区。大小相等、地位相同、可互换。

from 指的是本次复制数据的源区

to 指的是本次复制数据的目标区

老年代TenuredOld Generation, 长时间存活的对象

参数说明举例
  -Xms设置应用程序初始使用的堆内存大小(年轻代 +老年代)  -Xms2g
  -Xmx设置应用程序能获得的最大堆内存 早期JVM不建议超过32G,内存管理效率下降  -Xmx4g
-XX:NewSize设置初始新生代大小-XX:NewSize=128m
  -XX:MaxNewSize设置最大新生代内存空间– XX:MaxNewSize=256m
  -Xmnsize同时设置-XX:NewSize 和 -XX:MaxNewSize,代替两者  -Xmn1g
  -XX:NewRatio以比例方式设置新生代和老年代-XX:NewRatio=2 即:new:old=1:2
– XX:SurvivorRatio以比例方式设置eden和survivor(S0或S1)-XX:SurvivorRatio=6 即:Eden:S0:S1=6:1:1
  -Xss设置每个线程私有的栈空间大小,依据具体线程大小和数量  -Xss256k

三、垃圾回收器

  • 新生代
    • 新生代串行收集器Serial:单线程、独占式串行,采用复制算法,简单高效但会造成STW
    • 新生代并行回收收集器PS(Parallel Scavenge):多线程并行、独占式,会产生STW, 使用复制算法关注调整吞吐量,此收集器关注点是达到一个可控制的吞吐量
    • 新生代并行收集器ParNew:就是Serial   收集器的多线程版,将单线程的串行收集器变成了多线程并行、独占式,使用复制算法,相当于PS的改进版
  • 老年代
    • 老年代串行收集器Serial Old:Serial Old是Serial收集器的老年代版本,单线程、独占式串行,回收算法使用标记压缩
    • 老年代并行回收收集器Parallel   Old:多线程、独占式并行,回收算法使用标记压缩,关注调整吞吐量
    • Parallel Old收集器是Parallel Scavenge 收集器的老年代版本,这个收集器是JDK1.6之后才开始提供,从HotSpot虚拟机的垃圾收集器的图中也可以看出,Parallel Scavenge 收集器无法与CMS收集器配合工作,因为一个是为了吞吐量,一个是为了客户体验(也就是暂停时间的缩短)此为默认的新老年代的垃圾回收器
    • CMS (Concurrent Mark Sweep并发标记清除算法) 收集器

四、JVM相关工具

$JAVA_HOME/bin下

命令说明
jps查看所有jvm进程
jinfo查看进程的运行环境参数,主要是jvm命令行参数
jstat对jvm应用程序的资源和性能进行实时监控
jstack查看所有线程的运行状态
jmap查看jvm占用物理内存的状态
jhat+UseParNew
jconsole图形工具
jvisualvm图形工具
jstat -gc 14314 1000 10
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
12800.0 13312.0 5943.6  0.0   736768.0 393975.6 2174464.0   169471.5  104472.0 93626.6 11568.0 9563.4    620    6.709   4      0.577    7.286
12800.0 13312.0 5943.6  0.0   736768.0 393975.6 2174464.0   169471.5  104472.0 93626.6 11568.0 9563.4    620    6.709   4      0.577    7.286
12800.0 13312.0 5943.6  0.0   736768.0 393975.6 2174464.0   169471.5  104472.0 93626.6 11568.0 9563.4    620    6.709   4      0.577    7.286

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

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 [option] <pid>

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

jmap -histo[:live] <pid> #查看堆内存中的对象的数目: #live:只统计活动对象;

jmap -dump:<dump-options> <pid> # live, format=b , file=<file>

jmap -heap 14314
Attaching to process ID 14314, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.181-b13

using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 4294967296 (4096.0MB)
   NewSize                  = 715653120 (682.5MB)
   MaxNewSize               = 1431306240 (1365.0MB)
   OldSize                  = 1431830528 (1365.5MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
Exception in thread "main" java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at sun.tools.jmap.JMap.runTool(JMap.java:201)
	at sun.tools.jmap.JMap.main(JMap.java:130)
Caused by: java.lang.RuntimeException: unknown CollectedHeap type : class sun.jvm.hotspot.gc_interface.CollectedHeap
	at sun.jvm.hotspot.tools.HeapSummary.run(HeapSummary.java:157)
	at sun.jvm.hotspot.tools.Tool.startInternal(Tool.java:260)
	at sun.jvm.hotspot.tools.Tool.start(Tool.java:223)
	at sun.jvm.hotspot.tools.Tool.execute(Tool.java:118)
	at sun.jvm.hotspot.tools.HeapSummary.main(HeapSummary.java:50)
	... 6 more