一、JVM 常用命令行参数
- JVM 常用命令行参数
-
参数类型
-标准参数 , 所有的HotSpot都支持
-X 非标准参数, 特定版本的HotSpot支持的命令
-XX 不稳定参数, 下个版本可能取消 -
常见垃圾回收器组合参数设定 1.8
-XX:+UseSerialGC
Serial New(DefNew)+ Serial Old 使用单线程回收器(小型程序)-XX:+UseParNewGC
ParNew+SerialOld 很少使用了-XX:+UseConcMarkSweepGC
ParNew + CMS +Serial Old-XX:+UseParallelGC
Parallel Scavenge + Parallel Old [1.8 默认 Ps+SerialOld]-XX:+UseParallelIOldGC
Parallel Scavenge + Parallel Old-XX:+UseG1GC
G1
-XX:+PrintCommandLineFlags
查看程序使用的默认JVM参数
-XX:-Xmn10M -Xms10M
设置堆的最小、最大。尽量设置一样,防止弹性堆 消耗CPU
-XX:+PrintGC
+PrintGCDetails
+PrintGCTimeStamps
+PrintGCCause
打印GC信息
-XX:+PrintFlagsInitial 默认参数值
-XX:+PrintFlagsFinal 最终参数值
-XX:+PrintFlagsFinal | grep xxx 找到对应的参数
-XX:+PrintFlagsFinal -version | grep GC -
二、GC日志详解
三、调优的目的
吞吐量优先 ( PS+PO )
科学计算、数据挖掘
响应时间优先 (1.8版本 G1 或 Parnew + CMS )
网站 GUI API
在满足一定的响应时间的情况下,
要求达到多大的吞吐量
四、什么是调优? 重点:如何定位
1.根据需求进行JVM规划和预调优
预规划
淘宝历史最高并发 54w,
一千并发够几十万用户同时在线
12306号称百万
调优,从业务场景开始
无监控(压力测试,能看到结果),不调优
案例1:服务器选择
垂直电商(只卖一种产品),最高每日百万订单,处理订单系统需要什么样的服务器配置?
很多服务器都能支持 每日百万级订单 (1.5G 16G 即可)
比如购物高峰是晚上20:00——22:00
百万订单假设有72W是这两小时出现的
每小时平均也就36W个订单
每小时3600秒
也就QPS 100/s
正常服务器能支持QPS 1000/s
找一小时内的高峰期,1000订单/s
非要计算:一个订单产生多少内存
512K就很多了
512*1000 = 500MB
专业问法:要求响应时间为100ms
压测。
案例2: 12306抢票
大流量的处理方法:分而治之
12306遭遇春节大规模抢票应该如何支撑
12306是中国并发最大的秒杀网站
号称百万并发 最高了
CDN->LVS->Nginx->业务系统->每台机器1W并发( 单机10K问题【依靠Redis】 ) 100台机器
CDN做缓存
普通电商订单->下单->订单系统( IO )减库存->等待用户付款
12306的一种可能的模型:
下单->减库存和订单(redis kafka)同时异步进行->等付款
减库存Uzi后还会把压力压到一台服务器
可以做成分布式本地库存 + 单独服务器做库存均衡
可以分成多个库存服务器,分而治之,分别负责某些IP区域传来的订单,然后有多有少,就有个单独的服务器再控制各个库存服务器的数量均衡
2.优化JVM运行环境
优化环境案例
硬件升级系统反而卡顿问题
有一个 50万 PV的资料类网站( 从磁盘提取文档到内存 ),原服务器32位,1.5G的堆,用户反馈网站比较缓慢,因此公司绝对升级,
新的服务器64位。16G的堆,结果用户反馈卡顿十分严重,反而比之前的效率更低了。
为什么原网站慢?
很多用户浏览数据,很多数据加载到内存,内存不足,频繁GC,STW长。响应时间变慢。因此需要升级。
为什么会卡顿?
内存越大,FGC时间越长,STW时间就越长
解决方案: PS -> PN + CMS 或者 用G1
之前是默认的 PA + PO
如何优化?
需要使用适当的垃圾回收器
系统CPU经常100%,如何调优?
找CPU高的进程的线程的方法
找出哪个进程 CPU 高 top
CPU 100% 一定有线程在占用系统资源
该进程的哪个线程CPU高 top -Hp
导出该线程的堆栈 jstacl
查找哪个方法(栈帧)消耗时间 jstac
工作线程占比高 | 垃圾回收线程占比高
系统内存飙高,如何查找问题?
线程池不当运用产生OOM问题
内存飙高一定是堆栈飙高
导出堆内存 jmap
分析 jhat jvisualvm mat 等
如何监控JVM
jstat
jvisualvm
jprofiler
arthas
top命令
3.解决JVM运行过程中出现的各种问题
解决OOM等。。。reboot,搞不定了就重启
OOM问题(有些程序未必产生OOM ,不断FGC;CPU飙高,但内存回收特别少)
重启
扩内存
换垃圾回收器 G1
扩内存,换G1,jdk8,可以很大缓解卡顿
tomcat http-header-size过大会导致http11OutOfBuffer对象溢出
lambda表达式导致方法区溢出问题 MethodArea
Caused by:java.lang.OutOfMemoryError:ComPressed class space;( 产生很多class ,而且不会被回收,在被用)
直接内存溢出:使用Unsafe分配内存或者使用NIO
栈溢出问题: -Xss设定太小
递归调用本方法没写终止条件
以下两种写法哪个好
Object o=null ;
fori
o=new Object();
fori
Object o =new Object()
第一种更好,对象用完后没有引用指向它,可以直接回收
重写finalize引发频繁GC
小米云,HBase同步系统,系统通过nginx访问超时报警,最后排查,是C++程序员重写finalize引发频繁GC问题。
为什么C++程序员会重写finalize?
因为C++程序员需要手动释放内存。需要new 一个自己定义的析构函数。可能他看finalize和析构函数比较像,他重写的方法耗时比较长,析构不过来,就造成了频繁GC
C++中
new语句,默认调用构造函数
delete语句,默认调用析构函数
如果一个系统内存消耗不到10%,但观察GC日志,发现FGC总是频繁产生,是什么引起的?
有人手动调用了 System.gc( )
JVM内存占物理内存的比例 50% — 80%
new 大量线程会产生native thread OOM ,应该用线程池
解决方案:减少堆空间,预留更多内存来产生native
多个exists的联合sql会溢出,几百个对象笛卡尔积
五、调优基本思路
1. 熟悉业务场景,选择适合的垃圾回收器
根据是想要响应时间还是吞吐量选择
2. 选择回收器组合
3. 计算内存需求(经验值16G要1.5G)
4. 选定CPU (越高越好)
5. 设定年代大小,升级年龄
6. 设定日志参数
或者每天生产一个日志文件
-Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log 输出日志
-XX:+UseGCLogFileRotation 循环使用
-XX:NumberOfGCLogFiles=5 5个日志文件循环使用
-XX:GCLogFileSize=20M
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintGCCause
%t 是系统时间,产生日志名按系统时间生成
最多同时存在 5 个日志文件,循环使用,删除最老的,
每个文件最大20MB,输出文件到指定目录
打印 GCDetails
六、调优方式
一般网管团队会先收到报警信息,如CPU飙高,OOM
top命令 查看当前所有进程,默认 按CPU占比降序。(比如第一个进程号为1364的,占比16.4%)
查看当前所有进程的信息
top命令观察到问题
top -Hp 1364
查看某个进程的所有线程信息
jstack 线程id 打印指定java进程的堆栈跟踪信息
jstack 定位线程状况,重点关注 : WAITING BLOCKED 阻塞信息
注意 waiting on <0x00088ca3310>(a java.lang.Object) 是一个持有锁的对象
假如有一个进程有100个线程,很多线程都在wairting on 某一把锁,一定要找到是哪个线程持有这把锁,
一直不释放,有问题。搜索jstack dump信息,找<xxx>,看哪个线程持有这把锁RUNNABLE
jps
打印所有java进程号
jinfo 进程号
查看该进程的详细信息
jstat -gc 进程号 (毫秒数)
(动态观察GC情况)打印GC信息,不加括号则只打印一次
jstat -gc 4655 500
每隔500毫秒打印一次
=============JMX java扩展服务,界面===================
jconsole 远程监控
参数太多了
jvisualvm
远程监控可视化
Jprofiler
最好用,但收费
arthas 在线排查工具
直接 java -jar arthas-boot.jar 运行
jvm 观察jvm信息
thread 定位线程问题
thread 2 查看2号线程具体信息
dashboard 观察系统情况
heapdump /root/logs/XXX.hprof
输出堆文件到指定路径
jhat -J-mx512M 文件路径/文件名
分析堆文件 http://127.0.0.1:7000
jad 反编译文件
动态代理生成类的问题定位
第三方的类(观察代码)
版本问题(确定自己最新提交的版本是不是被使用了)
redefine 热替换
只能改方法实现(方法已经运行完成),不能改方法名,不能改属性
改完代码先编译 .class
再 redefine /root/projects/Hello.class
jad Hello 查看代码
sc
search class
watch
watch method
ctrl + c 退出
注意!!! 没有包含的功能 jmap
如果面试官问你是怎么定位OOM问题的?
如果回答 用图像界面
必错的,因为在服务器开图像界面,肯定会影响性能的
已经上线的系统不用图像界面用什么?
cmdline
arthas
用 jmap -histo 进程 | head -10
查找某进程最多的前10个对象
图像界面到底有什么用,用在什么地方?
测试!上线之前压力测试的时候进行监控( 压测观察 )
jmap -histo 1736 | head -20
查看1736号进程前20行信息,查看有多少对象产生,可以进行在线定位
jmap -dump:format=b,file= XXX pid
手动导出堆转储文件,不要用!!!!
线上系统,内存特别大, 这个命令执行期间会对进程产生很大影响,甚至卡顿
案例:
上百G内存执行这个 jmap 命令,整个系统瘫痪一小时
解决方案
设置了参数HeapDump , OOM 的时候会自动产生 堆转储文件。可以把这个文件拿出来进行分析
java -Xms20M -Xmx20M -XX:+UseParallelGC -XX:+HeapDumpOnOUtOfMemoryError com.soft.Main
很多服务器备份( 高可用 ),停掉这个服务器对其他服务器不影响 最好说这个
在线定位排查工具( 小公司一般用不到 )
七、GC常用参数
通用
-Xmn -Xms -Xmx -Xss
年轻代 最小堆 最大堆 栈空间
-XX:+UseTLAB
使用TLAB,默认打开,使用线程本地分配缓冲区
-XX:+PrintTLAB
打印TLAB的使用情况
-XX:TLABSize
设置TLAB大小
-XX:+DisableExplictGC
让System.gc()不管用,这是FGC
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintHeapAtGC
打印堆栈时间
-XX:+PrintGCTimeStamps
打印发生GC的时间
-XX:+PrintGCApplicationConcurrentTime
打印应用程序时间
-XX:+PrintGCApplicationStoppedTime
打印暂停时间
-XX:+PrintReferenceGC
记录回收了多少种不同引用类型的引用
-verbose:class
类加载详细过程
-XX:+PrintVMOptions
打印JVM参数
-XX:+PrintFlagsFinal(最终的)
-XX:+PrintFlagInitial(最初的)
命令查找 -XX:+PrintFlagsFinal -version | grep G1
-Xloggc:opt/log/gc.log
打印日志
-XX:MaxTenuringThreshold
GC升代年龄,最大15
-XX:PreBlockSpin
锁自旋次数
-XX:CompileThreshold
热点代码检测参数,本地化编译
================================
Parallel常用参数
-XX:SurvivorRatio
-XX:PreTenureSizeThreshold
大对象到底有多大
-XX:+ParallelGCThreads
并行收集器的线程数,同样适用于CMS,一般设为和CPU核数相同
-XX:+UseAdaptiveSizePolicy
自动选择各区大小比例
=============================
CMS常用参数
-XX:+UseConcMarkSweepGC
-XX:ParallelCMSThreads
CMS线程数量,默认核数一半
-XX:CMSInitiatingOccupancyFraction
使用多少比例的老年代后开始CMS收集,默认64%(近似值),如果频繁发生SerialOld卡顿,应该调小(频繁CMS回收)
-XX:+UseCMSCompactAtFullCollection
在FGC时进行压缩
-XX:CMSFullGCsBeforeCompaction
多少次FGC后进行压缩
-XX:CMSClassUnloadingEnabled
-XX:CMSInitiatingPermOccupancyFraction
达到什么比例时进行Perm回收
GCTimeRatio
设置GC时间占用程序运用时间的百分比
-XX:MaxGcPauseMillis
停顿时间,是一个建议值,GC会尝试各种手段达到这个时间,比如减少年轻代
========================================
G1常用参数
-XX:+UseG1GC
-XX:MaxGCPauseMillis
建议值:G1会尝试调整Young区的块数来达到这个值
-XX:GCPauseIntervalMillis
-XX:+G1HeapRegionSize
分区大小,建议逐渐增加该值 1 2 4 8 16 32
G1NewSizePercent
新生代最小比例,默认5%
G1MaxNewSize\Percent
新生代最大比例,默认60%
GCTimeRatio
GC时间建议比例,G1会根据这个值调整堆空间
ConcGCThreads 线程数量
InitiatingHeapOccupancyPercent
启动G1的堆空间占用比例
八、其他概念
内存泄漏 Memory leak
有对象回收不了一直占内存
内存溢出 Out Of Memory
不断地产生对象,内存爆满
吞吐量
用户代码执行时间 / (用户代码执行时间 + 垃圾回收时间)
响应时间
STW (stop the world)越短,响应时间越好
为什么阿里规范里规定,线程的名称(尤其是线程池)都要写有意义的名称
方便出错时回溯