1、为什么设置了-Xms128m -Xmx128m,启动后就飙到了300m?
2、为什么没有一种收集器可以适配所有场景?
1:JVM内存压榨。
2:空闲内存释放回操作系统。
3:根据实际应用场景,进行jvm性能调优。
在jdk1.8中,取消了永久代,元空间(Metaspace)登上舞台,方法区存在于元空间(Metaspace)。同时,元空间不再与堆连续,而且是存在于本地内存(Native memory)。
堆:包括老年代 + 新生代(Eden+Form+To或者Eden+S0+S1),Survivor区包括S0,S1。
Minor GC:又称新生代GC,指发生在新生代的垃圾收集动作。
Major GC/Full GC:老年代GC,指发生在老年代的GC。
stop-the-world:简称STW,是JVM在后台自动发起和自动完成的,在用户不可见的情况下,把用户正常的工作线程全部停掉,即GC停顿;
垃圾收集器:是垃圾回收算法(标记-清除算法、复制算法、标记-整理算法(压缩法)、分代收集算法)的具体实现,不同商家、不同版本的JVM所提供的垃圾收集器可能会有很在差别,本文主要介绍HotSpot(JVM的技术实现)虚拟机中的垃圾收集器。
并行垃圾收集:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态;如ParNew、Parallel Scavenge、Parallel Old。
并发垃圾收集:指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行);
用户程序在继续运行,而垃圾收集程序线程运行于另一个CPU上;如CMS、G1(并行与并发)。
吞吐量(Throughput):CPU用于运行用户代码的时间与CPU总消耗时间的比值;
吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
高吞吐量即减少垃圾收集时间,让用户代码获得更长的运行时间
垃圾收集器期望的目标(关注点):
(1)、停顿时间
停顿时间越短就适合需要与用户交互的程序;
良好的响应速度能提升用户体验;
(2)、吞吐量
高吞吐量则可以高效率地利用CPU时间,尽快完成运算的任务;
主要适合在后台计算而不需要太多交互的任务;
(3)、覆盖区(Footprint)
在达到前面两个目标的情况下,尽量减少堆的内存空间;
可以获得更好的空间局部性;
现在常见的垃圾收集器有如下几种:
新生代收集器:
老年代收集器:
堆内存垃圾收集器:G1
每种垃圾收集器之间有连线,表示他们可以搭配使用,如下图:
下面给出配置回收器时,经常使用的参数:
-XX:+UseSerialGC:在新生代和老年代使用串行收集器
-XX:+UseParNewGC:在新生代使用并行收集器
-XX:+UseParallelGC :在新生代使用并行收集器,老年代使用串行收集器(jdk1.8默认收集器)
-XX:+UseParallelOldGC:新生代和老年代都使用并行收集器
-XX:ParallelGCThreads:设置用于垃圾回收的线程数
-XX:+UseConcMarkSweepGC:老年代使用CMS收集器
-XX:ParallelCMSThreads:设定CMS的线程数量
-XX:+UseG1GC:启用G1垃圾收集器(jdk1.9默认收集器)
-XX:MaxGCPauseMillis:控制收集的暂停时间(ms),谨慎使用
1、分析内存占用情况----本地内存跟踪(Native Memory Tracking NMT)
# JVM启动参数配置,打开NMT会带来5%-10%的性能损耗,暂为调试开启
$ -XX:NativeMemoryTracking=summary
$ jcmd VM.native_memory summary
2、查看jvm参数默认值
$ java -XX:+PrintFlagsFinal
$ java -XX:+PrintFlagsInitial
3、查询JVM启动参数信息
$ jinfo -flags
4、查看full gc频率(full gc频率 =持续时间 /FGC)
$ jstat -gc
5、堆占用情况
$ jmap -heap
Jconsole : JDK自带,功能简单,但是可以在系统有一定负荷的情况下使用。
VisualVM:JDK自带,功能强大,与JProfiler类似。推荐
JProfiler:商业软件,需要付费(有破解版)。功能强大。
【注】: Jconsole、VisualVM远程连接参考
使用工具跟踪jvm占用的本地内存,查看内存都去哪里了,看看哪些部分可以调整压缩
【注意】
1. 打开NMT会带来5%-10%的性能损耗,暂为演示开启。
2. jvm参数设置有顺序要求,比如"-Xshare:on" 在linux系统需放在启动命令最后。
-Xms85m # JAVA 堆内存的初始值、默认为物理内存的1/64
-Xmx128m # JAVA 堆内存的最大值,默认为物理内存的1/4
-XX:NativeMemoryTracking=summary # JVM启动参数配置,打开NMT会带来5%-10%的性能损耗,暂为调试开启
-XX:+UseSerialGC # 启用串行、最轻量级GC,默认-XX:+UseParallelGC
-XX:CICompilerCount=1 # 减少编译器线程数,最低为1,默认是3
-XX:-TieredCompilation # 只启用最新编译层
-XX:MetaspaceSize=100m # 初始元空间大小,默认21MB
-XX:MaxMetaspaceSize=100m # 最大元空间,默认是没有限制的
-XX:CompressedClassSpaceSize=15m # 设置Klass Metaspace的大小 ,默认1G;Klass Metaspace就是用来存klass的,klass是class文件在jvm里的运行时数据结构,则CompressedClassSpace分配在MaxMetaspaceSize里头,即MaxMetaspaceSize=Compressed Class Space Size + Metaspace area
-Xss256k # 每个线程的堆栈大小,默认1M
-XX:MaxDirectMemorySize=30m # Direct ByteBuffer分配的堆外内存此参数到达指定大小后,即触发Full GC,上限由JVM运行时的最大内存来决定,基本上和-xmx大小相等。内存不足时抛出OutOfMemoryError
-Xshare:on # 开启类数据共享(linux系统需放在启动命令最后)
$ nohup java -XX:NativeMemoryTracking=summary -jar -Xms85m -Xmx128m ***.jar >>***.out 2>&1 &
$ nohup java -XX:+UseSerialGC -XX:NativeMemoryTracking=summary -XX:CICompilerCount=1 -XX:-TieredCompilation -XX:MetaspaceSize=100m -XX:MaxMetaspaceSize=100m -XX:CompressedClassSpaceSize=15m -Xms85m -Xmx128m -Xss256k -XX:MaxDirectMemorySize=30m -jar ***.jar -Xshare:on >>***.out 2>&1 &
使用NMT查看(重点关注每个分类下的commit大小,这个是实际使用的内存大小)
$ jcmd VM.native_memory summary
举例: 比如一个项目上传、下载功能需要大量的堆内存,那么参数设置较低,一定会内存溢出,这种有特殊情况的,可以用下述方案,效果显著,占用内存接近启动时内存
-Xms85m -Xmx300m -XX:+UseSerialGC
设置一个定时器,定时调用System.gc(),一段时间后(约半小时),会释放大量空闲内存
注:仅公司内部压缩内存使用,或有特殊需求时使用,不推荐正式上线项目使用。
可设置参数-XX:+DisableExplicitGC,禁止程序中调用System.gc()
【注】: jdk1.8暂不支持自动归还空闲内存。
java12的G1垃圾收集器,使其能够在空闲时自动将 Java 堆内存返还给操作系统。
(1)、根据实际应用场景,比如:吞吐量优先;减少卡顿、提示用户体验度优先,如何搭配合适的垃圾收集器;
(2)、设置合适的参数;
(3)、在理论基础上,模拟实际应用场景,进行长期的调试、观察,不可随意配置就上线使用,可能会适得其反。
(4)、充分利用服务器资源,比如8G,16G,32G如何搭配参数。
(1)、单个CPU的环境,在用户的桌面应用场景中,可用内存一般不大(几十M至一两百M)
推荐Serial收集器,参数说明如下:
-XX:+UseSerialGC # 新生代和老年代都使用串行垃圾收集器
启动参数如下:
-Xms128m -Xmx128m -XX:+UseSerialGC -XX:TargetSurvivorRatio=90 -XX:NewRatio=1 -XX:+DisableExplicitGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:C:\Users\cosmo-101\Desktop\gc.log
(2)、高吞吐为目标,对暂停时间没有特别高的要求
推荐Parallel Scavenge收集器,参数说明如下:
【注】:只需设置好内存数据大小(如"-Xmx"设置最大堆)。
-XX:+UseParallelOldGC # 新生代和老年代都使用并行收集器
-XX:MaxGCPauseMillis # 控制最大垃圾收集停顿时间,大于0的毫秒数
-XX:GCTimeRatio # 设置垃圾收集时间占总时间的比率,0
启动参数如下:
-Xms2g -Xmx2g -XX:+UseParallelOldGC -XX:MaxGCPauseMillis=200 -XX:GCTimeRatio=99 -XX:+UseAdaptiveSizePolicy -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m -XX:+DisableExplicitGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:C:\Users\cosmo-101\Desktop\gc.log
(3)、希望系统停顿时间最短,注重服务的响应速度
推荐CMS收集器,参数说明如下:
-XX:+UseConcMarkSweepGC # 指定老年代使用CMS收集器,会默认使用ParNew作为新生代收集器
-XX:+CMSScavengeBeforeRemark # 在执行CMS Remark阶段前,执行一次Minor GC,以降低STW的时间。通过 Minor GC可以减少新生代对老年代对象的引用,这样可以减少根对象数量,从而降低CMS Remark的工作量.
-XX:CMSFullGCsBeforeCompaction
设置执行多少次不压缩的Full GC后,来一次压缩整理;
为了减少合并整理过程的停顿时间,默认为0;
也就是说每次都执行Full GC,不会进行压缩整理。
启动参数如下:
-Xms2g -Xmx2g -XX:+UseConcMarkSweepGC -XX:TargetSurvivorRatio=90 -XX:NewRatio=1 -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m -XX:+CMSScavengeBeforeRemark -XX:CMSFullGCsBeforeCompaction=10 -XX:+DisableExplicitGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:C:\Users\cosmo-101\Desktop\gc.log
(4)、针对具有大内存、多处理器的机器,低GC延迟,堆大小约6GB或更大时
推荐G1收集器,参数说明如下:
【注】:使用G1回收器时,G1打破了以往将收集范围固定在新生代或老年代的模式,不需要为各个空间进行单独设置了,G1算法将堆整体划分为若干个区域(Region)。
-XX:+UseG1GC # 指定使用G1收集器
-XX:MaxGCPauseMillis # 为G1设置暂停时间目标,默认值为200毫秒
-XX:G1HeapRegionSize # 设置每个Region大小,范围1MB到32MB;目标是在最小Java堆时可以拥有约2048个Region
-XX:ParallelGCThreads=n # STW期间,并行线程数。建议设置与处理器相同个数,最多为8。
如果处理器多于8个,则将n的值设置为处理器的大约5/8。
启动参数如下:
-Xms8g -Xmx8g -XX:+UseG1GC -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m -XX:+DisableExplicitGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:C:\Users\cosmo-101\Desktop\gc.log
-XX:TargetSurvivorRatio # 默认值50,当survivor区存放的对象超过这个百分百,则对象会向老年代压缩,因此,有助于将对象留在新生代
-XX:NewRatio=1 # 老年代与新生代的比例,默认2:1,有助于将对象预留新生代,新生代Minor GC成本远远小于老年代的Full GC
-Xmn=70m
-XX:MetaspaceSize=256m # 初始元空间大小,默认21MB
-XX:MaxMetaspaceSize=256m # 最大元空间,默认是没有限制的,可以根据GC日志打印,如“Full GC (Metadata GC Threshold)”过于频繁,则调整此参数;未设置,虚拟机会自动从21MB初始大小逐渐增长,每次Full GC后自动调整
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log # 保存gc日志,作为优化的依据
-XX:+DisableExplicitGC # 禁止System.gc(),免得程序员误调用gc方法影响性能
1、 聚合报告参数详解
# 1、Label:每个 JMeter 的 element(例如 HTTP Request)都有一个 Name 属性,这里显示的就是 Name 属性的值;
# 2、#Samples:表示这次测试中一共发出了多少个请求,如果模拟10个用户,每个用户迭代10次,那么这里显示100;【我的是用户有100,只迭代一次,因此也是100】
# 3、Average:平均响应时间——默认情况下是单个 Request 的平均响应时间(ms);
# 4、Median:中位数,也就是 50% 用户的响应时间;
# 5、90% Line ~ 99% Line:90% ~99%用户的响应时间;
# 6、Min:最小响应时间;
# 7、Maximum:最大响应时间;
# 8、Error%:本次测试中出现的错误率,即 错误的请求的数量/请求的总数;
# 9、Throughput:吞吐量——默认情况下表示每秒完成的请求数(Request per Second);
# 11、Sent KB/src:每秒从客户端发送的请求的数量。
2、 压测工具使用
【注】:根据jmeter启动说明,执行测试计划不能用GUI,需要用命令行来执行
可以通过GUI调整测试参数,再通过命令行来执行
在jmeter安装目录下的bin文件夹下,执行命令如下:
$ jmeter -n -t testplan.jmx -l result.jtl -e -o webreport
# testplan.jmx 为测试计划文件路径
# result.txt 为测试结果文件路径
# webreport 为web报告保存路径。
3、 模拟实际应用,测试方案
(1). 创建一个测试计划;
(2). 在该计划下添加2个线程组,分别是对业务A、业务B;
(3). 分别设置线程组业务A、业务B的线程数为:50、50(总并发量为100);
(4). 观察吞吐量,响应时间是否满足需求。
4、不同启动参数压测报告对比图:
(1)、如下图:
-Xms128m -Xmx128m -XX:+UseSerialGC -XX:TargetSurvivorRatio=90 -XX:NewRatio=1 -XX:+DisableExplicitGC
(2)、如下图:
-Xms8g -Xmx8g -XX:+UseG1GC -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m -XX:+DisableExplicitGC
压测结果:
根据上述两图,图2 Average(平均响应时间),Throughput(吞吐)明显比图1优秀;
除聚合报告,还可以根据需要观察cpu,memory,Network,I/O等等。
监控cpu等需要安装插件(推荐安装jmeter3.1版本,版本过高会报错)
Jmeter性能指标分析参考链接
Jmeter插件参考链接
七种垃圾回收器原文地址