这篇文章会检验你有关JVM的知识以及项目交付相关的技能;尤其是涉及到JVM升级的时候。期待你们的评论及回复,一起探讨下如何规避这类的项目可能产生的性能问题。
背景
最近碰到了一个影响到线上生产环境的问题,我们使用的是WebLogic 10以及32位的Hotspot JVM 1.6 。鉴于目前的一些问题以及未来负载上升的预测,我们决定将HotSpot JVM 1.6升级成64位的。
注意我们并没有修改JVM的启动参数。
经过几周的功能测试及规划,这次升级成功地部署到了线上环境。不过,技术支持团队发现第二天便出现了严重的性能下降,其中还有线程锁竞争的问题,迫使部署团队不得不回滚了这次升级。
最终我们找到了问题的原因,而这次升级也将在最近重新进行发布。
问题:
从上面这些信息来看,说一下你认为可能导致这次性能下降的原因。
说一下这次升级有什么好处,对于这类升级的如何进行管理以及降低风险,给出一些你的建议。
答案:
我经常听到有人说只要从32位JVM升级到64位就能自动获得性能的提升。这只说对了部分。有显著的性能提升的前提是在这之前你的系统存在内存占用的问题比如过度GC或者java.lang.outofmemoryerror,并且你也进行了适当的调优及堆大小的调整。
不幸的是,我们通常都忽略了一个事实,对于 64位的JVM来说,系统中的本地指针会占用8个字节,而不是4个。这会导致你的程序的内存占用量的增加,因此会带来更频繁的GC以及性能的下降。
下面是Oracle的官方解释:
64位的虚拟机和32位的比起来,性能上有什么不同?
一般来说,和32位的虚拟机相比,同样的程序在64位机上仅需花费很小的_性能损失_就能获得更大的寻址空间。这是由于系统的本地指针在64位虚拟机中会占用8个字节。这些额外字节的加载会对内存的使用带来影响,其结果就是程序执行的速度会变稍微的变慢,具体是多少取决于你的Java程序在执行的过程中需要加载多少指针。好消息是AMD64和EM64T平台在64位模式下运行的时候,Java虚拟机能获得额外的寄存器,来生成更高效的本地指令序列。这些额外寄存器带来的性能提升几乎能弥补64位虚拟机带来的执行速度的下降。SPARC平台上64位虚拟机和32位的相比,大概会有10%~20%的性能下降。AMD64和EM64T平台上则大概是0%~15%,这取决于你应用程序执行的时候有多少指针了。
现在回到我们开始说的那个问题上,内存占用量有了显著的增加,这就是导致性能问题的罪魁祸首。根据你选择的GC策略,GC的老生代回收会导致JVM和线程的暂停时间变得更长,这引发了线程锁竞争以及其它的问题。从下图可以看到,升级到64位JVM后应用程序的内存占用量(老生代)增加了45%。
Java堆的使用量 ~老年代回收后占用量大概是900MB(32位)
Java堆的使用量 ~老年代回收后占用量大概是1.3GB(64位)
应用程序的Java堆的内存占用量增加了45%。当然这是本地指针的大小膨胀了的缘故。
还有一个问题就是在项目支付前,只进行了功能测试,而没有进行性能测试及压力测试。并且也没有对JVM参数的进行修改或者调整,这自然就增长了旧生代回收的频率以及GC的暂停时间。最终的解决方案是将堆的大小从2GB增加到2.5GB,并且开启压缩指针的选项。
现在你已经明白问题是什么了,下面是我关于类似升级的一些建议:
1. 进行性能压测,比较下应用程序在32位和64位时的内存占用量和GC的表现。
2. 确保有足够的时间来进行GC参数及堆大小的调优,以便减少GC的暂停时间。
3. 如果你用的是HotSpot JVM1.6 6u23以后的版本,可以启用指针压缩这个选项。这个选项使得JVM可以通过Java堆的某个64位的起始地址的32位的偏移量来表示大部分的指针;这样就减少了升级再来的内存占用量的增加。
4. 正确地规划你的JVM进程所在的宿主机的容量。确保内存和CPU的能力满足这次升级带来的额外的内存和CPU的消耗。
5. 采用一种低风险的升级策略,只升级线上环境的部分机器到64位JVM,比如说25%~50%。这样你还可以比较下现有的32位机器以及新升级的机器的表现,确保性能带来的收益及损失满足你的预期。
原创文章转载请注明出处:
http://it.deepinmind.com
英文原文链接
分享到:
相关推荐
Java虚拟机(JVM)是Java程序...了解并掌握这些JVM常见问题,不仅有助于解决实际开发中的性能问题,还能在面试中展现出深厚的Java基础。对于Java开发者来说,深入理解JVM的工作原理和调优策略是不可或缺的专业技能。
本文将详细介绍这一问题的原因以及具体的解决方法。 #### 一、问题背景 在尝试运行Eclipse时,用户可能会遇到如下的错误提示:“jvm terminated. Exit code=-1”。这种错误通常是由于JVM分配给Eclipse的内存不足...
深入学习JVM对于优化Java应用程序性能、理解和解决内存问题至关重要。本教程将涵盖JVM内存模型、内存分配以及优化策略。 一、JVM内存模型 1. 堆内存:堆是所有线程共享的一块内存区域,主要用于存储对象实例。Java...
通过调整GC参数和优化代码逻辑后,问题得到有效解决。 - **案例2:大数据处理系统调优**:针对大数据处理系统,通过对JVM参数的调整以及合理的内存分配策略,显著提升了系统的处理速度和吞吐量。 #### 六、总结 本...
2. 运行时数据区:包括方法区(Method Area)、虚拟机栈(JVM Stack)、本地方法栈(Native Method Stack)、程序计数器(PC Register)和堆(Heap)。每个线程有自己的虚拟机栈和程序计数器,而方法区和堆则是所有...
书中强调将理论知识转化为实践技能,提供了分析GC日志、JIT编译优化过程的方法,并增加了一系列处理JVM问题的实际案例。这有助于读者在遇到性能瓶颈或故障时,能够迅速定位问题并采取相应的解决措施。 总的来说,这...
这是为了缓解64位JVM带来的内存消耗增加问题。 6. **检测JVM位数**:可以通过检查系统属性如`sun.arch.data.model`或`os.arch`来判断JVM是32位还是64位。 7. **JRE、JDK、JVM与JIT的区别**: - **JRE**:Java运行...
Java虚拟机(JVM)的垃圾收集(Garbage Collection, GC)是Java程序运行时管理内存的关键机制。它自动地识别并释放不再使用...通过深入研究JVM源码,我们可以更深入地理解这些概念,并能有效地解决实际遇到的性能问题。
这主要是为了解决Java 6向后兼容性问题,因为Java 6最高向下兼容到Java 1.2版本,而这两个版本之间的class信息存在较大差异。 - **关联选项**:`-XX:+UseSplitVerifier` ##### -XX:+HandlePromotionFailure - **...
在Java开发中,JVM扮演着至关重要的角色,它是Java程序运行的基础,理解和掌握JVM对于提升程序性能、解决运行时问题具有重要意义。 1. **JVM架构** - 类加载机制:JVM通过类加载器将.class文件加载到内存中,包括...
为了解决这些问题,开发者需要对JVM的运行进行细致的监控和分析,比如使用JVM提供的监控工具和参数来进行性能调优。 最后,在JVM调优的过程中,我们还需要关注内存中的内容分布,如何在堆和栈之间合理分配数据,...
### JVM调优基础及实践 #### 一、Java虚拟机内存模型 JVM内存模型主要由以下几个部分组成: 1. **程序计数器(Program Counter Register)**:每条线程都有一个独立的程序计数器,用来记录当前线程正在执行的字节码...
这种方法区的调整是由于永久代存在的一些问题,比如大小设定困难,容易引发溢出,以及给垃圾收集器(GC)带来额外的复杂性,导致较低的回收效率。元空间位于直接内存(Direct Memory),这使得内存分配更加灵活,...
- JVM内存模型主要包括堆内存、栈内存、方法区和程序计数器。理解这些内存区域的功能和用途对于有效管理内存至关重要。 3. **JVM性能调优参数**: - `-Xms`和`-Xmx`分别用于设置初始堆内存和最大堆内存大小。合理...
但请注意,这只是解决问题的权宜之计,而不是长期解决方案,因为这可能掩盖了真正的问题。 总的来说,`-noverify`是一个双刃剑,它可以提高启动效率,但也可能引入安全隐患。在使用时,需要权衡性能和安全之间的...
### JVM调优总结 ...通过对数据类型、堆与栈的区别以及参数传递机制的理解,可以帮助开发者更好地诊断和解决性能问题。同时,合理的调优策略能够显著提升Java应用的性能表现,确保系统的稳定运行。
其次,了解JVM内存溢出和死锁问题的定位与分析方法,以便快速定位问题并解决。另外,架构师还应该了解JVM调优的一些最佳实践,比如如何避免内存泄漏和过度的垃圾回收开销。 总结起来,高并发系统下的JVM调优涉及对...
尽管如此,这种自动化管理也可能导致一些问题,如内存泄漏和内存溢出,尤其是在不了解JVM内存管理机制的情况下,这些问题往往难以定位和解决。 #### 二、JVM运行时数据区域详解 JVM在执行Java程序的过程中,会使用...
Java虚拟机(JVM)是Java程序运行的基础,它提供了平台无关性,使得...这些知识点涵盖了JVM的基础和进阶内容,对于Java开发者来说,理解和掌握它们对于提升编程能力、解决实际问题以及在面试中脱颖而出都具有重要意义。