科普文:一文搞懂jvm实战(五)通用JVM调优

264 篇文章 0 订阅
139 篇文章 0 订阅

JVM调优概叙

科普文:一文搞懂jvm(一)jvm概叙-CSDN博客

科普文:一文搞懂jvm原理(二)类加载器-CSDN博客

科普文:一文搞懂jvm原理(三)执行引擎-CSDN博客

科普文:一文搞懂jvm原理(四)运行时数据区-CSDN博客

         前面我们介绍了jvm,关于jvm实战,将分成两部分:

  • 通用jvm参数:设置jvm内存和垃圾收集器

科普文:一文搞懂jvm实战(五)通用JVM参数配置-CSDN博客

  • jvm调优:带上场景要求的调优设置

        JVM调优是一项需要根据应用特性和运行环境细致调整的任务。调优方法和参数只是众多可能性中的一小部分。成功的JVM调优需要对应用的行为有深刻的理解,以及对JVM工作原理的充分了解。

        在实际操作中,建议逐步调整参数,并结合性能监控工具来评估调优效果。

        记住,随着应用的发展和JVM技术的进步,调优是一个持续的过程。

1.JVM调优的重要性

  • 提高性能:合理的调优可以显著提高应用的响应速度和吞吐量。
  • 优化资源利用:使应用更高效地利用系统资源,减少资源浪费。
  • 增强稳定性:避免内存泄漏和崩溃,确保应用的稳定运行。

2.JVM调优参数(JVM 8)

  • 堆内存设置-Xms 和 -Xmx 设置堆的起始大小和最大大小。
  • 垃圾收集器选择-XX:+UseG1GC 使用G1垃圾收集器。
  • 性能监控-XX:+PrintGCDetails 打印垃圾收集细节。

3.在代码层面:

避免过度创建对象:频繁创建和销毁对象会增加垃圾回收的负担。优化代码,避免不必要的对象创建,尽量复用对象或使用对象池技术。

使用合适的集合类型:选择合适的集合类型可以提高性能。例如,如果需要高效的插入和删除操作,可以使用LinkedList;如果需要快速的查找操作,可以使用HashMap。

优化循环操作:减少循环操作中的方法调用和对象创建,尽量减少循环嵌套的层数,使用合适的循环方式(如增强型for循环、迭代器等)。

避免过度同步:过多的同步操作可能导致性能下降。在多线程环境下,使用合适的同步机制,避免不必要的锁竞争和阻塞。

使用合适的算法和数据结构:选择合适的算法和数据结构可以提高代码的执行效率。在解决问题时,考虑使用高效的算法和数据结构,减少不必要的计算和存储开销。

4.在架构方面:

水平扩展:通过将负载分布到多台服务器上,实现应用程序的水平扩展。合理设计系统架构,采用负载均衡、分布式缓存等技术,提高系统的可伸缩性和吞吐量。

异步处理:采用异步处理方式可以提高系统的响应速度和并发能力。例如,使用消息队列、异步任务等技术,将耗时操作异步化,避免阻塞。

缓存优化:合理使用缓存可以减少对后端资源的访问,提高系统性能。根据应用程序的特点,选择合适的缓存策略和缓存数据结构,避免缓存过期和一致性问题。

分布式设计:对于分布式系统,考虑数据的分片和分区,减少数据的跨节点访问,提高系统的并发处理能力和容错性。

监控和调优:建立系统监控和性能分析机制,对系统进行实时监测和调优。通过监控指标和日志分析,发现系统瓶颈和潜在问题,及时进行优化和调整。

5.系统参数方面:

堆内存大小(Heap Memory):堆内存是Java应用程序运行时分配对象的主要区域。根据应用程序的内存需求,调整堆内存的大小可以避免内存溢出或减少频繁的垃圾回收。可以通过设置-Xms和-Xmx参数来指定堆的初始大小和最大大小。

垃圾回收器(Garbage Collector):垃圾回收器负责回收不再使用的内存,以便为新的对象腾出空间。选择合适的垃圾回收器算法和参数可以平衡吞吐量、停顿时间和内存占用。常见的垃圾回收器包括Serial、Parallel、CMS、G1等。可以通过设置-XX:+Use参数来选择使用特定的垃圾回收器。

并发线程数(Concurrency Threads):并发线程数配置与应用程序的并发性能密切相关。通过调整线程池大小、线程栈大小和线程并发度,可以充分利用多核处理器和提高应用程序的并发处理能力。

JIT编译器(Just-In-Time Compiler):JIT编译器将热点代码编译为本地机器代码,以提高执行效率。通过调整JIT编译器的参数,如编译阈值、内联策略等,可以优化代码的执行性能。可以使用-XX:+PrintCompilation参数打印编译日志进行分析。

内存区域划分(Memory Regions):JVM将堆内存划分为不同的区域,如新生代、老年代、永久代/元空间等。通过调整这些区域的大小和比例,可以优化内存的分配和回收效率,以及减少垃圾回收的影响。

监控和调优工具(Monitoring and Profiling Tools):使用监控和调优工具可以帮助分析JVM的运行状态、内存使用情况、垃圾回收情况等。常见的工具包括VisualVM、jstat、jmap、jstack等。通过这些工具获取性能数据和线程信息,可以帮助识别性能瓶颈和优化点。

6.JVM调优目标

提高性能:JVM调优旨在提升Java应用程序的执行性能。通过合理的配置和调整,减少资源的浪费,优化代码的执行效率,提高应用程序的响应速度和吞吐量。

减少内存消耗:JVM调优可以帮助优化内存管理,减少内存的占用和泄漏。通过合理配置堆内存、选择合适的垃圾回收策略,避免内存溢出和过多的垃圾回收,降低应用程序的内存消耗。

优化垃圾回收:垃圾回收是JVM的重要功能,但不恰当的垃圾回收策略可能导致长时间的停顿和性能下降。JVM调优旨在选择合适的垃圾回收器、调整垃圾回收参数,平衡吞吐量和停顿时间,减少垃圾回收对应用程序的影响。

提高稳定性和可伸缩性:通过调优JVM参数和配置,可以提高应用程序的稳定性和可伸缩性。合理的线程管理、内存区域划分、并发配置等可以充分利用系统资源,提高应用程序的并发处理能力和可扩展性。

减少资源消耗:JVM调优可以减少不必要的资源消耗,如CPU、内存、网络等。通过优化代码和配置,避免资源的浪费,提高系统的资源利用率,降低运行成本

7.JVM调优量化目标

可以量化以下几个目标来评估和衡量优化的效果:
响应时间(Response Time):优化后的JVM应该能够减少应用程序的响应时间,即提高用户请求的处理速度。通过减少垃圾回收暂停时间、优化代码执行效率等方式,可以降低请求的处理时间,提高系统的响应性能。

吞吐量(Throughput):优化后的JVM应该能够提高系统的吞吐量,即单位时间内能够处理的请求数量。通过合理配置垃圾回收策略、调整线程池大小等方式,可以增加系统的并发处理能力,提高请求的吞吐量。

内存占用(Memory Footprint):优化后的JVM应该能够减少内存的占用和泄漏,有效利用可用的内存资源。通过合理设置堆内存大小、优化垃圾回收机制,可以降低内存的消耗,提高系统的稳定性和可伸缩性。

垃圾回收频率(Garbage Collection Frequency):优化后的JVM应该能够减少垃圾回收的频率,减少系统因垃圾回收而产生的停顿时间。通过选择合适的垃圾回收器算法和参数,调整垃圾回收策略,可以降低垃圾回收的频率,提高系统的运行效率。

CPU利用率(CPU Utilization):优化后的JVM应该能够更有效地利用CPU资源,提高系统的CPU利用率。通过优化代码的执行效率、合理设置线程池大小和线程并发度,可以充分利用多核处理器的能力,提高系统的并发性能

8.在jvm调优之前,我们需要达成下面共识:

        1.jvm调优,不是修改一下几个jvm参数,程序性能立马就能快的飞起。

        2.jvm调优,不是仅仅只是修改一下几个jvm参数,很多时候还需要调整代码、甚至是业务处理逻辑。

        3.jvm调优,需要带有目的性,否则jvm调优就是耍流氓。

        4.jvm调优,是监控+现场分析+jvm参数调整+代码优化+压力测试+调整论证的过程。

        5.jvm默认参数值是jvm官方测试论证的经验值,通常这些参数默认值是比较靠谱和通用的,不大会出问题。

        6.很多业务的性能瓶颈不在jvm上,毕竟他们业务的QPS不过百,数据不过百万,这种低压业务场景下,如果jvm还出问题,大概率是代码问题。

        7.禁忌过早优化,但是要在核心业务上加上jvm监控和告警,有jvm监控有异常或者告警时,先进行现场分析排查。

比如yong gc次数,以及单次yong gc耗时,full gc次数,以及单次full gc耗时,

以jstat -gcutil 统计为例

YGC :从应用程序启动到采样时年轻代中gc次数 yong gc平次控制在 每秒钟不超过1次
YGCT :从应用程序启动到采样时年轻代中gc所用时间(s)  yong gc耗时在100ms以内,小于50ms更佳
FGC :从应用程序启动到采样时old代(全gc)gc次数   full gc 每小时不超过1次  最好时每天不超过1次
FGCT :从应用程序启动到采样时old代(全gc)gc所用时间(s)  full gc单次耗时在1秒以内 小于 200ms更好
GCT:从应用程序启动到采样时gc用的总时间(s)

java进程问题分析工具,可以参考之前的文章

Java web应用性能分析之【java进程问题分析工具】_java 进程分析-CSDN博客

Java web应用性能分析之【java进程问题分析概叙】-CSDN博客

Java web应用性能分析之【java进程问题分析定位】-CSDN博客

    忌过早优化。《计算机程序设计艺术》的作者高德纳(Donald Ervin Knuth)曾说过一句经典的话:

The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming.

    真正的问题是,程序猿在错误的地方和错误的时间花了太多的时间担心效率问题;过早的优化是编程中所有(或者至少是大部分)罪恶的根源。

如何进行jvm调优

1.明确jvm调优目标

        jvm调优必须有一个明确的目标。首先通过分析定位业务服务的性能瓶颈。再通过性能瓶颈,明确提升业务服务性能,如:接口响应时间从1秒提升到500ms;QPS从1提升到10;

压测工具参考:Java web应用性能分析之【压测工具ab】_loadrunner压测是syn flooding攻击吗-CSDN博客

ab测试 百度qps=68

D:\Program Files\httpd-2.4.59-240404-win64-VS17\Apache24\bin>ab -n 1000  -c 100 -r  http://www.baidu.com/s?ie=UTF-8&wd=%E6%88%91%E7%88%B1%E4%B8%AD%E5%9B%BD

分析和定位当前系统的瓶颈

        对于JVM的核心指标,我们的关注点和常用工具如下:

1)CPU指标
  • 查看占用CPU最多的进程

  • 查看占用CPU最多的线程

  • 查看线程堆栈快照信息

  • 分析代码执行热点

  • 查看哪个代码占用CPU执行时间最长

  • 查看每个方法占用CPU时间比例

常见的命令:

// 显示系统各个进程的资源使用情况top

// 查看某个进程中的线程占用情况top -Hp pid

// 查看当前 Java 进程的线程堆栈信息jstack pid

常见的工具:JProfiler、JVM Profiler、Arthas等。
2)JVM 内存指标
  • 查看当前 JVM 堆内存参数配置是否合理

  • 查看堆中对象的统计信息

  • 查看堆存储快照,分析内存的占用情况

  • 查看堆各区域的内存增长是否正常

  • 查看是哪个区域导致的GC

  • 查看GC后能否正常回收到内存

常见的命令:

// 查看当前的 JVM 参数配置ps -ef | grep java
// 查看 Java 进程的配置信息,包括系统属性和JVM命令行标志jinfo pid
// 输出 Java 进程当前的 gc 情况jstat -gc pid
// 输出 Java 堆详细信息jmap -heap pid
// 显示堆中对象的统计信息jmap -histo:live pid
// 生成 Java 堆存储快照dump文件jmap -F -dump:format=b,file=dumpFile.phrof pid

常见的工具:Eclipse MAT、JConsole等。

3)JVM GC指标
  • 查看每分钟GC时间是否正常

  • 查看每分钟YGC次数是否正常

  • 查看FGC次数是否正常

  • 查看单次FGC时间是否正常

  • 查看单次GC各阶段详细耗时,找到耗时严重的阶段

  • 查看对象的动态晋升年龄是否正常

        JVM 的 GC指标一般是从 GC 日志里面查看,默认的 GC 日志可能比较少,我们可以添加以下参数,来丰富我们的GC日志输出,方便我们定位问题。

GC日志常用 JVM 参数:


// 打印GC的详细信息-XX:+PrintGCDetails
// 打印GC的时间戳-XX:+PrintGCDateStamps
// 在GC前后打印堆信息-XX:+PrintHeapAtGC
// 打印Survivor区中各个年龄段的对象的分布信息-XX:+PrintTenuringDistribution
// JVM启动时输出所有参数值,方便查看参数是否被覆盖-XX:+PrintFlagsFinal
// 打印GC时应用程序的停止时间-XX:+PrintGCApplicationStoppedTime
// 打印在GC期间处理引用对象的时间(仅在PrintGCDetails时启用)-XX:+PrintReferenceGC
4)小结--明确优化目标

        以上就是我们定位系统瓶颈的常用手段,大部分问题通过以上方式都能定位出问题原因,然后结合代码去找到问题根源。

定位出系统瓶颈后,在优化前先制定好优化的目标是什么,例如:

将FGC次数从每小时1次,降低到1天1次

将每分钟的GC耗时从3s降低到500ms

将每次FGC耗时从5s降低到1s以内

...

2.制定优化方案

        针对定位出的系统瓶颈制定相应的优化方案,常见的有:

  • 代码bug:升级修复bug。典型的有:死循环、使用无界队列、导入过大的xls文件导致频繁gc甚至oom、没有分页没有用游标直接select一张大表数据导致频繁gc甚至oom等等。

  • 不合理的JVM参数配置:优化 JVM 参数配置。典型的有:年轻代内存配置过小、堆内存配置过小、元空间配置过小。

3.论证+调整

        压测,对比优化前后的指标,统计优化效果。在达到调优目标前,可以持续调整、观察、跟踪优化效果。直到达到调优目标。

        备注:实在达不到调优目标,给出真实的最大验证结果,即当前性能瓶颈。如需提升,则需要增加服务器资源,或者需要调整业务方案。

jvm调优的步骤

        JVM调优是指对Java虚拟机进行优化,以提高Java应用程序的性能和稳定性。

        在进行JVM调优时,可以采取以下步骤:

  1. 监测和分析:使用性能分析工具,如JVisualVM、JProfiler等,监测和分析应用程序的性能瓶颈。了解哪些部分消耗了大量的内存或CPU资源。

  2. 调整堆内存:根据应用程序的内存需求和硬件资源的限制,适当调整Java堆的大小。可以通过设置-Xms和-Xmx参数来指定堆的初始大小和最大大小。

  3. 优化垃圾回收:选择合适的垃圾回收器和调整垃圾回收策略,以提高垃圾回收的性能。可以使用-XX:+UseParallelGC、-XX:+UseConcMarkSweepGC、-XX:+UseG1GC等参数来指定不同的垃圾回收器。

  4. 设置合适的线程数:根据应用程序的性质和硬件资源的限制,调整线程池的大小。可以使用-XX:ParallelGCThreads、-XX:ConcGCThreads等参数来指定垃圾回收线程的数量。

  5. 优化代码:通过优化代码,减少内存和CPU的使用。可以使用一些最佳实践,如使用局部变量而不是全局变量、避免频繁的对象创建和销毁、使用合适的数据结构等。

  6. 调整虚拟机参数:根据应用程序的性质和硬件资源的限制,调整虚拟机的参数。可以使用-XX:MaxPermSize、-XX:MaxTenuringThreshold、-XX:SurvivorRatio等参数来指定不同的虚拟机参数。

  7. 进行压力测试:通过使用压力测试工具,如JMeter、LoadRunner等,对应用程序进行压力测试。观察系统的响应时间、吞吐量和资源占用情况,以确定调优效果。

        以上是进行JVM调优的一般步骤,实际操作过程可能会因具体情况而异。在进行JVM调优时,需要注意监测和分析的准确性,尽量避免过度调优和过多优化,以免影响应用程序的稳定性和可维护性。

常见的jvm调优参数

        Java虚拟机(JVM)是Java应用运行的核心环境。

        JVM的性能优化对于提高应用性能、减少资源消耗和提升系统稳定性至关重要。

        下面将深入探讨JVM的调优方法和相关参数,以帮助开发者和系统管理员有效地优化他们的Java应用。
        JVM调优通常涉及到对堆内存、垃圾回收策略和线程堆栈大小等方面的调整。优化的目标是确保JVM在执行Java应用时能够高效地使用系统资源。

参考:【JVM调优】如何进行JVM调优?一篇文章就够了!-CSDN博客

1、堆内存调优

        使用场景:调整JVM的堆内存可以帮助避免内存溢出,提高垃圾回收的效率。

        代码示例: 启动Java应用时的堆内存设置:

# 设置初始堆大小和最大堆大小为1024MB
java -Xms1024m -Xmx1024m -jar your-application.jar

# 解释
-Xms1024m  # 设置JVM启动时的初始堆大小为1024MB
-Xmx1024m # 设置JVM可以使用的最大堆大小为1024MB


代码解释:

-Xms 参数用于设置JVM启动时的初始堆大小。

-Xmx 参数用于设置JVM可以使用的最大堆内存大小。

将初始化大小和最大大小设置为一直,这样的设置有助于避免应用在运行过程中频繁地进行内存分配。

同样在jdk1.8之后,元数据区也可以固定大小。

元空间(Metaspace)
-XX:MetaspaceSize=256m:设置初始元空间大小,元空间用于存放类元数据。
-XX:MaxMetaspaceSize=512m:设置最大元空间大小,以限制其无限增长可能导致的问题。

2、垃圾回收器选择和调优

        使用场景:合适的垃圾回收器能够提高应用的响应速度和吞吐量。

        代码示例:使用G1垃圾回收器启动Java应用:

垃圾回收器的选择,可以参考:科普文:一文搞懂jvm实战(五)通用JVM参数配置-CSDN博客

# 使用G1垃圾回收器
java -XX:+UseG1GC -jar your-application.jar

# 解释
-XX:+UseG1GC # 启用G1垃圾回收器

代码解释:

UseG1GC 参数用于启用G1垃圾回收器,它是适用于大堆内存并且需要低延迟的场景。


G1垃圾收集器的进一步优化
-XX:MaxGCPauseMillis=200:设置期望的最大GC暂停时间(毫秒),以便于优化延迟。
-XX:ParallelGCThreads=8:设置并行垃圾收集线程数。一般设置为可用CPU核心数。
-XX:ConcGCThreads=4:设置G1的并发标记线程数,一般为ParallelGCThreads的一

3、线程堆栈大小调优

        使用场景:合理的线程堆栈大小有助于提高线程创建和管理的效率。

        代码示例:设置线程堆栈大小:

# 设置线程堆栈大小为1MB
java -Xss1m -jar your-application.jar

# 解释
-Xss1m # 设置每个线程的堆栈大小为1MB


代码解释:

-Xss 参数用于设置每个线程的堆栈大小。

线程堆栈大小应根据应用的实际需求调整,避免过大消耗过多内存,或过小导致栈溢出。

继续探讨JVM调优的话题,下面提供三个更深入的实用示例,包括详细的代码和解释。


开启JVM的本地方法接口(JNI)检查
-Xcheck:jni:开启对JNI函数的检查,这有助于发现JNI相关的问题。

4、调整年轻代和老年代的比例

        使用场景:调整年轻代和老年代的比例可以根据应用的特性来优化垃圾回收行为,影响整体的垃圾回收效率。

        代码示例

# 设置年轻代和老年代的比例为1:2
java -XX:NewRatio=2 -jar your-application.jar

# 解释
-XX:NewRatio=2 # 设置老年代与年轻代的比例为2,即老年代是年轻代大小的两倍


代码解释:

-XX:NewRatio 参数用于设置老年代和年轻代的大小比例。

如果你的应用长时间运行并且主要进行老年代垃圾回收,增大这个比例可能会有帮助。

5、设置Survivor区比例

        使用场景:在年轻代中,Eden区和Survivor区的比例会影响对象晋升老年代的速度。根据应用的对象生命周期调整这个比例,可以优化内存管理。

        代码示例

# 设置Eden区和Survivor区的比例
java -XX:SurvivorRatio=8 -jar your-application.jar

# 解释
-XX:SurvivorRatio=8 # 设置Eden区与一个Survivor区的大小比例为8,即Eden是Survivor的8倍


代码解释:

-XX:SurvivorRatio 参数定义了年轻代中Eden区与Survivor区的大小比例。

适当调整这个比例可以优化对象在年轻代的存活时间,减少老年代的压力。

6、启用GC日志和调试

        使用场景:启用GC日志可以帮助你监控垃圾回收过程,并对性能问题进行诊断。

        代码示例

# 启用GC日志
java -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log -jar your-application.jar

# 解释
-XX:+PrintGCDetails         # 打印详细的GC日志
-XX:+PrintGCDateStamps      # 在GC日志中包含时间戳
-Xloggc:gc.log              # 指定GC日志输出文件

日志和监控
-XX:+PrintGCDetails:打印详细的GC日志。
-XX:+PrintGCDateStamps:为GC日志添加时间戳。
-Xloggc:/var/log/yourapp-gc.log:将GC日志写入指定文件。
-XX:+UseGCLogFileRotation:开启GC日志文件的轮替。
-XX:NumberOfGCLogFiles=5:指定GC日志文件的数量。
-XX:GCLogFileSize=20M:指定GC日志文件的大小。


代码解释:

启用这些GC日志参数可以提供关于垃圾回收的详细信息,包括每次GC的类型、时间、持续时间以及内存回收情况。

这些信息对于理解应用的内存使用模式和调整GC策略非常有用。

7、设置最大停顿时间目标

        使用场景:当应用需要低延迟时,可以设置JVM的最大停顿时间目标,这有助于减少垃圾回收造成的延迟。

        代码示例

# 设置最大停顿时间目标
java -XX:MaxGCPauseMillis=200 -jar your-application.jar

# 解释
-XX:MaxGCPauseMillis=200 # 设置垃圾回收的最大停顿时间为200毫秒

代码解释:

-XX:MaxGCPauseMillis 参数用于告诉垃圾回收器尽量在指定的时间内完成垃圾回收。

这对于需要低延迟的应用特别有用,如实时交互系统。

8、启用类数据共享

        使用场景:启用类数据共享(Class Data Sharing, CDS)可以减少JVM启动时间和内存消耗。

        代码示例

# 启用类数据共享
java -Xshare:on -jar your-application.jar

# 解释
-Xshare:on # 启用类数据共享来提高JVM启动速度和减少内存占用


代码解释:

-Xshare:on 参数用于启用CDS,这可以加快JVM的启动时间并减少运行时的内存占用。

适合在多个JVM实例运行相同应用的场景,例如容器化环境。

9、调整大对象直接进入老年代的阈值

        使用场景:对于那些创建了大量大对象的应用,调整这些大对象直接晋升到老年代的阈值,可以减少年轻代垃圾回收的次数。

        代码示例

# 设置大对象直接进入老年代的阈值
java -XX:PretenureSizeThreshold=1048576 -jar your-application.jar

# 解释
-XX:PretenureSizeThreshold=1048576 # 设置大对象(大于1MB)直接在老年代分配

​
代码解释:

-XX:PretenureSizeThreshold 参数用于设置一个大小阈值,超过这个大小的对象将直接在老年代分配内存。

这有助于减少大对象在年轻代中的分配和复制,特别是对于那些频繁创建和销毁大对象的应用。

10、调整GC日志文件的回滚和大小限制

        使用场景:当需要长时间收集GC日志进行分析时,管理日志文件的大小和回滚非常重要,以避免消耗过多磁盘空间。

        代码示例

shellCopy code
# 设置GC日志文件的回滚和大小限制
java -Xloggc:gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10M -jar your-application.jar

# 解释
-Xloggc:gc.log                  # 指定GC日志输出文件
-XX:+UseGCLogFileRotation       # 启用GC日志文件的回滚
-XX:NumberOfGCLogFiles=5        # 保留最近的5个GC日志文件
-XX:GCLogFileSize=10M           # 每个GC日志文件的最大大小为10MB

代码解释:

这些参数结合使用可以有效管理GC日志文件的大小和数量,避免单个日志文件过大,同时保留足够的历史数据供分析。

11、配置线程局部分配缓冲(TLAB)

        使用场景:TLAB可以减少多线程环境中对象分配的竞争。调整TLAB的大小可以优化线程对堆内存的使用,特别是在高并发场景下。

        代码示例

shellCopy code
# 配置TLAB的大小
java -XX:+UseTLAB -XX:TLABSize=64k -jar your-application.jar

# 解释
-XX:+UseTLAB          # 启用线程局部分配缓冲
-XX:TLABSize=64k      # 设置TLAB的大小为64KB


代码解释:

启用TLAB并调整其大小,可以帮助线程更高效地在堆上分配对象,减少了线程间的竞争。

TLAB的大小需要根据应用的具体情况进行调整。

12、使用并行垃圾回收器

        使用场景:对于需要高吞吐量的应用,如批处理或后台处理系统,使用并行垃圾回收器可以提高效率。

        代码示例

shellCopy code
# 使用并行垃圾回收器
java -XX:+UseParallelGC -XX:ParallelGCThreads=4 -jar your-application.jar

# 解释
-XX:+UseParallelGC        # 启用并行垃圾回收器
-XX:ParallelGCThreads=4   # 设置垃圾回收时使用的线程数为4


代码解释:

并行垃圾回收器在执行GC时会使用多个线程,这可以在多核处理器上显著提高垃圾回收的效率。

ParallelGCThreads 参数用于指定执行垃圾回收时并行线程的数量,通常设置为与CPU核心数相同。

13、JVM性能调优:

        设置JVM使Server模式、开启JVM字符串去重功能、禁用System.gc()的显式调用


-server 设置JVM使Server模式,特点是启动速度较慢,但运行时性能和内存管理效率很高,适用于生产环境。在具有64位能力的JDK环境下默认启用该模式。

-XX:+UseStringDeduplication:开启JVM字符串去重功能,有助于减少堆内存的占用。
-XX:+DisableExplicitGC:禁用System.gc()的显式调用,避免可能的性能问题。

14、启用并设置详细的类加载信息

-XX:+TraceClassLoading:启用类加载跟踪。
-XX:+TraceClassUnloading:启用类卸载跟踪。

15、场景预估:500万日活用户下单

        场景:500万日活用户,每个用户平均访问20来次的一个系统,按10%的付费转化率来计算,每天应该产生50万订单,50万订单一般集中在4小时,平均下来每秒几十个订单。

        当在高峰期的时候每秒有1000个下单请求:按3台机器算,每台机器抗300个请求左右。机器4核8G。

        估算内存:

每个订单按1kb,300个订单300kb
算上订单连带对象 订单库存等,放大20倍 查询对象再放大10倍

300kb*20*10=60mb  对象一秒后消失
1
系统给JVM内存是4G 我们分配给堆新生代1.5g 老年代1.5g 然后再给永久代256M内存,基本上这4G内存就差不多了,其他给JVM其他。

“-Xms3072M -Xmx3072M -Xmn1536M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M”
1
Minor GC
一秒60M那么我们差不多在25秒的时候就会占满新生代:
因为JDK1.6以后默认开启空间担保了的。主要就是比较 “老年代可用空间大小”和“历次Minor GC后进入老年代对象的平均大小”,刚开始肯定这个检查是可以通过的
1
2
由于设置了新生代各比例8:1:1将会在20秒的时候进入minor gc 过后会存在100m左右垃圾

-Xms3072M -Xmx3072M -Xmn1536M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -
XX:SurvivorRatio=8
1
2
由于s区每个区域150m 每次垃圾回收会有100m进入过来 由于动态年龄规划问题,这些对象会直接进入老年代。

调整新生代2g 老年代1g 这时同年龄问题已经解决

“-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -
XX:SurvivorRatio=8”
1
2
Full GC
由于有的对象还是会逃过15次年轻代GC进入老年代比如controller 得类等等需要长期存活的类

这个时候我们可以提前让这些对象进入老年代。让他们尽快不占用新生代内存。

“-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -
XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5”
1
2
大对象问题
由于这系统大对象会比较少,我们设置1M

“-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -
XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M”
1
2
大促时多久会执行一次Full GC
基本可以确认默认开启了空间担保参数。此外 现在 新生代 s1和s2区是200m

此外就是Minor GC过后可能存活的对象超过200MB放不下Survivor了,或者是一下子占到超过Surviovr的50%,此时 会有一些对象进入老年代中。 但是我们之前对新生代的JVM参数进行优化,就是为了避免这种情况,经过我们的测算,这种概率应该是很低的。 但是虽说是很低,也不能完全是是没有这种情况,比如某一次GC过后可能刚好机缘巧合有超过200MB对象,就会进入 老年代里。我们可以做一个假设,大概就是这个订单系统在大促期间,每隔5分钟会在Minor GC之后有一小批对象进入老年代, 大概200MB左右的大小。

每次Minor GC之前,都检查一下“老年代可用内存空间” < “历次Minor GC后升入老年代的平均对象大小”

设置了“-XX:CMSInitiatingOccupancyFaction”参数,比如设定值为92%,那么此时可能前面几个条件都没满 足,但是刚好发现这个条件满足了,比如就是老年代空间使用超过92%了,此时就会自行触发Full GC

可以大概估算一哈 差不多40分钟左右才有可能会产生full GC 目前就先估算
经过前面的推算,我们基本可知道,老年代大概有900MB的对象了,剩余可用空间仅仅只有100MB了,此时就会触发一次Full GC,这个时候新生代正好有200M进入过来。这种可能 但是概率相对较少。
1
2
“-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFaction=92 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0”
1
UseCMSCompactAtFullCollection 与 CMSFullGCsBeforeCompaction 是搭配使用的;前者目前默认就是true了CMSFullGCsBeforeCompaction 说的是,到底还要再执行多少次full GC才会做压缩。默认是0.

指定垃圾回收器
“-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -
XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC”
1
2
总结
根据业务合理分配老年代新生代比例。优化之前尽量去估算一下内存大概多少。根据新生代每次产生对象与GC后剩余对象来估算老年代存储大小。Full GC优化的前提是Minor GC的优化,Minor GC的优化的前提是合理分配内存空间,合理分 配内存空间的前提是对系统运行期间的内存使用模型进行预估

优化思路其实简单来说就一句话:尽量让每次Young GC后的存活对象小于Survivor区域的50%,
都留存在年轻代里。尽量别让对象进入老年代。尽量减少Full GC的频率,避免频繁Full GC对JVM性能的影响。

16、各种参数

参数名称含义默认值说明
-Xms初始堆大小物理内存的1/64(<1GB)默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制.
-Xmx最大堆大小物理内存的1/4(<1GB)默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制
-Xmn年轻代大小(1.4or lator)注意:此处的大小是(eden+ 2 survivor space).与jmap -heap中显示的New gen是不同的。 整个堆大小=年轻代大小 + 年老代大小 + 持久代大小. 增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-XX:NewSize设置年轻代大小(for 1.3/1.4)
-XX:MaxNewSize年轻代最大值(for 1.3/1.4)
-XX:PermSize设置持久代(perm gen)初始值物理内存的1/64
-XX:MaxPermSize设置持久代最大值物理内存的1/4
-Xss每个线程的堆栈大小JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行 调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右 一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。(校长) 和threadstacksize选项解释很类似,官方文档似乎没有解释,在论坛中有这样一句话:”” -Xss is translated in a VM flag named ThreadStackSize” 一般设置这个值就可以了。
-XX:NewRatio年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)-XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5 Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
-XX:SurvivorRatioEden区与Survivor区的大小比值设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10
-XX:LargePageSizeInBytes内存页的大小不可设置过大,会影响Perm的大小=128m
-XX:+UseFastAccessorMethods原始类型的快速优化
-XX:+DisableExplicitGC关闭System.gc()这个参数需要严格的测试
-XX:MaxTenuringThreshold垃圾最大年龄如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代. 对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代即被回收的概率 该参数只有在串行GC时才有效.
-XX:+AggressiveOpts加快编译
-XX:+UseBiasedLocking锁机制的性能改善
-Xnoclassgc禁用垃圾回收
-XX:SoftRefLRUPolicyMSPerMB每兆堆空闲空间中SoftReference的存活时间1ssoftly reachable objects will remain alive for some amount of time after the last time they were referenced. The default value is one second of lifetime per free megabyte in the heap
-XX:PretenureSizeThreshold对象超过多大是直接在旧生代分配0单位字节 新生代采用Parallel Scavenge GC时无效 另一种直接在旧生代分配的情况是大的数组对象,且数组中无外部引用对象.
-XX:TLABWasteTargetPercentTLAB占eden区的百分比1%
-XX:+CollectGen0FirstFullGC时是否先YGCfalse

  • 38
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-无-为-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值