JVM学习08——JVM调优

一、JVM 常用命令行参数

  1. JVM 常用命令行参数
    1. 参数类型
      -标准参数 , 所有的HotSpot都支持
      -X 非标准参数, 特定版本的HotSpot支持的命令
      -XX 不稳定参数, 下个版本可能取消

    2. 常见垃圾回收器组合参数设定 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)越短,响应时间越好
	
为什么阿里规范里规定,线程的名称(尤其是线程池)都要写有意义的名称
	方便出错时回溯
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值