竹笋

首页 » 问答 » 灌水 » 深入理解JVM虚拟机JVM参数以及调
TUhjnbcbe - 2024/2/26 22:24:00

目录

一.JVM优化的必要性

二.JVM调优原则

三.JVM运行参数设置

四.JVM性能调优工具

五.常用调优策略

六.JVM调优实例

七.一个具体的实战案例分析

JVM优化的必要性

1.1:项目上线后,什么原因使得我们需要进行jvm调优

1)、垃圾太多(java线程,对象占满内存),内存占满了,程序跑不动了!!2)、垃圾回收线程太多,频繁地回收垃圾(垃圾回收线程本身也会占用资源:占用内存,cpu资源),导致程序性能下降3)、回收垃圾频繁导致STW

因此基于以上的原因,程序上线后,必须进行调优,否则程序性能就无法提升;也就是程序上线后,必须设置合理的垃圾回收策略;

1.2:jvm调优的本质是什么??

答案:回收垃圾,及时回收没有用垃圾对象,及时释放掉内存空间

1.3:基于服务器环境,jvm堆内存到底应用设置多少内存?

1、32位的操作系统---寻址能力2^32=4GB,最大的能支持4gb;jvm可以分配2g+

2、64位的操作系统---寻址能力2^64=PB,高性能计算机(IBMZunixG+)

Jvm堆内存不能设置太大,否则会导致寻址垃圾的时间过长,也就是导致整个程序STW,也不能设置太小,否则会导致回收垃圾过于频繁;

1.4:总结

如果你遇到以下情况,就需要考虑进行JVM调优了:

Heap内存(老年代)持续上涨达到设置的最大内存值;

FullGC次数频繁;

GC停顿时间过长(超过1秒);

应用出现OutOfMemory等内存异常;

应用中有使用本地缓存且占用大量内存空间;

系统吞吐量与响应性能不高或下降。

JVM调优原则

JVM调优是一个手段,但并不一定所有问题都可以通过JVM进行调优解决,因此,在进行JVM调优时,我们要遵循一些原则:

大多数的Java应用不需要进行JVM优化;

大多数导致GC问题的原因是代码层面的问题导致的(代码层面);

上线之前,应先考虑将机器的JVM参数设置到最优;

减少创建对象的数量(代码层面);

减少使用全局变量和大对象(代码层面);

优先架构调优和代码调优,JVM优化是不得已的手段(代码、架构层面);

分析GC情况优化代码比优化JVM参数更好(代码层面);

通过以上原则,我们发现,其实最有效的优化手段是架构和代码层面的优化,而JVM优化则是最后不得已的手段,也可以说是对服务器配置的最后一次“压榨”。

2.1JVM调优目标

调优的最终目的都是为了令应用程序使用最小的硬件消耗来承载更大的吞吐。jvm调优主要是针对垃圾收集器的收集性能优化,令运行在虚拟机上的应用能够使用更少的内存以及延迟获取更大的吞吐量。

延迟:GC低停顿和GC低频率;

低内存占用;

高吞吐量;

其中,任何一个属性性能的提高,几乎都是以牺牲其他属性性能的损为代价的,不可兼得。具体根据在业务中的重要性确定。

2.2JVM调优量化目标

下面展示了一些JVM调优的量化目标参考实例:

Heap内存使用率=70%;

Oldgeneration内存使用率=70%;

avgpause=1秒;

Fullgc次数0或avgpauseinterval=24小时;

注意:不同应用的JVM调优量化目标是不一样的。

2.3JVM调优的步骤

一般情况下,JVM调优可通过以下步骤进行:

分析GC日志及dump文件,判断是否需要优化,确定瓶颈问题点;

确定JVM调优量化目标;

确定JVM调优参数(根据历史JVM参数来调整);

依次调优内存、延迟、吞吐量等指标;

对比观察调优前后的差异;

不断的分析和调整,直到找到合适的JVM参数配置;

找到最合适的参数,将这些参数应用到所有服务器,并进行后续跟踪。

以上操作步骤中,某些步骤是需要多次不断迭代完成的。一般是从满足程序的内存使用需求开始的,之后是时间延迟的要求,最后才是吞吐量的要求,要基于这个步骤来不断优化,每一个步骤都是进行下一步的基础,不可逆行之。

调优原则总结

JVM的自动内存管理本来就是为了将开发人员从内存管理的泥潭里拉出来。JVM调优不是常规手段,性能问题一般第一选择是优化程序,最后的选择才是进行JVM调优。

即使不得不进行JVM调优,也绝对不能拍脑门就去调整参数,一定要全面监控,详细分析性能数据。

附录:系统性能优化指导

JVM运行参数设置

3.1、堆参数设置

-XX:+PrintGC使用这个参数,虚拟机启动后,只要遇到GC就会打印日志

-XX:+UseSerialGC配置串行回收器

-XX:+PrintGCDetails可以查看详细信息,包括各个区的情况

-Xms设置Java程序启动时初始化堆大小

-Xmx设置Java程序能获得最大的堆大小

-Xmx20m-Xms5m-XX:+PrintCommandLineFlags可以将隐式或者显示传给虚拟机的参数输出

3.2、新生代参数配置

-Xmn可以设置新生代的大小,设置一个比较大的新生代会减少老年代的大小,这个参数对系统性能以及GC行为有很大的影响,新生代大小一般会设置整个堆空间的1/3到1/4左右

-XX:SurvivorRatio用来设置新生代中eden空间和from/to空间的比例。含义:-XX:SurvivorRatio=eden/from**/**eden/to

不同的堆分布情况,对系统执行会产生一定的影响,在实际工作中,应该根据系统的特点做出合理的配置,基本策略:尽可能将对象预留在新生代,减少老年代的GC次数

除了可以设置新生代的绝对大小(-Xmn),还可以使用(-XX:NewRatio)设置新生代和老年代的比例:-XX:NewRatio=老年代/新生代

配置运行时参数:

-Xms20m-Xmx20m-Xmn1m-XX:SurvivorRatio=2-XX:+PrintGCDetails-XX:+UseSerialGC

3.3、堆溢出参数配置

在Java程序在运行过程中,如果对空间不足,则会抛出内存溢出的错误(OutOfMemory)OOM,一旦这类问题发生在生产环境,则可能引起严重的业务中断,Java虚拟机提供了-XX:+HeapDumpOnOutOfMemoryError,使用该参数可以在内存溢出时导出整个堆信息,与之配合使用的还有参数-XX:HeapDumpPath,可以设置导出堆的存放路径

内存分析工具:MemoryAnalyzer

配置运行时参数-Xms1m-Xmx1m-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=d:/Demo3.dump

3.4、栈参数配置

Java虚拟机提供了参数-Xss来指定线程的最大栈空间,整个参数也直接决定了函数可调用的最大深度。

配置运行时参数:-Xss1m

3.5、方法区参数配置

和Java堆一样,方法区是一块所有线程共享的内存区域,它用于保存系统的类信息,方法区(永久区)可以保存多少信息可以对其进行配置,在默认情况下,-XX:MaxPermSize为64M,如果系统运行时生产大量的类,就需要设置一个相对合适的方法区,以免出现永久区内存溢出的问题

-XX:PermSize=64M-XX:MaxPermSize=64M

3.6、直接内存参数配置

直接内存也是Java程序中非常重要的组成部分,特别是广泛用在NIO中,直接内存跳过了Java堆,使用Java程序可以直接访问原生堆空间,因此在一定程度上加快了内存空间的访问速度

但是说直接内存一定就可以提高内存访问速度也不见得,具体情况具体分析

相关配置参数:-XX:MaxDirectMemorySize,如果不设置,默认值为最大堆空间,即-Xmx。直接内存使用达到上限时,就会触发垃圾回收,如果不能有效的释放空间,就会引起系统的OOM

3.7、对象进入老年代的参数配置

一般而言,对象首次创建会被放置在新生代的eden区,如果没有GC介入,则对象不会离开eden区,那么eden区的对象如何进入老年代呢?

通常情况下,只要对象的年龄达到一定的大小,就会自动离开年轻代进入老年代,对象年龄是由对象经历数次GC决定的,在新生代每次GC之后如果对象没有被回收,则年龄加1

虚拟机提供了一个参数来控制新生代对象的最大年龄,当超过这个年龄范围就会晋升老年代

-XX:MaxTenuringThreshold,默认情况下为15

配置运行时参数:-Xmx64M-Xms64M-XX:+PrintGCDetails

结论:对象首次创建会被放置在新生代的eden区,因此输出结果中from和to区都为0%

根据设置MaxTenuringThreshold参数,可以指定新生代对象经过多少次回收后进入老年代。另外,大对象新生代eden区无法装入时,也会直接进入老年代。

JVM里有个参数可以设置对象的大小超过在指定的大小之后,直接晋升老年代-XX:PretenureSizeThreshold=15

参数:-XmxM-XmsM-XX:+UseSerialGC-XX:MaxTenuringThreshold=15-XX:+PrintGCDetails

使用PretenureSizeThreshold可以进行指定进入老年代的对象大小,但是要注意TLAB区域优先分配空间。虚拟机对于体积不大的对象会优先把数据分配到TLAB区域中,因此就失去了在老年代分配的机会

参数:-Xmx30M-Xms30M-XX:+UseSerialGC-XX:+PrintGCDetails-XX:PretenureSizeThreshold=-XX:-UseTLAB

3.8、TLAB参数配置

TLAB全称是ThreadLocalAllocationBuffer,即线程本地分配缓存,从名字上看是一个线程专用的内存分配区域,是为了加速对象分配对象而生的。每一个线程都会产生一个TLAB,该线程独享的工作区域,Java虚拟机使用这种TLAB区来避免多线程冲突问题,提高了对象分配的效率

TLAB空间一般不会太大,当大对象无法在TLAB分配时,则会直接分配到堆上

-XX:+UseTLAB使用TLAB

-XX:+TLABSize设置TLAB大小

-XX:TLABRefillWasteFraction设置维护进入TLAB空间的单个对象大小,它是一个比例值,默认为64,即如果对象大于整个空间的1/64,则在堆创建对象

-XX:+PrintTLAB查看TLAB信息

-XX:ResizeTLAB自调整TLABRefillWasteFraction阈值

参数:-XX:+UseTLAB-XX:+PrintTLAB-XX:+PrintGC-XX:TLABSize=00-XX:-ResizeTLAB-XX:TLABRefillWasteFraction=-XX:-DoEscapeAnalysis-server

内存参数

四.JVM性能调优工具

这个篇幅在这里就不过多介绍了,可以参照:

深入理解JVM虚拟机——Java虚拟机的监控及诊断工具大全

五.常用调优策略

这里还是要提一下,及时确定要进行JVM调优,也不要陷入“知见障”,进行分析之后,发现可以通过优化程序提升性能,仍然首选优化程序。

5.1、选择合适的垃圾回收器

CPU单核,那么毫无疑问Serial垃圾收集器是你唯一的选择。

CPU多核,

1
查看完整版本: 深入理解JVM虚拟机JVM参数以及调