`
gaozzsoft
  • 浏览: 427188 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类

Java性能优化的策略和常见方法(二)

    博客分类:
  • JVM
阅读更多

1)JVM对堆空间的管理

JVM 在初始化的过程中分配堆。堆的大小取决于指定或者默认的最小和最大值以及堆的使用情况。如果用Heapbase表示堆底,heaptop表示堆能够增长到的最大绝对值,用heaplimit表示实际的堆顶;则两者的差值(heaptop - heapbase)由命令行参数 -Xmx 决定。heaplimit指针可以随着堆的扩展而上升,随着堆的收缩而下降。heaplimit永远不能超过heaptop,也不能低于使用 -Xms 指定的初始堆大小。任何时候堆的大小都是 heaplimit - heapbase。如果整个堆的自由空间比例低于 -Xminf 指定的值(minf 是最小自由空间),堆就会扩展。如果整个堆的自由空间比例高于 -Xmaxf 指定的值(maxf 是最大自由空间),堆就会收缩。-Xminf 和 -Xmaxf 的默认值分别是 0.3 和 0.6,因此 JVM 总是尝试将堆的自由空间比例维持在 30% 到 60% 之间。参数 -Xmine(mine 是最小扩展大小)和 -Xmaxe(maxe 是最大扩展大小)控制扩展的增量。这 4 个参数对固定大小的堆不起作用(用相等的 -Xms 和 -Xmx 值启动 JVM,这意味着 HeapLimit = HeapTop),因为固定大小的堆不能扩展或收缩。

2)基本收集算法

  • 复制:将堆内分成两个相同空间,从根(ThreadLocal的对象,静态对象)开始访问每一个关联的活跃对象,将空间A的活跃对象全部复制到空间B,然后一次性回收整个空间A。因为只访问活跃对象,将所有活动对象复制走之后就清空整个空间,不用去访问死对象,所以遍历空间的成本较小,但需要巨大的复制成本和较多的内存。
  • 标记清除(mark-sweep):收集器先从根开始访问所有活跃对象,标记为活跃对象。然后再遍历一次整个内存区域,把所有没有标记活跃的对象进行回收处理。该算法遍历整个空间的成本较大暂停时间随空间大小线性增大,而且整理后堆里的碎片很多。
  • 标记整理(mark-sweep-compact):综合了上述两者的做法和优点,先标记活跃对象,然后将其合并成较大的内存块。

3)分代

分代是Java垃圾收集的一大亮点,根据对象的生命周期长短,把堆分为年轻代和年老代,根据不同代的特点采用不同的收集算法。

年轻代(New Area
实际上大部分对象都是朝生暮死,随生随灭的,因此所有收集器都为年轻代选择了复制算法。复制算法优点是只访问活跃对象,缺点是复制成本高。因为年轻代只有少量的对象能熬到垃圾收集,因此只需少量的复制成本。而且复制收集器只访问活跃对象,对那些占了最大比率的死对象视而不见,充分发挥了它遍历空间成本低的优点。年轻代随堆内存增大而增大,JVM会根据情况动态管理其大小变化。-Xmns<value>, -Xmnx<value>, -Xmos<value>, -Xmox<value> 等JVM选项可以设置年轻代与年老代的初始尺寸和最大尺寸。

年轻代里面又分为2个区域,一个是Allocate区,所有新建对象都会存在于该区,另一个是Survivor区;并实施复制算法。每次复制就是将Allocate中的活对象复制到Survivor或者年老代中(如果符合一定的年老化条件),然后将Allocate区与Survivor区的角色互换。

年老代(Tenured Area)

年轻代的对象如果能够经历过数次收集,就会进入年老区。年老区使用标记整理算法。因为年老区的对象通常有较长的生命周期,采用复制算法就要反复地复制对象,很不合算,所以采用标记清理算法。

年老期限(Tenure age)
年老期限是用于衡量一个年轻代的对象在什么情况下被升级为年老代的对象。这个参数会被JVM动态调整,并达到一个最大值14。每次垃圾收集之后存活下来的对象的年老期限会递增一。一个年老期限为x的对象意味着,当该对象经历了Allocate区和Survivor区的x次反转后仍然存活,则该对象会被升级为年老代对象。该阈值的调整是基于年轻代空间所占堆空间的比例。

倾斜比率(Tilt ratio)
Allocate区在年轻代区域中占用的空间是使用一种称为Tilting的技术进行最大化的。Tilting控制Allocate区和Survivor区的相对大小。基于每次反转之后存活下来的对象所占空间的总数,该倾斜比率(Tilt ratio)会被调整以使Survivor区变得更小。比如,如果初始年轻代的大小为500MB,那么Allocate区和Survivor区将各占一半,即250MB。随着应用程序的运行,一次垃圾收集事件被触发,而且只有50MB的对象存活下来。在这种情况下,Survivor区的空间将被减少,从而为Allocate区提供更多的空间。较大的Allocate区意味着将经历更长的时间才会发生下一次垃圾收集。如下图所示,Survivor区的空间会被逐步调整到最合适的比例。

垃圾收集前后的Allocate区和Survivor区的分布举例

4)verbosegc日志输出

verbosegc日志由 JVM 在指定 -verbosegc 命令行参数时生成,是一种非常可靠的独立于平台的调试工具。启用 verbosegc 可能对应用程序的性能有一定影响。如果这种影响是无法接受的,则应该使用测试系统来收集 verbosegc 日志。这是监控整个 JVM 是否运转良好的一种好办法,在出现 OutOfMemory 错误的情况下,这种方法尤其重要。

5)正确设置堆的大小

计算正确的堆大小参数很容易,但它可能对应用程序启动时间和运行时性能有很大的影响。初始大小和最大值分别由参数 -Xms 和 -Xmx 控制,这些值通常是根据理想情况和重负荷情况下堆的使用情况的估计来设置的,但 verbosegc 可以帮助确定这些值,而避免胡乱猜测。下面是从启动到完成程序的初始化(或者进入“就绪”状态)这段时间里,一个应用程序的 verbosegc 输出,如下所示。

计算正确的堆大小参数很容易,但它可能对应用程序启动时间和运行时性能有很大的影响。初始大小和最大值分别由参数 -Xms 和 -Xmx 控制,这些值通常是根据理想情况和重负荷情况下堆的使用情况的估计来设置的,但 verbosegc 可以帮助确定这些值,而避免胡乱猜测。下面是从启动到完成程序的初始化(或者进入“就绪”状态)这段时间里,一个应用程序的 verbosegc 输出,如下所示。

<GC[0]: Expanded System Heap by 65536 bytes
<GC[0]: Expanded System Heap by 65536 bytes
<AF[1]: Allocation Failure. need 64 bytes, 0 ms since last AF>
<AF[1]: managing allocation failure, action=1 (0/3983128) (209640/209640)>
<GC(1): GC cycle started Tue Oct 29 11:05:04 2002
<GC(1): freed 1244912 bytes, 34% free (1454552/4192768), in 10 ms>
<GC(1): mark: 9 ms, sweep: 1 ms, compact: 0 ms>
<GC(1): refs: soft 0 (age >= 32), weak 5, final 237, phantom 0>
<AF[1]: completed in 12 ms>

上述记录表明,第一次发生 AF 时,堆中的自由空间为 0%(3983128 中有 0 字节可用)。此外,第一次垃圾收集之后,自由空间比例上升到 34%,略高于 -Xminf 标记(默认为 30%)。根据应用程序的使用,使用 -Xms 分配更大的初始堆可能会更好一些。几乎可以肯定的是,上例中的应用程序在下一次 AF 时会导致堆扩展。分配更大的初始堆可以避免这种情况。一旦应用程序进入 Ready 状态,通常不会再遇到 AF,因此也就确定了比较好的初始堆大小。类似地,通过增加应用程序负载也可以探测到避免出现 OutOfMemory 错误的 -Xmx 值。

如果堆太小,即使应用程序不会长期使用很多对象,也会频繁地进行垃圾收集。因此,自然会出现使用很大的堆的倾向。但是由于平台和其他方面的因素,堆的最大大小还受物理因素的限制。如果堆被分页,性能就会急剧恶化,因此堆的大小一定不能超出安装在系统上的物理内存总量。比如,如果 AIX 机器上有 1 GB 的内存,就不应该为 Java 应用程序分配 2 GB 的堆。

垃圾收集周期所花费的时间直接与堆的大小成正比。一条好的原则是根据需要设置堆的大小,而不是将它配置得太大或太小。

常见的一种性能优化技术是将初始堆大小(-Xms)设成与最大堆大小(-Xmx)相同。因为不会出现堆扩展和堆收缩,所以在某些情况下,这样做可以显著地改善性能。通常,只有需要处理大量分配请求的应用程序时,才在初始和最大堆大小之间设置较大的差值。但是要记住,如果指定 -Xms100m -Xmx100m,那么 JVM 将在整个生命期中消耗 100 MB 的内存,即使利用率不超过 10%。

6)避免堆失效

如果使用大小可变的堆(比如,-Xms 和 -Xmx 不同),应用程序可能遇到这样的情况,不断出现分配失败而堆没有扩展。这就是堆失效,是由于堆的大小刚刚能够避免扩展但又不足以解决以后的分配失败而造成的。通常,垃圾收集周期释放的空间不仅可以满足当前的分配失败,而且还有很多可供以后的分配请求使用的空间。但是,如果堆处于失效状态,那么每个垃圾收集周期释放的空间刚刚能够满足当前的分配失败。结果,下一次分配请求时,又会进入垃圾收集周期,依此类推。大量生存时间很短的对象也可能造成这种现象。避免这种循环的一种办法是增加 -Xminf 和 -Xmaxf 的值。比方说,如果使用 -Xminf.5,堆将增长到至少有 50% 的自由空间。同样,增加 -Xmaxf 也是很合理。如果 -Xminf等于 5,-Xmaxf 为默认值 0.6,因为 JVM 要把自由空间比例保持在 50% 和 60% 之间,所以就会出现太多的扩展和收缩。两者相差 0.3 是一个不错的选择,这样 -Xmaxf.8 可以很好地匹配 -Xminf.5。

如果记录表明,需要多次扩展才能达到稳定的堆大小,但可以更改 -Xmine,根据应用程序的行为来设置扩展大小的最小值。目标是获得足够的可用空间,不仅能满足当前的请求,而且能满足以后的很多请求,从而避免过多的垃圾收集周期。-Xmine、-Xmaxf 和 -Xminf 为控制应用程序的内存使用特性提供了很大的灵活性。

7)应该避免的开关

下列命令行开关应避免使用:

-Xnocompactgc 该参数完全关闭压缩。虽然在性能方面有短期的好处,最终应用程序堆将变得支离破碎,即使堆中有足够的自由空间也会导致 OutOfMemory 错误
-Xcompactgc 使用该参数将导致每个垃圾收集周期都执行压缩,无论是否有必要。JVM 在压缩时要做大量的决策,在普通模式下会推迟压缩
-Xgcthreads 该参数控制 JVM 在启动过程中创建的垃圾收集帮助器线程个数。对于 N-处理器机器,默认的线程数为 N-1。这些线程提供并行标记和并行清理模式中的并行机制
参考文献:

<<IBM JDK 5.0 Diagnostics Guide>>
http://www-128.ibm.com/developerworks/cn/java/

分享到:
评论

相关推荐

    大话JAVA性能优化

    但根据标题《大话JAVA性能优化》和描述“虽然有些地方可能过时,但是还是可以一读”以及标签“java 优化”,可以推断出书籍内容可能围绕Java编程语言的性能优化相关知识。基于这些信息,我们可以构建关于Java性能...

    大话java性能优化 周明耀 完整版

    《大话java性能优化》是周明耀先生的一本深入探讨Java性能调优的专业书籍,其主要内容涵盖了Java程序设计中的各种性能优化策略和技术。这本书旨在帮助开发者理解和掌握如何提升Java应用的运行效率,减少资源消耗,...

    Java 性能优化 一书源码

    Java性能优化是软件开发中的一个关键领域,尤其是在大型企业级应用和高并发系统中。《Java性能优化》一书深入探讨了如何通过各种技术提升Java应用程序的效率和响应速度。以下是一些基于书籍源码和相关文件名的关键...

    Java性能优化实战视频全集

    ### Java性能优化实战知识点概述 #### 一、理论分析篇 **1.1 性能优化的衡量指标及注意事项** - **衡量指标**: 包括响应时间、吞吐量、资源利用率等。 - **注意事项**: 在进行性能优化时,需确保优化方案不会引入...

    《Java程序性能优化》(葛一鸣)PDF版本下载.txt

    《Java程序性能优化》这本书主要探讨了如何通过各种技术和策略来提高Java应用程序的性能。 ### 性能瓶颈分析 - **CPU使用率高**:程序执行时,某些部分可能过度消耗CPU资源,导致性能下降。 - **内存泄漏**:对象...

    java性能优化

    总的来说,Java性能优化涉及多个层面,需要综合考虑程序设计、环境配置、代码实现和硬件资源等因素,通过精细化管理和技术手段,实现性能的提升。对于开发人员来说,理解和掌握这些优化技巧是提升Java应用效率的关键...

    java性能优化集锦

    本资料集锦主要涵盖了Java性能优化的多个方面,包括但不限于代码优化、内存管理、系统设计与优化等关键领域。 一、Java程序性能优化(23条) 1. **避免过度使用反射**:反射在某些场景下很有用,但其开销较大,应...

    Java企业应用-性能优化原则, 方法与策略.pptx

    在Java世界中,性能优化涉及到多个层面,包括基础原理、调优方法以及特定策略。以下是对这些主题的详细探讨: 1. 性能基础与方法论: - Little's定律:在稳定的系统中,长期平均的客户数量(L)等于长期平均到达率...

    阿里+Java+开发手册、阿里巴巴Java性能调优实战

    本书不仅讲解了性能优化的理论知识,还提供了大量的实战案例和分析,帮助开发者快速定位性能瓶颈并给出有效的优化策略。在JVM内存调优方面,书中详细介绍了不同垃圾回收(GC)算法的原理和适用场景,并指导开发者...

    阿里JAVA性能优化实战

    【阿里JAVA性能优化实战】是面向Java开发人员和架构设计人员的专业课程,旨在提供系统优化的知识和技巧。本文将深入探讨Java性能优化的关键点,并基于给出的部分内容进行详细阐述。 性能调优对于任何互联网公司都是...

    java性能优化权威指南

    《Java性能优化权威指南》是一本深度探讨Java应用程序性能提升的专业书籍。这本书旨在帮助开发者、系统管理员以及架构师掌握在实际开发过程中优化Java程序的关键技术和方法。书中涵盖了从基础概念到高级策略的全面...

    Java性能优化解析

    Java性能优化解析主要关注如何提升Java应用程序的效率和运行效果,包括多个层面的考虑。首先,性能的定义涉及五个关键指标: 1. 运算性能:衡量不同算法的执行速度和效率,寻找最佳的实现方式。 2. 内存分配:优化...

    java性能的优化-如何提高java运行效率

    【Java 性能优化概述】 Java 作为一种跨平台的编程语言,自1990年代中期推出以来,凭借其“一次编写,到处运行”的特性获得了广泛赞誉,但也因其相对于C等语言的性能和运行效率问题受到诟病。尤其是在服务器端应用...

    java performance java性能优化

    本文将深入探讨Java性能优化的方法和技术,帮助开发者通过减少时间和空间消耗来提升Java程序的整体性能。 #### 二、时间消耗的优化 ##### 2.1 标准代码优化 在编写Java程序时,开发者不应期望编译器(如`javac`或...

    Java性能优化

    ### Java性能优化关键知识点 #### 一、合理运用单例模式 **单例模式**是一种常用的软件设计模式,它确保一个类只有一个实例,并提供一个全局访问点。在Java性能优化中,合理使用单例模式可以带来以下优势: 1. **...

    Java开发后端性能优化

    JVM是Java性能优化的重要环节。理解JVM的工作原理,如垃圾收集机制、类加载机制,以及如何调整JVM参数,都是优化的关键。例如,设置合适的堆大小、新生代与老年代比例、GC策略等,可以显著改善应用性能。在容器化...

    Java高性能系统常见设计与优化

    Java高性能系统的设计与优化是构建高效、稳定且可扩展的应用程序的关键环节。在这个主题中,我们将深入探讨几个关键的知识点,包括高性能系统的一般架构、优化原则以及JVM(Java虚拟机)的优化。 首先,让我们关注...

    Java性能优化指南:JVM调优技巧与实践

    随着Java技术的不断发展,新的调优方法和工具将不断涌现,帮助开发者更有效地优化JVM性能。 JVM性能调优是一个多方面的任务,涉及JVM配置、垃圾回收策略、内存管理、线程使用等多个方面。通过采用合适的调优技巧和...

    JAVA性能优化之路(一).docx

    本文将围绕这些方面展开,详细阐述优化策略和实践方法。 首先,性能诊断是优化的第一步。在OS层,我们需要关注系统的资源利用率,如CPU、内存、磁盘I/O和网络I/O。通过监控这些指标,可以发现是否存在过度使用或...

Global site tag (gtag.js) - Google Analytics