Java虚拟机二:JVM性能调优

2023-11-18

堆空间的划分

Java中的堆是JVM所管理的最大的一块内存空间,主要用于存放各种类的实列对象。这样划分的目的是为了使JVM能够更好的管理堆内存中的对象。堆的内存划分如图:

JVM å®æ´æ·±å¥è§£æ

Java堆的内存划分如图所示,分别为年轻代、Old Memory(老年代)、Perm(永久代)。其中在Jdk1.8中,永久代被移除,使用MetaSpace代替。其中新生代被细分为 Eden 和 两个Survivor 区域,这两个Survivor区域分别被命名为 from 和 to ,以示区分。

  • 新生代(1/3堆空间)   老年代(2/3堆空间)
  • Eden :from :to  = 8 :1 :1
  • JVM 每次只会使用Eden 和 其中一块Survivor 区域来为对象服务,所以无论什么时候,总是有一块Survivor 区域是空闲的,因此新生代实际可用内存空间为 9/10 的新生代空间。
  • 元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。

GC堆

java中的堆是GC垃圾回收的主要区域。前篇已经分析过垃圾收集需要完成两件事:检测出垃圾和回收垃圾,讲解了如何判断垃圾回收,和垃圾回收算法以及常用垃圾收集器。本部分针对垃圾收集过程进行主要说明。

新生代几乎是所有Java对象出生的地方,即Java对象申请的内存空间以及存放都是在这个地方。java中的大部分对象通常不需要长久存活,具有朝生夕灭的性质。当一个对象被判定为“死亡”的时候,GC就有责任来回收掉这部分对象的内存空间。新生代是GC收集垃圾的频繁区域。GC分为两种:Minor GC 和 FullGC  。MinorGC 是针对新生代中的垃圾收集动作,所采用的是复制算法。当老年代内存空间不足时,将发生FulGC,采用标记清除/整理算法。Full GC指的是清理整个堆空间,包括年轻代和永久代。垃圾回收结果图:

ä¸æ带你深å¥ç解JVM

GC过程:

①对象在Eden和一个FromSurvivor区域出生后,当Eden区域满了,或者新创建的对象大小 > Eden所剩空间,将进行一次MinorGC。②如果对象还存活着,并且能被另一块ToSurvivor区域容纳,则将使用复制算法将这些仍存活的对象复制到另一块ToSurvivor区域中,然后清理所使用过得Eden和Survivor区域,并且将这些对象的年龄设置为1.③后续Eden区继续生成对象,再次发生Minor gc的时候,会将存活的对象复制到From区,并将Eden区和To区清空回收,并将改对象的年龄+1.④部分对象会在From区域和To区域中复制来复制去,每熬过一次MinorGC,就将对象的年龄+1。当对象的年龄达到某个值时(默认15),最终如果还存活,就存入老年代。

注意:

  • 对于一些较大的对象,(即需要分配一块比较大的连续的内存空间)则直接进入到老年代。
  • FullGc 发生的次数不会有MinorGc那么频繁,并且做一次FullGc要比进行一次MinorGc的时间更长。
  • MinorGc拷贝到老年代空间不足和大对象老年代空间也不足 触发FullGC
  • Perm永久代空间不足会触发Full GC,可以让CMS清理永久代的空间。

发现虚拟机频繁full GC时应该怎么办:可以用命令查看触发GC的原因是什么 jstat –gccause 进程id

什么是空间分配担保策略?

JVM在发生Minor GC之前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象的总空间,如果大于,则此次Minor GC是安全的如果小于,则虚拟机会查看HandlePromotionFailure设置项的值是否允许担保失败。如果HandlePromotionFailure=true,那么会继续检查老年代最大可用连续空间是否大于历次晋升到老年代的对象的平均大小,如果大于则尝试进行一次Minor GC,但这次Minor GC依然是有风险的;如果小于或者HandlePromotionFailure=false,则改为进行一次Full GC。

动态年龄判断

根据对象年龄有另外一个策略也会让对象进入老年代,不用等待15次GC之后进入老年代,他的大致规则就是,假如当前放对象到Survivor区域,一批对象的总大小大于这块Survivor内存的50%,那么大于这批对象年龄的对象,就可以直接进入老年代了。

GC时为什么要停顿所有Java线程?

设置STW机制,因为GC先进行可达性分析。可达性分析是判断GC Root对象到其他对象是否可达,假如分析过程中对象的引用关系在不断变化,分析结果的准确性就无法得到保证。

JVM参数设置

-Xms 堆初始值 如:-Xms256m

-Xmx

堆最大可用值  如:-Xms512m
-Xmn 新生代堆最大可用值, 通常为Xmx1/3
-Xss JDK1.5+ 每个线程堆栈大小为1M 
-XX:NewRatio

配置新生代与老年代占比 1:2   如:-XX:NewRatio =2 则 新生代占用整个堆空间1/3,老年代2/3

-XX:SurvivorRatio

新生代中eden空间和from/to空间的比例 默认值为8.

即Eden占用新生代8/10  from/to 1/10

-XX:PermSize 永久代的初始化大小
-XX:MaxPermSize 永久代的最大值
-XX:+PrintGCDetails 打印GC信息
-XX:+HeapDumpOnOutOfMemoryError 让虚拟机在发生内存溢出时,Dump出当前的内存堆转存储快照,以便分析用。

 

 

 

 

 

 

 

 

 

 

 

 

使用示例:-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

当前堆最大内存 20M,初始化堆内存 20M,新生代最大可用内存 1M,Eden 区域和 form、to 区域的比例是 2:1:1,打印 GC 日志,使用串行回收

内存溢出与内存泄漏区别

内存溢出:申请空间超出系统能够提供的空间大小     内存泄露:内存泄露是指程序中间动态分配了内存,但在程序结束时没有释放这部分内存,从而造成那部分内存不可用的情况,最终导致内存溢出

JVM性能调优

调优的最终目的都是为了令应用程序使用最小的硬件消耗来承载更大的吞吐,JVM也不列外,JVM性能调优的目的:减少Full GC的次数,提高JVM性能。

1、性能定义

要查找和评估器性能瓶颈,首先要知道性能定义,对于jvm调优来说,我们需要知道以下三个定义属性,依作为评估基础:这三个属性中,其中一个任何一个属性性能的提高,几乎都是以另外一个或者两个属性性能的损失作代价,不可兼得。

  • 吞吐量(性能):重要指标之一,是指不考虑垃圾收集引起的停顿时间或内存消耗,垃圾收集器能支撑应用达到的最高性能指标。
  • 延迟(停顿时间):其度量标准是缩短由于垃圾啊收集引起的停顿时间或者完全消除因垃圾收集所引起的停顿,避免应用运行时发生抖动。
  • 内存占用:垃圾收集器流畅运行所需要 的内存数量。

2、性能调优原则

在调优过程中,我们应该谨记以下3个原则,以便帮助我们更轻松的完成垃圾收集的调优,从而达到应用程序的性能要求。

① MinorGC回收原则: 每次minor GC 都要尽可能多的收集垃圾对象。以减少应用程序发生Full GC的频率。

②GC内存最大化原则:处理吞吐量和延迟问题时候,垃圾处理器能使用的内存越大,垃圾收集的效果越好,应用程序也会越来越流畅。

③ GC调优3选2原则: 在性能属性里面,吞吐量、延迟、内存占用,我们只能选择其中两个进行调优,不可三者兼得。

JVM性能调优方式:系统估算法和GC日志分析法

一、系统估算法以实际案列进行说明:亿级流量电商系统JVM参数设置优化

以标准的4核8G机器为例说明,首先系统预留4G,其他4G按如下规则分配 :

  • 堆内存:3g
  • 新生代:1.5g
  • 新生代Eden区:1228m
  • 新生代Survivor区:153m
  • 方法区:256m
  • 虚拟机栈:1m/thread

设置参数:-Xms3072m   -Xmx3072m  -Xmn1536m  -Xss=1m  -XX:PermSize=256m  -XX:MaxPermSize=256m   -XX:HandlePromotionFailure -XX:SurvivorRatio=8

é¢è¯å®ï¼è¯´ä¸ä¸ä½ ä»¬çº¿ä¸JVMæ¯æä¹ä¼åçï¼

1、估算系统每秒占用内存数量

在优化JVM之前,要先估算要系统每秒占用的内存数量,如有个亿级流量电商系统,日活用户200万,付费转化率10%,那么每日下单量在20w左右,这些单正常在三四个小时内产生,平均每秒几十单。在大促活动中这些单会在几分钟内产生,假如每秒处理2400单,部署在三台服务器上,那么每台订单服务每秒大概会有800个请求,然后粗略的估算下每个请求占用多少内存,计算出每秒要花费多少内存。

假设是每秒800个请求,每个订单对象锁定1k,首先下单还设计其他对象,如库存、优惠券、积分等,现将请求对象放大10倍。然后,下单过程还涉及其他查询等操作在放大10倍,最后确定每个请求需要分配100k的空间,那1秒需要分配大约80m的内存。1秒后变为垃圾对象(1秒内方法执行完)

2、计算下多长时间触发一次Minor GC

按照之前的估算1秒需要分配大约80m的内存的话,Eden区的空间是1228m那平均每15秒就要执行一次Minor GC。

3、检查下Survivor区是否足够

按照上面的模型,每15秒就要执行一次Minor GC,GC执行期间并不能回收掉所有的新生代中的对象,那每次GC执行期间还会剩下大约80m无法回收的对象会进入Survivor区,但是别忘记JVM有动态年龄判断机制,80M大于Survivor的50%,直接进入老年代。老年代分配1536m,大概需要4.8分钟填满,此时就会发生FullGc。很明显FullGC频率就高,JVM性能调优的目的就是为了减少FullGC频率。

解决:从上面分析来看,这样设置下来Survivor的空间明显小了一点,所以将新生代设置2048m,才能避免触发动态年龄判断,这样就可以避免频繁FullGc。

二、GC日志分析法

想要进行JVM性能调优,首先要了解现有JVM运行情况,我们可以通过观察应用的GC日志,特别是Full GC 日志,对现有系统情况进行了解。

GC日志指令: -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:<filename>

GC日志是收集调优所需信息的最好途径,即便是在生产环境,也可以开启GC日志来定位问题,开启GC日志对性能的影响极小,却可以提供丰富数据。

必须得有FullGC 日志,如果没有的话,可以采用监控工具强制调用一次,或者采用以下命令,亦可以触发

jmap -histo:live pid

在稳定阶段触发了FullGC我们一般会拿到如下信息:

å¦ä½åççè§åjvmæ§è½è°ä¼

从以上gc日志中,我们大概可以分析到,在发生fullGC之时,整个应用的堆占用以及GC时间,当然了,为了更加精确,应该多收集几次,获取一个平均值。或者是采用耗时最长的一次FullGC来进行估算。

在上图中,fullGC之后,老年代空间占用在93168kb(约93MB),我们以此定为老年代空间的活跃数据。

其他堆空间的分配,基于以下规则来进行

å¦ä½åççè§åjvmæ§è½è°ä¼

基于以上规则和上图中的FullGC信息,我们现在可以规划的该应用堆空间为:

java 堆空间: 373Mb (=老年代空间93168kb*4)

新生代空间:140Mb(=老年代空间93168kb*1.5)

永久代空间:5Mb(=永久代空间3135kb*1.5)

老年代空间: 233Mb=堆空间-新生代看空间=373Mb-140Mb

对应的应用启动参数应该为:

java -Xms373m -Xmx373m -Xmn140m -XX:PermSize=5m -XX:MaxPermSize=5m

提升吞吐量的性能调优的目标就是就是尽可能避免或者很少发生FullGC ,尽量在MinorGC 阶段回收更多的对象,避免对象提升过快到老年代。

 

文章参考:

https://www.toutiao.com/a6659169929876472331/

https://www.toutiao.com/a6758453803709628942/

https://www.toutiao.com/a6777892950434120204/

https://www.toutiao.com/a6750187070641144324/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java虚拟机二:JVM性能调优 的相关文章

  • 性能说明:预热后代码运行速度变慢

    下面的代码运行完全相同的计算 3 次 它没有做太多事情 基本上将 1 到 100m 之间的所有数字相加 前 2 个块的运行速度大约是第三个块的 10 倍 我已经运行这个测试程序超过 10 次 结果显示差异很小 如果有的话 我希望第三个块运行
  • 无法识别的选项: - 无法创建 Java 虚拟机

    我正在尝试运行我的 jarLinux 服务器上的文件 这些是我正在运行的命令 JAVA HOME app local opt jdk packages jdk1 6 0 30 export PATH JAVA HOME bin PATH c
  • IntelliJ 调试:暂停整个虚拟机,然后进入单线程

    我正在调试一个具有大量线程的应用程序 我的断点设置为暂停整个虚拟机 当线程遇到其中一个断点时 我想使用 Step Over 但这似乎会恢复整个虚拟机 直到该步骤完成 如果我可以只单步执行到达断点的单个线程 那确实会有帮助 在 Intelli
  • 如果只有完全限定名称,如何获取 java 类的二进制名称?

    反射类和方法以及类加载器等需要使用所谓的 二进制 类名称 问题是 如果只有完全限定名称 即在源代码中使用的名称 如何获得二进制名称 例如 package frege public static class RT public static
  • 内存中的方法表示是什么?

    在思考一下 Java C 编程时 我想知道属于对象的方法如何在内存中表示 以及这一事实如何涉及多线程 是为内存中的每个对象单独实例化一个方法还是执行 同一类型的所有对象共享该方法的一个实例 如果是后者 执行线程如何知道哪个对象是 要使用的属
  • Cassandra DB 中的日期插入:重要的 1 小时轮班问题(后续)

    这是这个的后续其他原帖 https stackoverflow com questions 23080188 date insertion in cassandra db non trivial 1h shift issue 2308355
  • getResourceAsStream(file) 在哪里搜索文件?

    我很困惑getResourceAsStream 我的包结构如下 src net floodlightcontroller invoked getResourceAsStream here resources floodlightdefaul
  • 显示JVM中当前运行的所有线程组和线程

    所以我的任务是显示所有线程组以及当前在 JVM 中运行的属于这些组的所有线程 输出时应首先显示线程组 然后在下面显示该组中的所有线程 这是针对所有线程组完成的 目前 我的代码将仅显示每个线程组 然后显示每个线程 但我不确定如何达到我所描述的
  • 如何查看JVM中JIT编译的代码?

    有什么方法可以查看 JVM 中 JIT 生成的本机代码吗 一般用法 正如其他答案所解释的 您可以使用以下 JVM 选项运行 XX UnlockDiagnosticVMOptions XX PrintAssembly 根据特定方法进行过滤 您
  • STS 无法在我的计算机上启动

    我试图在 eclipse 上设置 Spring mvc 项目 基本项目进展顺利 但是使用 Restful 服务 Jersey 等开始出现许多与依赖项相关的错误 所以我打算转到STS 我正在使用 STS 2 9 2 它给我 无法创建java虚
  • 如何计算Java数组的内存大小?

    我知道如何通过添加三个部分来计算Java对象的内存大小 标头 属性 引用 我还知道Java数组也是一个对象 但是当我读到 Understanding the JVM Advanced Features and Best Practices
  • java.library.path 中没有字体管理器

    以下代码在我的桌面上运行得很好 BufferedImage image new BufferedImage width height BufferedImage TYPE INT RGB Graphics g image getGraphi
  • 在intellij中为java启用ssl调试

    从我的问题开始 上一期尝试通过 tls ssl 发送 java 邮件 https stackoverflow com questions 39259578 javamail gmail issue ready to start tls th
  • Java 接口合成方法生成,同时缩小返回类型

    我有 2 个接口和 2 个返回类型 interface interfaceA Publisher
  • Intellij Idea 使用什么 JVM 来启动?

    我是 Eclipse 用户 最近决定尝试 Intellij Idea 我的操作系统是 Ubuntu 12 使用 Eclipse 时 可以通过在 eclipse ini 中指定来轻松选择用于启动 Eclipse 的 JVM http wiki
  • 为什么 MetaSpace 大小是已用 MetaSpace 的两倍?

    我写了一个程序来模拟MetaSpace OOM 但我发现MetaSpace Size几乎总是两倍大Used MetaSpace Why 我用标志运行我的程序 XX MaxMetaspaceSize 50m 程序抛出OOM时Used Meta
  • 强制jvm返回本机内存[重复]

    这个问题在这里已经有答案了 我时不时地运行需要大量内存的 eclipse 任务 因此 当任务运行时 jvm 会消耗大约 2 3GB 的 RAM 这是可以的 但是一旦 jvm 占用了该内存 它就不会释放它 并且我遇到了一种情况 堆中已用内存约
  • Scala REPL 中的递归重载语义 - JVM 语言

    使用 Scala 的命令行 REPL def foo x Int Unit def foo x String Unit println foo 2 gives error type mismatch found Int 2 required
  • JVM内存段分配

    好吧 我有一个关于 JVM 内存段的问题 我知道每个 JVM 都会选择稍微不同地实现这一点 但这是一个总体概念 在所有 JVM 中应该保持相同 一个在运行时不使用虚拟机执行的标准C C 程序在运行时有四个内存段 代码 堆栈 堆 数据 所有这
  • 如何在Java中计算对象的数字年龄[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我想知道Java中对象的年龄 当我们使用new关键字时 Java中用户定义的对象被创建 但是什么时候它会被销毁 是跨越JVM的perm

随机推荐

  • mybatis之foreach用法

    在做mybatis的mapper xml文件的时候 我们时常用到这样的情况 动态生成sql语句的查询条件 这个时候我们就可以用mybatis的foreach了 foreach元素的属性主要有item index collection ope
  • 选择器函数querySelector和querySelectorAll

    选择器是Css非常强大的功能 早先一般是通过getElementById和getElementsByTagName来获取页面元素 在一些场景下就很不方便 后来DOM扩展出了Selector API标准 其中 Selector API Lev
  • 移植uboot-支持yaffs烧写,打补丁

    1 修改uboot支持yaffs 首先 每个命令都会对应一个文件 比如nand命令对应的common cmd nand c 而我们使用nand命令时 便会进入do nand 函数 位于common cmd nand c 1 1do nand
  • 产品经理的思考-ChatGPT的影响

    最近ChatGPT的不断升温 公司开始全面布局和应用人工智能 本以为今年的赋智会有个过渡过程 没想到来的这么凶猛 随着应用的深入 越来越多的开始了灵魂质问 随着大模型的不断深入应用 什么职位会被取代 我们应该如何与ChatGPT共存 Cha
  • 2017年10米分辨率全球土地覆盖产品(FROM-GLC10)Python下载爬虫

    此为2017清华大学地球系统科学系宫鹏教授团队研发的重大成果世界首套 2017年10米分辨率全球土地覆盖产品 FROM GLC10 爬虫下载爬虫分享 一 参考网站 1 全国各省10米分辨率的土地利用数据的制作与分享 2 世界首套2017年1
  • vs2019编译c语言提示有病毒,关于VS2019代码编译的问题(C++)

    代码很长 这里就不打出来了 1 严重性代码说明项目文件行禁止显示状态 错误C2664 BOOL SetFileAttributesW LPCWSTR DWORD 无法将参数 1 从 const char 36 转换为 LPCWSTR Pro
  • android opengl es 总结

    什么是OpenGL ES OpenGL ES 为OpenGL for Embedded System的缩写 为适用于嵌入式系统的一个免费二维和三维图形库 为桌面版本OpenGL 的一个子集 OpenGL ES 定义了一个在移动平台上能够支持
  • Python:每日一题之最少砝码

    问题描述 你有一架天平 现在你要设计一套砝码 使得利用这些砝码可以称出任意 小于等于 N 的正整数重量 那么这套砝码最少需要包含多少个砝码 注意砝码可以放在天平两边 输入格式 输入包含一个正整数 N 输出格式 输出一个整数代表答案 样例输入
  • (CMake) 指定生成器 generator

    文章目录 问题引入 具体处理 当前环境 例子 解决方案 命令行 设置变量 设置win环境变量 END 附录 win cmake 3 24 2 help linux cmake 3 10 2 help cmake基础 CMake 从下载到构建
  • Keil运行stm32项目无法打断点调试

    项目场景 有个新同事接了外协写的STM32F429的项目 项目接过来编译和烧录都没问题 但是Debug调试时候没法打断点 没有灰色区域可以点断点 点击运行可以 但点暂停也没有停止黄色光标 debug模式下就如同这样 1 问题描述 根据上述现
  • 关于APT32C001ADC采集不准的问题说明

    因为之前开发一款产品 要使用到触摸按键 又不想新增一个触摸IC 所以选择了APTC001进行开发 但是在调试的时候发现ADC有时候会不准 有时候是0电压的 但读寄存器的值却不是零 有时候读电源电压 那应该是4096的 但实际采集回来的去不是
  • 【毕业设计】机器视觉手势检测和识别系统 - python 深度学习

    文章目录 0 前言 1 实现效果 2 技术原理 2 1 手部检测 2 1 1 基于肤色空间的手势检测方法 2 1 2 基于运动的手势检测方法 2 1 3 基于边缘的手势检测方法 2 1 4 基于模板的手势检测方法 2 1 5 基于机器学习的
  • 浅述SATA接口Raid、AHCI、IDE三种模式

    今天在一台计算机上插上CF卡 不能工作 CF卡灯不亮 进BIOS SATA mode从IDE改成AHCI就好了 首先说一下 关于主板的SATA接口的工作模式 BIOS中常见的选项有以下三种 RAID 部分技嘉主板叫XHD AHCI IDE
  • Java String 常用操作方法说明和使用

    ps Java中的String类是一个非常重要的类 在Java程序中广泛使用 它可以用来保存和操作字符串 在这篇博客中 我们将对Java String的所有操作方法进行说明和使用 1 1 使用双引号创建字符串 String str1 Hel
  • ObjectC基础之注释、关键字、数据类型

    一 OC的注释 OC的注释不是 或者 了 它的注释是 举个例子 这是被注释掉的内容 二 OC的关键字 上图我们比较陌生的有 register typedef extern union unsigned const signed goto v
  • 自定义函数实现字符串处理函数strcat、 strcpy、strcmp、strlen和strlwr

    编C语言程序 用自定义函数实现字符串处理函数strcat strcpy strcmp strlen和strlwr的功能 strlen char str int n 0 char p str while p n return n strcat
  • 蓝桥杯 题库 简单 每日十题 day2

    01 卡片 题目描述 本题为填空题 只需要算出结果后 在代码中使用输出语句将所填结果输出即可 小蓝有很多数字卡片 每张卡片上都是数字 0 到 9 小蓝准备用这些卡片来拼一些数 他想从 1 开始拼出正整数 每拼一个 就保存起来 卡片就不能用来
  • 源 “MySQL 8.0 Community Server“ 的 GPG 密钥已安装,但是不适用于此软件包。请检查源的公钥 URL 是否配置正确。

    源 MySQL 8 0 Community Server 的 GPG 密钥已安装 但是不适用于此软件包 请检查源的公钥 URL 是否配置正确 只需将 sudo yum install y mysql server 改为 sudo yum i
  • Linux如何查看进程、杀死进程、启动进程等常用命令

    1 查进程 ps命令查找与进程相关的PID号 ps a 显示现行终端机下的所有程序 包括其他用户的程序 ps A 显示所有程序 ps c 列出程序时 显示每个程序真正的指令名称 而不包含路径 参数或常驻服务的标示 ps e 此参数的效果和指
  • Java虚拟机二:JVM性能调优

    堆空间的划分 Java中的堆是JVM所管理的最大的一块内存空间 主要用于存放各种类的实列对象 这样划分的目的是为了使JVM能够更好的管理堆内存中的对象 堆的内存划分如图 Java堆的内存划分如图所示 分别为年轻代 Old Memory 老年