• 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
    • 2
    • 3
    • 4
    • 5

    要求达到多大的吞吐量

    四、什么是调优? 重点:如何定位

    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
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126

    五、调优基本思路

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    六、调优方式

    一般网管团队会先收到报警信息,如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信息,找,看哪个线程持有这把锁RUNNABLE
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述
    在这里插入图片描述

    jps
    	打印所有java进程号
    
    • 1
    • 2

    在这里插入图片描述

    jinfo 进程号
    	查看该进程的详细信息
    	
    jstat -gc 进程号 (毫秒数)
    	(动态观察GC情况)打印GC信息,不加括号则只打印一次
    		jstat -gc 4655 500
    		每隔500毫秒打印一次
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    =============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
    				很多服务器备份( 高可用 ),停掉这个服务器对其他服务器不影响                          最好说这个
    				在线定位排查工具( 小公司一般用不到 )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    七、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的堆空间占用比例
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119

    八、其他概念

    内存泄漏 Memory leak
    	有对象回收不了一直占内存
    	
    内存溢出 Out Of Memory
    	不断地产生对象,内存爆满
    	
    吞吐量
    	用户代码执行时间 / (用户代码执行时间 + 垃圾回收时间)
    	
    响应时间
    	STW (stop the world)越短,响应时间越好
    	
    为什么阿里规范里规定,线程的名称(尤其是线程池)都要写有意义的名称
    	方便出错时回溯
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    小米红米手机刷Hyper澎湃OS欧版EU教程-全球语言-完整GO框架-纯净飞速
    C语言二维数组定义、赋值、按要求遍历操作、输出以及函数调用
    如何做好测试管理岗?深度分析职业规划
    Android设置应用图标
    Spring面试题21:说一说Spring的@Required注解和@Qualifier注解
    地表水与地下水耦合模拟
    (40)STM32——OV2640摄像头实验
    第二次笔记: 无符号整数的表示和运算 有符号整数的表示和运算 原码 补码 反码 移码
    业务用例元模型-软件方法(下)第9章分析类图案例篇Part08
    Android之使用GirdLayoutManager时候给Item设置边距
  • 原文地址:https://blog.csdn.net/niTaoTaoa/article/details/126351459