Tomcat负载均衡、调优核心应用进阶学习笔记(四):JVM调优
文章目录
概述
JVM主要是对象的创建回收等,而其他方面的调优主要应该是由java程序员来实现,所以常见的java调优,无非就是调整jvm进程所能够使用的内存空间。
JVM运行时由classloader装载类,装载之后开始在内存中部署并运行真正的执行过程是由执行引擎执行的,而执行引擎运行时有可能还要调用本地方法,所以java程序如果书写中有本地方法调用的话,则与本地方法库建立联系,而我整个JVM的内存空间大体上分为上图中下面方框中的五个组成部分
方法区
堆内存(方法区和堆内存是占用空间最大的,尤其是堆内存,java的各种对象多存储在堆内存中, 所以所谓的调优也主要是对堆内存进行调整)
JVM栈
本地方法栈
程序计数器寄存器
JVM管理的内存段可分为两大类:线程共享内存和线程私有内存
java实现多任务管理的时候,内部线程实现,有些是守护线程有些是工作线程,守护线程主要是跟jvm自身运行管理相关的,jvm其实就是模拟了一个计算机运行的环境包含了abi和api,所以在任何的jvm内部都是全状态的一个虚拟的程序运行环境。
线程共享内存
- 方法区:存储jvm加载的class、常量、静态变量、即使编译器编译后的代码等
- java堆:存储java的所有对象实例、数组等
线程私有内存
- 程序计数寄存器:每个线程有自己的计数寄存器,存储当前线程执行字节码的地址
- jvm栈:jvm会为每个运行此案城分配一个栈区,线程调用方法时和方法返回时会进行入栈和出栈的操作
- 本地方法栈去:与jvm stack类似,只不过此区域是调用本地方法服务
重点关注共享内存
Java HotSpot 虚拟机选项 -X -XX 的含义
-x -xx通常是用来保存这些内存区域的配置、状态数据,一个变量的打头选项。
Options that begin with -X are non-standard (not guaranteed to be supported on all VM implementations), and are subject to change without notice in subsequent releases of the JDK.
以 -X 开头的选项是非标准选项(不能保证被所有的 JVM 实现都支持),并且在 JDK 的后续版本中不需要通知就可以进行更改。
Options that are specified with -XX are not stable and are subject to change without notice.
以 -XX 指定的选项是不稳定的,并且会在没有通知的情况下进行更改,会根据当前jvm内部的运行特征不断的发生变化。
这些数据的获取可用通过 jinfo -flages 进行获取
//只要安装了jdk
//获取任意指定的标志的相关信息
➜ ~ jinfo -h
Usage:
jinfo [option] <pid>
(to connect to running process)
jinfo [option] <executable <core>
(to connect to a core file)
jinfo [option] [[email protected]]<remote server IP or hostname>
(to connect to remote debug server)
where <option> is one of:
//用于显示任意指定的标志的相关信息(vm flag的值)
//这些值有的以-x开头有的以-xx开始
-flag <name> to print the value of the named VM flag
//禁用某个flag
-flag [+|-]<name> to enable or disable the named VM flag
//设置某个flag的值
-flag <name>=<value> to set the named VM flag to the given value
-flags to print VM flags
-sysprops to print Java system properties
<no option> to print both of the above
-h | -help to print this help message
jps
1234 xxxx
jinfo -flags 1234 即可查看进程xxxx在启动进程时候的某些选项
Java Heap SIze Options
堆内存相关的选项:
-xx:NewsSize
-xx:MaxNewSize
-xx:SurvivorRatio
-Xms
-Xmx
The memory structure of a JVM process
-Xms:JVM已启动就需要立即分配的堆内存空间,即堆内存空间初始化大小(堆内存空间不包括持久代)
-Xmx:新生代加上新生代预留,老年代加上老年代预留(reserved),堆内存空间的最大值
-XX:NewSIze:新生代初始化空间 ,所以(-Xms)-(-XX:NewSIze)=老年代的初始化大小
-XX:MaxNewSize:新生代最大值,(-XX:MaxNewSize)-(-XX:NewSIze)= 新生代reserved,(-Xmx)-(-XX:MaxNewSize)=老年代最大空间值,(-Xmx)-(-XX:MaxNewSize)-(-Xms)-(-XX:NewSIze)=老年代reserved
-XX:MaxPerSize:持久代的最大值
-XX:PermSize:持久代的初始化值
-XX:ServivorRatio:Eden/survivor eden和(from+to)的比例
摘录
常见配置汇总
堆设置
-Xms:初始堆大小
-Xmx:最大堆大小
-XX:NewSize=n:设置年轻代大小
-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
-XX:MaxPermSize=n:设置持久代大小
收集器设置
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器
-XX:+UseParalledlOldGC:设置并行年老代收集器
-XX:+UseConcMarkSweepGC:设置并发收集器
垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
并行收集器设置
-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
并发收集器设置
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。
调优总结
年轻代大小选择
响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。
吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。
年老代大小选择
响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:
并发垃圾收集信息
持久代并发收集次数
传统GC信息
花在年轻代和年老代回收上的时间比例
减少年轻代和年老代花费的时间,一般会提高应用的效率
吞吐量优先的应用
一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。
较小堆引起的碎片问题
因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:
- -XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。
- -XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩
垃圾回收的瓶颈
传统分代垃圾回收方式,已经在一定程度上把垃圾回收给应用带来的负担降到了最小,把应用的吞吐量推到了一个极限。但是他无法解决的一个问题,就是Full GC所带来的应用暂停。在一些对实时性要求很高的应用场景下,GC暂停所带来的请求堆积和请求失败是无法接受的。这类应用可能要求请求的返回时间在几百甚至几十毫秒以内,如果分代垃圾回收方式要达到这个指标,只能把最大堆的设置限制在一个相对较小范围内,但是这样有限制了应用本身的处理能力,同样也是不可接收的。
分代垃圾回收方式确实也考虑了实时性要求而提供了并发回收器,支持最大暂停时间的设置,但是受限于分代垃圾回收的内存划分模型,其效果也不是很理想。
为了达到实时性的要求(其实Java语言最初的设计也是在嵌入式系统上的),一种新垃圾回收方式呼之欲出,它既支持短的暂停时间,又支持大的内存空间分配。可以很好的解决传统分代方式带来的问题。
摘自:https://www.cnblogs.com/andy-zhou/p/5327288.html#_caption_19