资源预览内容
第1页 / 共25页
第2页 / 共25页
第3页 / 共25页
第4页 / 共25页
第5页 / 共25页
第6页 / 共25页
第7页 / 共25页
第8页 / 共25页
第9页 / 共25页
第10页 / 共25页
亲,该文档总共25页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
1.1. 概述概述 Java 平台标准版 (Java SE) 被广泛应用于各种应用, 从桌面上的小小的 applet 到大型服务器上的 Web Service 无处不在。 为了支持各种不同的部署场景, Java HotSpot 虚拟机提供了多种垃圾回收器,每种都为满足不同的需求而设定。这 是也为了满足大大小小不同应用需求的一部分。不过,那些需要高性能应用的用 户、开发者和管理员们也被选择适合他们应用的恰当的垃圾回收器的繁琐困扰 着。取消这些额外操作的重要一步是在 J2SE 5.0 中作出的:垃圾回收器会根 据应用运行的计算机类型而作出选择。 这个垃圾回收器的“更好的选择”总的说是一种进步,不过,这并不意味着对所 有的应用这都是最好的选择。对于有极端的性能或其他需求的用户,仍需要显式 地指定垃圾回收器,并调优某些参数,以达到满意的性能。本文就为这些需求提 供了一些相关信息。首先,本文会基于串行的 stop-the-world 垃圾回收器来介 绍垃圾回收器的一般性特征和基本调优开关。 接下来会介绍其他垃圾回收器的特 点和如何选择一个垃圾回收器。 何时选择垃圾回收器?对于一些应用,这个答案可能是“永远不”。也就是说, 在有低频率、短时的垃圾收集器造成的停顿的情况下,大部分程序都运行良好。 不过,这并不适用于很多程序,特别是那些处理大量数据(若干 GB)、很多线 程和需要处理很多事务的情况。 Amdahl 观察到,大部分工作负载并不能被很好的并行化;有部分情况下总是会 被顺序执行, 无法从并行化中获益。 这对 Java 平台也是如此。 特别的, 在 J2SE 1.4 以前,Sun Java 平台的虚拟机并不支持并行垃圾回收,这样,在多处理器 系统中,垃圾回收会对并行应用产生严重影响。 下图显示了一个除了垃圾回收以外均为完美可伸缩的理想系统的性能曲线。 红色 曲线是一个在但处理器系统中会花费 1% 的时间在垃圾回收上的程序。它在 32 处理器的系统中,将损失 20% 的吞吐量。而一个花费 10% 时间在垃圾回收上的 应用 (不考虑单处理器系统中额外的垃圾回收时间)在系统扩张到 32 处理器系 统中时,会损失超过 75% 的吞吐量 。 这意味着在小型开发系统中微不足道的速度问题当扩张到大规模系统中就可能 成为严重的性能瓶颈。从另一个角度看,减少这样的性能瓶颈的小改动就可以获 得很大的性能收益。对足够大规模的系统,选择合适的垃圾收集器并进行必要调 优是绝对值得的。 对于大多数“小”应用(在现代处理器上大约需要 100MB 堆内存的应用)来说通 常是足够的。其他垃圾收集器会带来额外的负载或复杂性,这回让系统的某些行 为付出一定的代价。如果一个应用不需要一个垃圾收集器的某个功能。那么就使 用串行的垃圾收集器好了。 一个不应该使用串行垃圾收集器场景是一个超多线程 的大程序运行在一个大型的、有大量内存和两个或多个处理器的系统中。当应用 运行在这些服务器级的计算机上的时候,并行垃圾收集器会被缺省选择(参见下 面的功效学 )。 本文以 Solaris 操作系统(SPARC (R) 平台版本)中的 Java SE 6 作为参考。 不过,文中所述的概念和建议适用于所有支持的平台,包括 Linux, Microsoft Windows 和 Solaris 操作系统(x86 平台版本)。此外,文中的命令行参数也 对所有平台有效,虽然它们的缺省值在各个平台可能有所不同。 2.2. 功效学(功效学(ErgonomicsErgonomics) “功效学”是一个 J2SE 5.0 引入的概念。引入功效学概念是为了通过不设置或 设置很少的几个命令行参数的情况下提供更好的性能,这些参数包括: 垃圾收集器, 堆尺寸, 和运行时编译器 这里的参数选择假定应用所运行的主机类型和应用的类型一致(也就是说,大型 应用运行在大型的机器上)。这些选项简化了垃圾回收的调优。选择并行垃圾回 收器, 用户可以指定应用的最大中断时间和希望的吞吐量。这和指定堆大小来调 优性能是相对应的。最常用的功效学相关的内容在可以参考 “Ergonomics in the 5.0 Java Virtual Machine” 这篇文章。建议在尝试本文提到的细节配置 之前尝试该文章中介绍的功效学手段。 本文中的功效学特性被作为并行垃圾回收器的自适应尺寸策略的一部分。 这包括 指定垃圾回收性能的目标和性能调优的一些附加选项。 3.3.代代 J2SE 平台的优势之一是它将内存分配、垃圾回收这些繁复的细节屏蔽了起来。 然而, 一旦垃圾回收成为主要的瓶颈,那么理解一下这些隐藏在背后的细节就变 得有必要了。垃圾回收器对应用程序对对象的使用方式进行判断,这个判断会反 映在可调优参数中,他们可以被调整,以提高性能而不牺牲掉抽象性。 当一个对象不再可能被从其他任何地方访问到的时候就会被认为是垃圾了。 最直 接的垃圾回收算法就是简单地迭代所有可找到的对象。 任何没有被跌带到的对象 都可以被认为是垃圾了。这个方法的用时和活着的对象数量成正比,这对于那些 维护着大量活数据的程序来说是不可接受的。 从 J2SE 1.2 开始,虚拟机就引入了各种不同的垃圾回收算法,这些算法都使用 分代垃圾收集。尽管原生的垃圾回收会检查堆中的所有活着的对象,分代垃圾收 集采用了很多观测到的大部分应用程序的经验特征, 用来最小化发现废弃的对象 的工作量。最重要的经验特征是 weak generational 假设,该假设认为大部分 对象都只存活一少段时间。 下图中的蓝色区域是对象生存期的典型分布。横轴是对象被分配后的生存期。纵 轴方向计算的字节数是相应生存期的对象的总字节数。左侧的尖峰表明,对象在 分配之后不久就被废弃了。比如,迭代器对象常常只会在一个循环中被用到。 当然,有些对象确实活得要长一些,于是,分布曲线延伸到了右边。比如,典型 情况下,有些对象在初始化的时候被创建,并一直存活到进程结束。在这两种极 限情况之间,那些对象活的时间也是中等的,在图中表现初来的就是从开始的峰 值泄漏初来的蓝色区域。有些应用可能会有看起来十分不同的分布曲线,不过绝 大多数的进程都是这个常见的形状。 大部分对象都会“英年早逝”这个事实让高 效的垃圾收集变得具有可能性了。 为了为这样的应用环境优化,内存被按照“代” (generation)进行管理,或 者说,内存池中存放不同年龄的对象。当一个年龄断被填满后,就对该代的垃圾 进行回收。在内存池中的大部分对象都是年轻的对象(年轻的代),而大部分对 象也会在年轻的时候就成为垃圾。当年轻代被填满的时候,会导致一次“小回 收”(译注:原文 minor,似乎“未成年”更贴切一些,不过咱们读起来会很别 扭),这里只有年轻代的对象惠北回收,而其他年龄断的垃圾则不与理会。该回 收算法的成本是,一阶情况下,正比于被回收的活的对象的数量;年轻代因为满 是死对象,所以回收非常迅速。而在“小回收”中存活下来的对象于是乎就会被 转移到所谓的年老代(tenured generation)。最终,当年老代被填满而需要回 收的时候,就会导致一次主回收,这时整个堆都会被回收。主回收通常会运行锝 比小回收慢很多,因为大量的对象都会被处理。 如上文记述,对不同的应用,“工效学”会动态选择垃圾收集器来提供较好的性 能。 串行垃圾收集器用于哪些数据量比较小的程序,而且它的缺省参数也让大多 数小程序能够高效工作。 而大吞吐量垃圾收集器用于那些有中到大数据量的数据 集。工效学选择的堆尺寸参数和自适应尺寸策略用于为服务器提供更好的性能。 这些选择的大多数而不是所有的情况下工作得很不错。 这就引出了本文的核心宗 旨: 如果垃圾收集器成为了瓶颈,你可能不得不调整整个堆的大小乃至每个代的尺 寸。 检查垃圾收集器的详细输出,然后检查垃圾收集器对你关注的各个性能指标 的影响。 (并行垃圾收集器之外的)缺省的代排布大概就是这样的。 初始化的时候,最大的地址空间虚拟地保留住而没有分配出去,直到真的需要的 时候为止。整个保留的对象地址空间被分给了年轻的和年老的代。 年轻代包括“伊甸园”和两个幸存者空间。 大部分对象最初在伊甸园里被分配出 来。一个幸存者空间在任意时刻都是空的,作为伊甸园中的活对象的目的地,另 一个是用于下一次收集。对象在幸存者空间之间停留到足够老之后,就会被复制 到年老代去了。 另一个和年老代有密切关系的代是永久的(permanent)代,这里保存着虚拟机 需要的用来描述那些 Java 语言层面没有等价物的对象。比如,那些描述类和方 法的的对象就存放在永久代。 3.13.1 性能考虑性能考虑 对于垃圾回收的性能,主要有两种量度方法: 1.吞吐量。吞吐量是在一段足够长的时间中,没有花费在垃圾回收上的时间占总 时间的百分比。吞吐量包含了花在空间费配上的时间(不过空间分配速度的调优 一般是没有必要的)。 2.延时。延时是由于等待垃圾回收而导致的程序没有响应的时间。 不同的用户对垃圾收集有不同的需求。比如,对于一个 web server 而言,吞吐 量是合理的量度,因为垃圾收集带来的短时时延是可以容忍的,或者说是很容易 就被网络时延所掩盖了。不过,对于交互的图形界面程序而言,极短的停顿都会 影响用户的使用体验。 有些用户对其他的因素很敏感。Footprint 是一个进程的工作集,由页和 cache line 来量度。对于内存相对于进程数量很有限的系统而言。Footprint 会影响到 程序的可伸缩性。Promptness 是对象死掉和该块内存重新可用之间的时间间隔 的量度,这是分布式系统的一个重要考虑因素,包括远程方法调用(RMI)。 总的说,一个特定的代的尺寸选择是上述这些因素之间的权衡的结果。比如,一 个非常大的年轻代的大小可以最大化吞吐律,但会以 Footprint、Promptness 和延时作为代价。而年轻代延时可以通过缩小该代的大小来达到最小化,但同样 会损失吞吐量。近似地,调整一个代的尺寸不会影响到其他代的垃圾收集频率和 时延。 没有一个简单的方法来设置代的尺寸。 最好的选择由程序使用内存的方式和用户 的需求来决定。这样,虚拟机对垃圾收集器的选择并不总是最优的,而且可以通 过后面介绍的命令行参数来调整。 3.23.2 测量测量 使用应用特定的量度,吞吐量和 footprint 很容易被测量。例如,web 服务器的 吞吐量可以使用一个客户端负载生成器来测量,而该服务器的 footprint 则可 以在 Solaris 操作系统中使用 pmap 命令来测量。另一方面,垃圾收集导致的 时延可以方便地通过监测虚拟机自己的诊断输出来估算出来。 命令行参数 -verbos:gc 可以送出每一次垃圾收集时的堆和垃圾收集信息。比 如,这是一个大型服务器应用的输出: 1 2 3 GC 325407K-83000K(776768K), 0.2300771 secs GC 325816K-83372K(776768K), 0.2454258 secs Full GC 267628K-83769K(776768K), 1.8479984 secs 这里是两次小回收和之后的一次主回收。箭头前后的数字(比如第一行的 325407K-83000K)分别指垃圾回收前后的所有活着的对象占用的空间。在小回 收之后,这个尺寸之中仍然包含一些没有被回收的垃圾(死掉的对象)。这些对 象要么存在在年老代中,要么被年老或永久代中的对象所引用。 后面的括号中的数字(比如第一行中的 (776768K))是全部提交的堆大小,也就 是虚拟己不向操作系统申请内存的情况下,全部 java 对象可用的存储空间。注 意, 这个数字不包括幸存者空间中的一个,因为幸存者空间在一个给定时间只有 一个可用,同时也不包括永久代的空间,这里面是虚拟机使用的元数据。 最后一个数字(比如 0.2300771 secs)是垃圾收集所用的时间;这个例子里大 约是四分之一秒。 第三行中主垃圾回收的格
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号