JVM 之 性能调优
目录
3.针对JVM堆的设置,一般可以通过-Xms -Xmx限定其最小、最大值。
6.在配置较好的机器上(比如多核、大内存),选择适当的收集算法
性能调优
需要哪些基本知识
1.相关基础知识(JVM知识)
2.分析的数据(程序产生的)
3.分析数据的工具(虚拟机监控工具)
4.经验
优化思路
1.优化程序的SQL
2.监控CPU运行状态
3.监控内存使用状态
4.网络通信
5.操作系统自身问题
常见问题
1.频繁Full GC
Full GC:
会对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个堆进行回收,所以比较慢,因此应该尽可能减少Full GC的次数
导致Full GC的原因及解决方法:
1)年老代(Tenured)被写满
调优时尽量让对象在新生代GC时被回收、让对象在新生代多存活一段时间和不要创建过大的对象及数组避免直接在旧生代创建对象 。
2)持久代Pemanet Generation空间不足
增大Perm Gen空间,避免太多静态对象 , 控制好新生代和旧生代的比例
3)System.gc()被显示调用
垃圾回收不要手动触发,尽量依靠JVM自身的机制
Survivor空间太小导致Eden区很多对象放不下,这样老年代占用增长会很快。
jmap -heap pid 查看具体参数设置,关注参数:SurvivorRatio(eden:survivor)
JDK8使用parallel GC作为默认GC算法。该算法使用了AdaptiveSizePolicy策略,每次GC后重新计算新生代各区大小。依据是:GC过程中统计的GC时间、吞吐量、内存占用量。
关闭方法:
1. JVM参数关闭:-XX:-UseAdaptiveSizePolicy
2. 老年代使用CMS,CMS不会开启这个策略,新生代开启parnew GC。
-XX:+UseConcMarkSweepGC 使用CMS收集器
-XX:+ UseCMSCompactAtFullCollection Full GC后,进行一次碎片整理;整理过程是独占的,会引起停顿时间变长
-XX:+CMSFullGCsBeforeCompaction 设置进行几次Full GC后,进行一次碎片整理
-XX:ParallelCMSThreads 设定CMS的线程数量(一般情况约等于可用CPU数量)
-XX:+CMSScavengeBeforeRemark 在remark之前强制进行一次Young GC。
每个JDK版本都会有默认GC算法,具体查看 这里
2.对堆外内存估算少
Java程序占用内存分为堆内存和非堆内存,堆内存里分新生代和老年代,非堆内存包含方法区、jdk8的MetaSpace、栈空间、程序计数器等
在设置堆内存时,要考虑非堆内存(其他程序+其他区+操作系统共用内存)。否则会宕机。
解决方法:
1.设置Xmx和Xms时要为堆外内存留出足够的空间,建议大于1G,如果内存资源允许大于2G。
优化JVM参数。
a) 对于接口类型的应用,使用CMS回收器,减少停顿时间,保证接口性能。
b) 对于后台定时任务,MQ消费监听等类型的应用,使用默认的Parallel Scavenge+Parallel Old回收器,优先保证吞吐量。并关闭UseAdaptiveSizePolicy参数。
-XX:SurvivorRatio=6
-XX:-UseAdaptiveSizePolicy
3.针对JVM堆的设置,一般可以通过-Xms -Xmx限定其最小、最大值。
为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,我们通常把最大、最小设置为相同的值。
4.根据实际情况,设置年轻代与老年代内存比例
年轻代和年老代将根据默认的比例(1:2)分配堆内存。
可以通过调整二者之间的比率NewRadio来调整二者之间的大小。也可以针对回收代,比如年轻代,通过 -XX:newSize -XX:MaxNewSize来设置其绝对大小。
同样,为了防止年轻代的堆收缩,我们通常会把-XX:newSize -XX:MaxNewSize设置为同样大小。
5.合理设置年轻代和老年代大小
更大的年轻代必然导致更小的年老代。
大的年轻代会延长普通GC的周期,但会增加每次GC的时间;小的年老代会导致更频繁的Full GC。
更小的年轻代必然导致更大年老代。
小的年轻代会导致young GC很频繁,但每次的GC时间会更短;大的年老代会减少Full GC的频率,但是会增加老年代的gc时间
如何选择应该依赖应用程序对象生命周期的分布情况:
a)如果应用存在大量的临时对象,应该选择更大的年轻代;
b)如果存在相对较多的持久对象,年老代应该适当增大。
但很多应用都没有这样明显的特性,在抉择时应该根据以下两点:
(A)本着Full GC尽量少的原则,让年老代尽量缓存常用对象,JVM的默认比例1:2也是这个道理
(B)通过观察应用一段时间,看应用在峰值时年老代会占多少内存,在不影响Full GC的前提下,根据实际情况加大年轻代,比如可以把比例控制在1:1。但应该给年老代至少预留1/3的增长空间。
6.在配置较好的机器上(比如多核、大内存),选择适当的收集算法
默认的GC算法不一定是最适合的。所以根据实际情况选择适当的GC算法
使用方法:
-XX:+UseParallelOldGC
7.线程堆栈的设置
每个线程默认会开启1M的堆栈,用于存放栈帧、调用参数、局部变量等,对大多数应用而言这个默认值太了,一般256K就足用。理论上,在内存不变的情况下,减少每个线程的堆栈,可以产生更多的线程,但这实际上还受限于操作系统。
最大线程数计算公式:
(MaxProcessMemory – JVMMemory – ReservedOsMemory) / (ThreadStackSize) = Number of threads
注:
MaxProcessMemory:进程最大寻址空间。(一般为服务器内存)
JVMMEMORY:JVM的内存空间(堆+永久区)即-Xmx大小 (应该是实际分配大小)
ReservedOsMemory:操作系统预留内存
ThreadStackSize:-Xss大小
8.可以通过下面的参数打Heap Dump信息
-XX:HeapDumpPath
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:/usr/aaa/dump/heap_trace.txt
通过下面参数可以控制OutOfMemoryError时打印堆的信息
-XX:+HeapDumpOnOutOfMemoryError
本文摘自:
性能调优
上一篇: 学习笔记-Comet
下一篇: IM 系统架构与开发的相关知识