优大网

月份存档: 6 月 2013 ( 2 / 2)

优化技巧分享:把内存消耗降低至原来的1/20

摘要:Plumbr是一家Java存泄露检测器开发公司,在最近的客户报告中,他们发现了一个内存耗尽相关的问题。在检测后他们查出了问题所在,通过优化最终降低了21.5倍的堆内存。

这是最近发生的又一起内存相关的事件了。这个案例是从一个最近的客户报告中提取出来,一个异常运行的应用在其产品中反复报告内存耗尽。

这个症状是由我们的一个实验性功能发现,它主要用来监测某一类数据结构的使用情况。它提供了一个信号探针,结果会指向问题源代码的某一位置。为了保护客户的隐私,我们人为重建了该例子并保持它同原真实场景在技术层面的一致性。你可以免费在此处 下载到源码

故事开始于一组从外界源加载进来的对象。同外部的信息交互是基于XML的接口,这本身并没什么大不了的,但事实上“基于XML的格式进行通讯”的实现细节被分散到了系统的每一个角落。 传入系统的文档是首先被转换成XMLBean实例,然后在整个系统范围内被使用,这中做法听起来有点傻。

整个问题中最核心的部分是一个延迟加载的缓冲方案。缓存的对象是“Person”的实例:

 

1
2
3
4
5
6
7
// Imports and methods removed to improve readability
public class Person {
    private String id;
    private Date dateOfBirth;
    private String forename;
    private String surname;
}

 

 

你也许会说这才能消耗多少内存呢。但当我们揭开进一步的细节时,发现事情就变了味了。表面上根据设计,声称实现只用到的诸如上文提到的那样一些简单的类,但真实的情形是使用了基于模型生成的数据结构。使用的模型是诸如下面的这个简化的XSD片段。

 

1
2
3
4
5
6
<xs:schema targetNamespace="http://plumbr.eu" xmlns:xs="http://www.w3.org/2001/XMLSchema"
        elementFormDefault="qualified"> <xs:element name="person"> <xs:complexType>
        <xs:sequence> <xs:element name="id" type="xs:string"/> <xs:element
        name="dateOfBirth" type="xs:dateTime"/> <xs:element name="forename"
        type="xs:string"/> <xs:element name="surname" type="xs:string"/>
        </xs:sequence> </xs:complexType> </xs:element> </xs:schema>

 

 

使用 XMLBeans,开发者生成了该模型,并在真实的场景中使用。现在我们回到开始的这个缓存的方案上来,假设它设计初衷是为了支持最多1.3M Person类的实例,而我们实际却要塞进去同等数量的大家伙,这从根上就注定了失败。

跑一组测试用例后,发现1.3M个基于XMLBean的生成的实例需要消耗大概1.5GB的堆空间。我们当时想这肯定可以做的更好。

第一个改进是显而易见的,外部同系统内部集成的实现细节是不应该把影响传递给系统的每一个角落的。所以我们把缓存改成了使用简单的  java.util.HashMap<Long, Person>。ID是键,Person是值。我们发现内存的消耗立即降低到了214MB。但这还不能令我们满意。

由于Map中的键是一个数,我们有十足的理由使用 Trove Collections来进一步降低它的内存消耗。这在实现上的改动很快,我们只需把  HashMap 改成  TLongObjectHashMap<Person> ,堆的消耗进一步降低到了143MB。

活干到这个程度我们已经可以收工了,但是工程师的好奇心驱使我们要更进一步。不由自主的我们发现了系统的数据存在着大量的重复信息。例如Date Of Birth其实已经在ID中编码了,所以Date Of Birth可以直接从ID中得到,而不必使用额外的空间去它。

经过改良,Person类现在变成了这个样子:

 

1
2
3
4
5
6
// Imports and methods removed to improve readability
public class Person {
    private String id;
    private String forename;
    private String surname;
}

 

 

重新跑一边测试证实我们的改进的确有效,堆消耗降低到了93MB。但是我们还未满足。

该应用在64位的机器上使用老的JDK6。默认情况下,这么做不能 压缩普通对象的指针的。通过参数”-XX:UseCompressedOops“切换到压缩模式使我们获得了额外的收获,现在我们的内存消耗降低到了73MB。

 

当然,我们还能走的更远。比如基于键值建立B-tree,但这已经开始影响到了代码的可读性,所以我们决定到此为止。降低21.5倍的堆内存应该已经是一个足够好的结果了。

让我们再重复一下学到了什么

如果你对这个实验很好奇,请在此处 下载相关的代码。使用到的的测量工具和其具体描述可以在这篇博文找到。

 

  • 别把同外部模块的整合影响到系统的每一个角落
  • 冗余的数据可能带来开销。在可能的情况下尽量消除它
  • 基本数据类型是你最经常打交道的朋友,务必知道些关于它们的工具,如果还没玩过 Trove请立刻开始吧
  • JVM自带的优化技术不可忽视

原文链接:  Nikita Salnikov-Tarnovski ,编译:感谢@ NULL_文龙 的热心翻译

译文链接: http://blog.jobbole.com/40666/

代码审查——提高代码质量的终极武器

摘要:SmartBear公司发起一项调查研究2013年代码审查的使用情况并从超过650名专业开发人士那收集了“实践经验”,结果非常有趣。90%的受访者表示代码审查提高软件质量;74%表示代码审查还有助于跨团队之间知识共享。

如果糟糕的软件是我们的克星,那么优秀的代码就是解药。

软件无法工作是件非常恼人的事!而这种情况往往是由于糟糕的代码所致。在一个项目中,如果开发者孤军奋战,这种情况出现的几率就会增大。

幸运的是,团队中的一些成员愿意贡献自己的空闲时间来改善软件质量。通常,这些人就是我们常说的QA测试者——他们坚持不懈地寻找bug。这里有一个最佳实践方式能够更有效地识别软件代码中的缺陷——同行代码审查(peer code review)。

研究表明,同行代码审查是寻找代码缺陷最高效的方法。有分析称超过12000软件代码使用同行代码审查可以完成60%的效率,而使用单元测试只有25%。每隔一小时使用同行代码审查,可以为开发团队的QA测试节省20个小时,这个听起来不错吧。

近日,SmartBear公司发起一项调查研究2013年代码审查的使用情况。超过650名专业开发人士回答了如何使用代码审查以及对性能影响的相关问题并从中收集了“实践经验”,结果非常有趣。

在此次调查中超过70%的受访者参与了不同程度的协同审查,结果发现,那些做代码审查的比不做代码审查的软件整体质量满意度高达2倍。

我们从中抽取了一个问题,一起来看下代码审查能带来哪些好处。

Q:你认为代码审查最大的好处是什么?

如图所示,显而易见,代码审查最大好处莫过于提高软件质量;74%的受访者表示,代码审查还有助于跨团队之间知识共享;有60%的受访者还发现,代码审查有助于团队之间相互指导及提升协作。

由此可以看出:

 

  1. 代码审查是提高开发团队技能以及保持团队迭代更新最有效的最佳实践方法。
  2. 代码审查工具辅助文档过程本身允许大家互相学习和更正评论。
  3. 假如做代码审查的开发者碰巧没坐在你边上或者开发团队遍布全球或者某个疯狂的同事从凌晨1点工作到5点,那么代码审查工具可追踪任何一条评论以方便你在空闲的时候查看。

 

代码审查不仅能帮助你确保评审、改进代码,还能为开发人员节省大量的时间,帮助你拯救因糟糕软件无法正常运行而带来的压力。

英文出自:Smartbear

http://www.csdn.net/article/2013-06-03/2815518-code-review

较新的文章

Copyright © 2024 优大网 浙ICP备13002865号

回到顶部 ↑