调优的一些原则

1.多数的java应用不需要在服务器上GC(垃圾回收)优化,虚拟机内部已有很多优化来保证应用的稳定运行,所以不要为了调优而调优,不当的调优可能适得其反

2.在应用上线之前,先考虑机器的JVM参数设置到最优(合适)

3.在进行GC优化之前,需要确认项目的架构和代码等已经没有优化空间。我们不能指望一个系统 架构有缺陷,或者代码层次优化没有穷尽的应用,通过GC优化令其性能达到一个质的飞跃

4.GC优化是一个系统而复杂的工作,没有万能的调优策略可以满足所有的性能指标。GC优化必须 建立在我们深入理解各种垃圾回收器的基础上,才能有事半功倍的效果

5.处理吞吐量和延迟问题时,垃圾处理器能使用的内存越大,即java堆空间越大,垃圾收集效果越 好,应用运行也越流畅,这称之为GC内存最大化原则

6.在这三个属性(吞吐量、延迟、内存)中选择其中两个JVM调优,称之为GC调优3选2

什么情况下需要调优

  • Heap内存(老年化)持续上涨到最大内存值
  • Full GC次数频繁
  • GC停顿(Stop World)时间过长(超过1秒,具体值按应用场景而定)
  • 应用出现Out Of Memory等内存异常
  • 应用出现Outof Direct MemoryError等内存异常(failed to allocate 16777216 byte(s) of direct memory (used:1056964615,max:1073741824))
  • 应用中有使用本地缓存且占用大量内存空间
  • 系统吞吐量与响应性能不高或下降
  • 应用的CPU占用过高不下或内存占用过高不下

调优前需要知道的一些概念

1.吞吐量:用户代码时间/(用户代码执行时间+垃圾回收时间)。是评价垃圾收集器能力的重要指标之一,是不考虑垃圾收集引起的停顿时间或内存消耗,垃圾收集器能支撑应用程序达到的最高性能指标。吞吐量越高算法越好。(吞吐量99/100意味着100秒的程序执行时间应用程序线程运行了99秒, 而在这一时间段内GC线程只运行了1秒。)

2.低延迟:STW越短,响应时间越好。评价垃圾收集器能力的重要指标,度量标准是缩短由于垃圾收集引起的停顿时间或完全消除因垃圾收集所引起的停顿,避免应用程序运行时发生抖动。暂停时间越短算法越好。(STW:指的是GC事件发生过程中,会产生应用程序的停顿。停顿产生时整个应用程序线程都会被暂停,没有任何响应,有点像卡死的感觉,这个停顿称为STW

3.在设计(或使用)GC算法时,我们必须确定我们的目标:一个GC算法只可能针对两个目标之一(即只专注于最大吞吐量或最小暂停时间),或尝试找到一个二者的折中

4.Minor GC尽可能多的收集垃圾对象,我们把这个称作Minor GC原则,遵守这一原则可以降低应用程序Full GC的发生频率。Full GC较耗时,是应用程序无法达到延迟要求或吞吐量的罪魁祸首

5.堆大小调整的着手点,分析点:

(1)统计Minor GC持续时间

(2)统计Minor GC的次数

(3)统计Full GC的最长持续时间

(4)统计最差情况下Full GC频率

(5)统计GC持续时间和频率对优化堆的大小是主要着手点

(6)我们按照业务系统对延迟和吞吐量的要求,在按照这些分析我们可以进行各个区大小的调整

JVM常用参数解读

1.Xms:是指设定程序启动时占用内存大小,一般来讲,大点,程序会启动快一点,但是也可能会导致机器暂时变慢

2.Xmx:是指设定程序运行期间最大可占用的内存大小,如果程序运行需要占用更多的内存,超出了这个设置值,就会抛出Out Of Memory异常

3.Xss:是指设定没个线程的堆大小。这个就要依据你的程序,看一个线程大约需要占用多少内存,可能会有多少线程同事运行等

4.-Xmn、-XX:NewSize/-XX:MaxNewSize、-XX:NewRatio

        <1>高优先级:-XX:MaxNewSize

        <2>中优先级:-Xmn(默认等级 -Xmn=-XX:MaxNewSize=-XX:MaxNewSize=?)

        <3>低优先级:-XX:NewRatio

5.如果想在日志中追踪类加载与卸载的情况,可以使用启动参数 -XX:TraceClassLoading-XX:XX:TraceClassUnLoading

常用性能调优工具

1.MAT

2.jvisualvm

3.jconsole

4.Arthas

5.show-busy-java-threads

线上排查问题的一般流程

1.CPU占用过高排查流程

(1)利用top命令可以查出占用CPU最高的进程pid,如果pid为9876

(2)然后查看该进程下占用最高的线程id【top -Hp 9876】

(3)假设占用率最高的线程ID为6900,将其转换为16进制形式(因为java native线程以16进制形式输出)【printf'%x\n' 6900】

(4)利用jstack打印出java线程调用栈信息【jstack 9876 | grep '0x1af4' -A 50 --color】,这样可以更好定位问题

2.内存占用过高排查流程

(1)查找进程id:【top -d 2-c】

(2)查看JVM堆内存分配情况:jmap -head pid

(3)查看占用内存比较多的对象:jmap -histo pid | head -n 100

(4)查看占用内存比较多的存活对象:jmap -histo:live pid | head -n 100

垃圾识别算法

1.引用计数算法
引用计数算法是判断对象是否存活的算法之一:它给每一个对象加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能被使用的,即将被垃圾回收器回收。

缺点:
1.他需要单独的字段存储计数器,这样的做法增加了存储空间的开销。
2.无法解决对象减互相循环引用的问题。即当两个对象循环引用时,引用计数器都为1,当对象周期结束后应该被回收却无法回收,造成内存泄漏。

2.可达性分析算法
目前主流使用的都是可达性分析算法来判断对象是否存活。算法基本思路:以“GC Roots”作为对象的起点,从此节点开始向下搜索,搜索所走过的路径成为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的

哪些对象可作为GC Roots?
虚拟机栈(栈帧中的本地变量表)中引用的对象;
方法区中类静态属性引用的对象;
方法区中常量引用的对象;
本地方法栈中JNI(Native方法)引用的对象;
活跃线程的引用对象。