JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM包括即时编译(JIT)、内存管理(垃圾回收GC技术)和Runtime技术,其中GC调优是性能调优中应用最为广泛。本次调优思路主要根据自己手中的项目针对GC展开说明。
JVM是Java Virtual Machine(Java虚拟机)的缩写,Java代码再不同平台上运行时不需要重新编译,Java语言使用JVM屏蔽了与具体平台相关的硬件指令差异,使得Java语言编译程序只需生成在JVM上运行的字节码,实现在多种平台上不加修改的运行。JVM包括即时编译(JIT)、内存管理(垃圾回收GC技术)和Runtime技术,其中GC调优是性能调优中应用最为广泛。本次调优思路主要根据自己手中的项目针对GC展开说明:
1.首先优选尽可能高的JDK版本,高版本具有更新的特性和优化,对Java程序性能有好处。
2.其次根据试剂业务场景和硬件资源给JVM选择合理的对空间。
3.最后要选择合理的GC算法。
常用性能监测工具
目前常用的虚拟机性能监测工具主要是 jstat、jmap、jinfo、jps、jstack、jconsole。
top指令:查看当前所有进程的使用情况,CPU占有率、内存使用情况、服务器负载状态等参数。
jps:与linux上的ps类似,用于查看有权访问的虚拟机进程,可以查看本地运行的Java程序,并显示他们的进程号。
jinfo:可以输出并修改运行时的Java进程的一些参数。
jstat:可以用来监视JVM内存内的各种堆和非堆的大小及其内存使用量。
jstack:堆栈跟踪工具,一般用于查看某个进程包含线程的情况。
jmap:打印出来某个Java进程内存内的所有对象情况,一般用于查看内存占用情况。
jconsole:一个Java GUI监视工具,可以一图变化的形式显示各种数据,并可以通过远程连接监视远程服务器的JVM进程。
jvisualVM:另外一种Java GUI监视工具,拥有和jconsole相似的功能,但是更强大的是jvisualVM能够产生dump文件,相当于拥有jmap工具的功能,并且能够分析jmap生成的dump文件。需要手动下载添加该工具插件。
jstat工具
jstat是JDK自带的JVM统计监控工具,利用JVM内建的指令对Java应用程序的资源和性能进行实时的命令行的监控,包括堆大小和应用程序GC状况的监控。无需单独安装,一般位于JDK包的bin目录下。
GC优化中,利用jstat -gc
参数 | 说明 |
---|---|
S0C | 年轻代中第一个survivor区的容量(字节) |
S1C | 年轻代中第二个survivor区的容量(字节) |
S0U | 年轻代中第一个survivor区目前已使用空间(字节) |
S1U | 年轻代中第二个survivor区目前已使用空间(字节) |
EC | 年轻代中Ede区的容量(字节) |
EU | 年轻代中Ede区已使用空间(字节) |
OC | 老年代的容量(字节) |
OU | 老年代已使用空间(字节) |
PC | Perm(永久代)的容量(字节) |
PU | Perm(永久代)已使用空间(字节) |
YGC | 从应用程序启动到采样是年轻代中gc次数 |
YGCT | 从应用程序启动到采样是年轻代中gc所用时间(s) |
FGC | 从应用程序启动到采样是老年代(全gc)gc次数 |
FGCT | 从应用程序启动到采样是老年代(全gc)gc所用时间(s) |
GCT | 从程序启动到采样时gc用的总时间(s) |
jmap工具
jmap是JDK自带的堆信息查看和调试工具,可以将dump信息导出到文件分析,可以查看堆空间分配等信息,是Java性能调优的常用工具之一。无需单独安装,JDK自带,一般位于Java的bin目录下。
常用使用参数如下
| 参数 | 说明 |
| — | — |
| -dump:[ live ],format=b,file=/you/path/filename.hprof < pid > | 输出jvm的堆对象内容到指定文件。推荐以.hprof后缀命名文件,可以用MAT工具直接分析。 live选项是可选的,如果选live,那么只输出活的对象到文件 |
| -finalizerinfo < pid > | 输出正等候回收的对象的信息 |
| -heap < pid > | 输出当前java进程堆的概要统计信息,如GC算法,heap的配置空间等 |
| -histo[ :live ] < pid > | 输出当前class的实例数目、内存占用、类全名信息 |
| -permstat < pid > | 打印classload和jvm heap中perm代的信息,包含每个classloader的名字、活泼性、地址、父classloader和加载的class数量等信息 |
JVM原理及配置建议
设置JVM对空间大小
原理
JVM在执行java程序是会把他多管理的内存划分为若干个不同的运行时数据区域,主要包括:程序计数器、方法区、虚拟机栈、本地方法栈和堆:
1.程序计数器可以看做是当前线程所执行的字节码的行号指示器。
2.方法区用于存储被JVM加载的类信息、常量、静态变量等数据。
3.虚拟机栈存储的是Java方法执行的线程内存模型,每一个方法别调用到执行完毕的过程,对对应一个栈帧再虚拟机栈中从入栈到出栈的过程。
4.本地方法栈和虚拟机栈的功能相同,差别是本地方法栈只为本地方法调用服务。
5.堆是JVM管理内存中占用比例最大的一块,用于存储Java程序对象示例,几乎所有的对象实例内存都在这里分配。从内存回收角度和经典垃圾收集器分代理论来看,堆内存空间一般分为:新生代、老年代、永久代(Java8+取消Perm区,新增Metaspace元空间区域)、Eden空间、From/Survivor等区。
针对堆空间的优化是Java性能调优的重点之一。如果没有这是JVM对空间大小,JVM会根据服务器物理内存大小设置默认堆大小的值。例如:在64位的服务器端,当物理内存小于192MB时,JVM堆大小默认选为物理内存的一半;当物理内存大于192MB且小于128G时,JVM堆大小默认选为物理内存的1/4,当物理内存大于等于128G时,都为32G;
应用程序选用多大的堆空间大小及配比,一般要根据程序的GC情况和服务器内存资源进行综合评估,是个循序渐进不断优化的过程,如果GC频繁触发,可以尝试增加空间缓存。通常情况下,Java程序会通过参数指定堆大小。
配置推荐配置原则
1.应用程序运行时,计算老年代存活对象的占用空间大小X。程序整个堆大小(Xmx和Xms)设置为X的3 - 4倍。永久代PermSize和MaxPermSize设置为X的1.2 - 1.5倍,老年代内存大小设置为X的2 - 3 倍。
2.JDK官方建议年轻代占整个对大小空间的3/8左右。
配置方式
参数 | 说明 |
---|---|
-Xmx | 设置JVM最大可用堆内存大小 |
-Xms | 设置初始堆大小,一般与Xmx保持一致 |
-Xmm | 设置年轻堆大小 |
-Xss | 设置每个线程的堆大小。JDK 1.5以后每个线程堆栈大小默认为1MB,1.5以前为256K |
-XX:NewRotio= | 设置年轻代(包括Eden和两个Survivor区)与老年代的比值(不包括永久代),如设置为4,则年轻代与老年代所占比值为1:4,年轻代占整个堆栈的1/5 |
-XX:SurvivorRotio= | 设置年轻代中Eden区与Survivor区的大小比值。如设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6 |
-XX:MaxPermSize | 设置永久代对大小 |
选择合适的垃圾回收器
原理
垃圾回收器是内存回收的具体实现,JDK自带的垃圾回收器仪器完成集成垃圾回收和清理算法、业务程序可以通过设置参数选择垃圾回收期,虚拟机用到的7中经典的垃圾回收器如下表。根据使用内存区域不同,JDK自带的垃圾回收期可分为新生代回收器和老年代回收器,两者可以配合使用。新生代回收器用于堆空间中新生代区域的垃圾回收,老年代回收器用于堆空间中老年代区域的垃圾回收。G1是一种新型的堆内垃圾收集器。既可以用于新生代也可以用于老年代垃圾回收。
名称 | 说明 | 收集模式 | 分代适用类型 |
---|---|---|---|
Serial | 单线程串行收集器 | 串行收集器 | 新生代 |
ParNew | 多线程并行Serial收集器 | 并行收集器 | 新生代 |
Parallel Scavenge | 并行吞吐量优先收集器 | 并行收集器 | 新生代 |
Serial Old | Serial单线程收集器老年代版 | 并发收集器 | 老年代 |
CMS(Consurrent Mark Sweep) | 并行最短停顿时间收集器 | 并发收集器 | 老年代 |
Parallel Old | Parallel Scavenge 并行收集器来年代版本 | 并行收集器 | 老年代 |
G1 | 面向局部收集和基于Region内部布局的信息低延时收集器 | 并发/并行收集器 | 新生代/老年代 |
下图展示新生代GC和老年代GC配合使用的方法,连线表示可以配合使用。
根据收集模式经典垃圾回收器可分为三类:串行收集器、并行收集器、并发收集器
串行收集器:使用单线程处理所有垃圾回收工作,因为无需多线程交互,所以效率比较高。但是无法使用多处理器的优势,所以次收集器适合单处理器及其,适合用在小数据量(100MB左右)情况下的多处理器机器上,可以使用 -XX:+UseSerialGC打开。
并行收集器:也称为吞吐量收集器,是一种类似于串行收集器的分代收集器。与串行相比的主要区别是有多个线程,加速垃圾收集。可以设置并行垃圾回收的线程数、指定垃圾回收时的最长暂停时间、设置吞吐量(垃圾回收时间与非垃圾回收时间的比值)。
并发收集器:指多条垃圾收集线程与用户线程同时进行(不一定并行,可能交替工作),如典型的CMS(并发标记清除收集器)。垃圾回收只暂停很少的时间,此收集器适合对响应时间要求比较高的中、大规模应用。
垃圾回收器选择建议
1.完成一次Full GC后,应该释放出70%的堆空间。
2.YGC平均耗时小于500ms,执行频率不低于10s/次。
3.FGC平均耗时小于1s,执行频率不低于10min/次。
4.业务应用对吞吐量要求较高,对响应时间没有特别要求的,推荐使用并行收集器。如科学计算和后台处理程序等。
5.对响应时间要求较高的中大型应用程序,推荐使用并发收集器。如web服务器等。
6.对应JDK版本1.8版本以上,多CPU处理器且内存资源不是瓶颈,建议有限考虑G1回收器。
7.单线程应用使用串行收集器。
根据以上只是,大概已经了解了JVM性能优化的基本原理和基本工具的使用以及基本的优化思路,下面就可以根据自己的程序进行优化啦!!