对于垃圾回收相关的建议

说明

  • ==本文摘自【MemoryManagement-Whitepaper-1-150020.pdf】并转译,本文并不是完整的转译,部分地方有删减;==
  • ==本人水平有限,如有不正确的地方烦请指出,感激不尽。==

概述

在前一节中说的垃圾回收器、虚拟机和堆大小的自动选择很大比例都是在应用程序的合理范围之内,另外,对于选择和配置垃圾回收器的初始化建议就是什么都不要做,意思就是说对于垃圾回收器不要设置什么特殊用法,让系统在应用程序运行时根据平台和操作系统来自动选择,然后测试你的应用程序,如果它的性能达到了高吞吐量和低停顿时间,那么你就可以完成了,你不必去调试或修改垃圾回收器选项。

在另一方面,如果你的应用程序看起来因为垃圾回收有性能问题,那么最简单的就是你得首先考虑垃圾回收器的默认选项是不是最符合你的应用程序,如果你不是,那就要明确指定你认为最合适的回收器,然后再测试看看有没有得到你要的性能。

你可以使用后面描述的一些工具来计算和分析性能,根据这些结果,你可以考虑修改(虚拟机默认)选项,比如那些控制堆大小或者垃圾回收行为(的选项),这些常用的选项会在后面的章节中有描述。==需要注意的是,最合适的性能调整是需要先计算然后在调整,关于计算,需要使用实际代码中的相关测试来操作,当然,为了避免过度优化,我们也需要考虑应用程序的数据集,硬件等-设置是垃圾回收器的实现。==

这一节中提供了选择垃圾回收器和设置堆大小的相关信息,后面还有提供调整并行垃圾回收器和如何避免内存溢出异常的建议。

选择不同垃圾回收器的时机

正如在前面章节中所说过的一样,每个回收器都有其适用的场景,其中也有说到默认情况下,系统会自动根据平台和操作系统来选择并行或串行回收器,如果你的应用程序或环境需要比默认回收器更适合的回收器,可以明确使用命令行选项来设置:

-XX:+UseSerialGC //串行
-XX:+UseParallelGC //并行
-XX:+UseParallelOldGC //并行压缩
-XX:+UseConcMarkSweepGC //并行标记清理-CMS

堆容量

在前面章节中有提到过,堆大小有默认的初始大小和最大大小,这些默认可能可以适应大多数应用,但如果你的性能问题的分析或由于部分代或整个堆导致的内存溢出问题,你可以通过后面章节中描述的命令行选项来修改这些参数。例如,默认的堆最大值是64MB,在非server模式机器上经常是非常小的容量,因此你可以通过’-Xmx’选项来设置更大的容量。那如果你有较长的停顿时间的问题,那么你可以考虑适当的增加堆的可用内存。吞吐量通常需要更多的内存来支持,因此拥有足够的内存是影响垃圾回收性能的一个关键因素。

在讨论完你给提供堆更多有效内存之后,接下来你要考虑的是如何调整不同代的大小。影响垃圾回收性能的第二个因素是堆中的年轻代,除非你找出老年代回收或停顿时间的问题,否则你只能增加年轻代的内存。然而,当你使用串行回收器的时候,不要让年轻代的内存超过堆的一半。

当你使用其中一种并行垃圾回收器时,你指定想要的动作比拓展堆大小更能获取更好的收益,让回收器自动选择并根据对应的动作动态修改堆大小。

并行回收器的调整策略

如果垃圾回收器选择使用并行回收器或并行压缩回收器(不管自动选择还是明确指定),都需要去设置适合自己应用程序的吞吐量目标。不要选择堆的最大值除非你能明确知道你需要比默认最大值还大的堆最大值。堆会根据所选择的吞吐量目标来增长或减少大小,堆大小在初始化区间和应用程序动作更改区间有小小的波动是在允许范围之内。

如果堆增长到它的最大值,在大多数情况下这就意味着在这个最大值之内是无法实现吞吐量目标。设置这个最大值接近于平台中的物理内存总数,但又不会引起应用程序的切换问题。(设置之后)再次运行应用程序,如果吞吐量目标还没达到,那么这个目标就大于当前平台的有效内存的最大承受值。

如果吞吐量目标达到了,但停顿时间又太长了,那么就需要设置最大停顿时间目标了。选择了最大停顿时间目标就意味着你的吞吐量目标就会达不到,所以你要选择一个你的应用程序能接收的最大停顿时间。

垃圾回收器为满足目标会导致堆大小的振荡,即时应用程序到了稳定状态。实现吞吐量的压力(这会请求更大的堆)会在最大停顿时间目标和最小空间利用率之间做权衡(这两者都希望更小的堆)。

发生内存溢出时怎么办

开发者很经常遇到的一个问题就是应用程序的终端会提示’java.lang.OutOfMemoryError’异常,这个错误在没有足够分配给对象的时候就会抛出,也就是说,垃圾回收器已经搞不了更多的有效空间给新对象了,并且堆也无法再扩展。产生内存溢出错误不一定就是内存泄漏,可能是配置产生的问题,比如,设置的堆大小(或者是没设置的默认值)不足以应用程序的申请(就会导致内存溢出错误)。

诊断内存溢出错误的第一步就是应该检查全部错误信息,在异常信息中,在’java.lang.OutOfMemoryError’之后有更详细的堆栈信息。这里给出常见的额外信息例子,如果遇到这些情况,直接可以这样来解决:
- Java 堆空间,这意味着在堆中分配不了对象了,这个问题的产生可能就是配置的问题才导致的。例如,通过-Xmx命令行选项设置的堆最大值(或者是默认值)不足以应用程序的使用,同时也给出了提示,对象不再需要垃圾回收,因为应用程序无意中保持了这些对象的引用。HAT工具可以用来查看哪些是可以抵达的对象并且也知道每个存活对象保持了哪些引用。引起这个错误的原因可能还有一个,那就是程序终结器(finalizers)的过度使用,比如线程调用析构函数不能跟上终结器队列的增加率。可以使用jconsole管理工具查看哪些对象在等待执行析构函数。
- 持久代空间,这个异常也提示持久代空间已经满了,像之前描述的一样,JVM在堆中存储元数据的区域。如果应用程序加载很多类,那么持久代就膨胀,你可以通过命令行选项’-XX:MaxPermSize=n’来设置对应的大小。
- 请求的数组大小超过虚拟机的限制,这个异常也提示应用程序尝试分配比堆空间还大的数组。比如,应用程序想要分配512MB的空间,但堆的最大值是256MB,那么就会抛出对应的异常。在这种情况下要么是堆容量太小,要么就是数组的大小太大了。

在后面的章节会有说到有工具来检查内存溢出的问题,在这些大量有用的工具中有少数几款工具,如HAT(堆分析工具)、jconsole管理工具和jmap就是用来检测内存问题的。

猜你喜欢

转载自blog.csdn.net/Android_app/article/details/77244178