2010-08-04 113 views
14

我正在进行使用JProfilerEclipse Tptp.和的Java应用程序的车间性能分析,性能调整,内存分析,内存泄漏检测等。我需要一套我可以提供的练习给参与者,他们可以: 使用该工具来分析发现问题:瓶颈,内存泄漏,次优代码等。我相信有很多经验和实际的例子。Java性能分析,性能调整和内存分析练习

  • 解决问题并实现优化的代码
  • 通过
  • 理想的情况下进行分析的另一个会话演示解决方案,编写单元测试,演示的性能增益

问题,也解决方案不应该过于复杂;应该有可能在几分钟之内解决它们,最好在几分钟之内解决它们。 一些有趣的领域行使:

  • 解决内存泄漏
  • 优化循环
  • 优化对象的创建和管理
  • 优化字符串操作
  • 解决问题的并发性和并发性加剧瓶颈

理想情况下,练习应该包括样本未经优化的代码和d解决方案代码。

+1

所以你要求的课程材料? – 2010-08-04 14:07:04

+0

练习更加精确。我想在研讨会中使用它们,但我会说这些对任何调整和分析Java应用程序的人都很有用。 – Dan 2010-08-04 14:19:11

+4

您可能需要考虑将VisualVM与其他两台显示器一起使用。或者如果时间有问题,不包括JProfiler。 JProfiler非常漂亮,但很难让人们按原样进行配置,而不会让他们付钱购买该工具。坦率地说,其他两个通常足以发现瓶颈和僵局。 – 2010-08-04 14:19:16

回答

6

我试图找到我在野外看到的现实生活中的例子(可能稍有改动,但基本问题都非常真实)。我也尝试将它们聚集在同一场景中,以便您可以轻松构建会话。方案:你有一个耗时的函数,你想为不同的值做很多次,但是相同的值可能会再次弹出(理想情况下,创建后不会太长)。一个很好而简单的例子就是你需要下载和处理的url-web页面对(对于练习它应该可能被模拟)。

循环:

  • 你要检查是否有一组单词在网页上弹出。在循环中使用你的函数,但是使用相同的值,伪代码:

    for (word : words) { 
        checkWord(download(url)) 
    } 
    

    一个解决方案很简单,只需在循环之前下载页面即可。 其他解决方案如下。

内存泄漏:

  • 简单:你也可以用一种高速缓存解决您的问题。在最简单的情况下,您可以将结果放到(静态)地图中。但是如果你不能阻止它,它的大小将会无限增长 - >内存泄漏。
    可能的解决方案:使用LRU映射。最有可能的性能不会降低太多,但内存泄漏应该消失。
  • 更棘手的一点:假设您使用WeakHashMap实现前一缓存,其中键是URL(不是字符串,请参阅后面的内容),值是包含URL,下载页面和其他内容的类的实例。你可能会认为它应该没问题,但事实上并非如此:由于值(不是弱引用的)具有对密钥(URL)的引用,所以密钥永远不会被清除 - >良好的内存泄漏。
    解决方案:从值中删除URL。
  • 和以前一样,但是url是interned字符串(“如果我们碰巧又有相同的字符串,则保存一些内存”),value并没有指向这个。我没有尝试,但在我看来,它也会导致泄漏,因为interned字符串不能进行GC编辑。
    解决方案:不要实习生,这也会导致你不能跳过的建议:不要做premature optimization, as it is the root of all evil

对象创建&字符串:

  • 说你只想显示页面的文本(〜删除HTML标签)。编写一个逐行执行的函数,并将其附加到不断增长的结果中。起初结果应该是一个字符串,因此追加需要很多时间和对象分配。你可以从性能的角度(为什么追加很慢)以及从对象创建的角度(为什么我们创建了如此多的字符串,StringBuffers,数组等等)来检测这个问题。
    解决方案:对结果使用StringBuilder。

并发:

  • 你想通过并行执行下载/过滤,以加快整个东西了。使用它们创建一些线程并运行你的代码,但是在一个大的同步块(基于高速缓存)内部做所有事情,只是“保护高速缓存免受并发问题的困扰”。效果应该是,您只能有效地使用一个线程,因为所有其他线程都在等待获取缓存上的锁定。
    解决方法:只同步周围缓存操作(例如,使用'java.util.collections.synchronizedMap())

  • 同步的代码的所有微小的碎片。这应该会损害性能,可能会阻止正常的并行执行。如果你幸运/足够聪明,你也可以想出死锁。 道德:同步不应该是一个特别的事情,在“它不会伤害”的基础上,而是一个深思熟虑的事情。

奖金锻炼:

在开始填满你的缓存并没有做过多的分配之后,但仍然有一个小泄漏某处。通常这种模式不太容易理解。您可以使用探查器的“书签”或“水印”功能,该功能应在缓存完成后立即创建。

1

不要忽视this method,因为它适用于任何语言和操作系统,适用于these reasons。一个例子是here。此外,尝试使用I/O和重要呼叫深度的示例。不要只使用像Mandelbrot这样的cpu-bound程序。如果你拿这个不是太大的C例子,并用Java重新编码,那应该说明你的大部分观点。

让我们来看看:

  • 解决内存泄漏。
    垃圾收集器的要点是堵塞内存泄漏。但是,您仍然可以分配太多的内存,并且对于某些对象而言,它显示为“新”中的大部分时间。

  • 优化循环。
    一般来说,循环不需要进行优化,除非在它们内部做得很少(并且它们占用很多时间)。

  • 优化对象创建和管理。
    这里的基本方法是:保持数据结构尽可能简单。特别是远离通知风格的尝试来保持数据的一致性,因为这些东西会跑掉并使呼叫树变得非常浓密。这是大型软件性能问题的主要原因。

  • 优化字符串操作。
    使用字符串构建器,但不要冒用不会占用执行时间百分比的代码。

  • 并发性。
    并发有两个目的。
    1)性能,但是这个只有的作品的程度,它允许多件硬件同时启动。如果硬件不在那里,它没有帮助。好痛。
    2)表达的清晰度,例如UI代码不必担心重计算或网络I/O同时进行。

在任何情况下,都不能强调,在证明某些事情需要大量时间之前不要做任何优化。

+0

你可以肯定用GC语言有“真正的”内存泄漏。 – 2010-08-04 15:46:56

+0

@Zwei:你说得对。这种方法不包括100%的问题。可能存在内存增长问题,这不是性能问题。对于那些人来说,你需要一种工具来追踪剩余物品的来源以及为什么它们没有被释放。 – 2010-08-04 15:56:10

+0

Mike,在我的linux机器上使用jdk 6,'javap'显示使用'+'连接的字符串在内部经过优化以使用StringBuilder类。在提出这个建议之前,先看看生成的字节码。 – questzen 2010-08-08 09:41:16

0

我已经使用JProfiler来分析我们的应用程序。但它没有太多的帮助。然后我使用JHat.Using JHat你不能实时看到堆。你必须采取堆转储然后分析它。使用OQL(Object Query Language)是查找堆泄漏的好技术。