Java内存问题的一些见解
作者:网络转载 发布时间:[ 2015/8/24 11:06:55 ] 推荐标签:编程语言 测试开发技术
一个实际的例子是在JBossWS 1.2.0版本中出现的一个bug(在JBossWS1.2.1版本已经被修复)——“DOMUtils doesn’t clear thread locals”。此问题是ThreadLocal变量导致的,它引用了一个14MB的解析文档。
大型临时对象
大型临时对象在坏的情况下也能导致outofmemoryerror错误或者至少强烈的GC反应。例如,如果非常大的文档(XML、PDF、图片…)必须阅读和处理时。在一个特定的情况下,应用程序几分钟都没有响应或性能非常有限,几乎没有可用的。其中根本原因是垃圾回收反应过于强烈。下面对读取PDF文档的一段代码作了详细分析:
byte tmpData[] = new byte [1024];
int offs = 0;
do{
int readLen = bis.read (tmpData, offs, tmpData.length - offs);
if (readLen == -1)
break;
offs+= readLen;
if (oofs == tmpData.length){
byte newres[] = new byte[tmpData.length + 1024];
System.arraycopy(tmpData, 0, newres, 0, tmpData.length);
tmpData = newres;
}
} while (true);
这些文档采用按固定字节数的方式来读取。首先,他们被读入中字节数组中,然后发送到用户的浏览器中。然而仅仅几个并行请求将会导致堆溢出。由于读取文档采用了极其低效的算法,这将导致问题越来越糟糕。初的想法只是创建1KB的初始字节数组。如果这个数组满了,则一个新的1KB数组将被创建,同时这个老的数组将拷贝到新的数组中。这意味着当读取文档时,一个新数组将被创建,同时将读取的每字节都复制到新数组中。这将导致大量的临时对象和两倍于实际数据大小的内存消耗——数据将被复制。
在处理大量数据时,优化处理逻辑性能是至关重要的。在这种情况下,一个简单的负载测试会显示这一问题。
糟糕的垃圾回收器配置
到目前为止,在所提到的情境中出现的问题基本都是由应用程序代码所导致的。然而,这些原因的根源是由于垃圾回收器配置错误,或者丢失。我常常看到用户相信他们的应用程序服务器的默认设置,同时也相信应用服务器的开发者了解哪些是自己的程序好的。无论如何,堆的配置很大程度上取决于应用程序和实际使用场景。根据场景来调整参数,应用程序才能更好地执行。和一批执行长期任务的应用程序相比,一个执行大量短而持久的应用程序配置起来是完全不同的。此外,实际的配置还取决于JVM使用情况。对IBM来说,什么才能使Sun Jvm正常运行可能是一场噩梦(或至少是不理想的)。配置错误的垃圾收集器通常不会立即被确认为性能问题的根源(除非你监控了垃圾收集器的活动)。通常我们肉眼可见的问题都是响应过慢。同时,理解垃圾回收活动与响应时间的关系也是不明显的。如果垃圾回收的时间与响应时间没什么关联,人们通常会发现一个非常复杂的性能问题。响应时间和执行时间度量问题主要体现在应用程序——对于这种现象,在不同的地方都没有一个明显的模式。
下图显示了事务指标与垃圾收集时间在dynaTrace中的关系。我发现了一些情况,关于垃圾回收器的优化问题。人们正打算花几周的时间去解决如何在几分钟内设置解决性能问题。
类加载器内存泄露
在谈到内存泄漏时,大部分人主要认为是堆中的对象。除了对象,类和常量也是托管在堆中。根据JVM,它们被放入堆中特定的区域。例如Sun JVM使用所谓的代或PermGen。通常情况下,类被放入堆中好几次。仅仅是因为他们已经被不同的类加载器加载。在现代化企业级应用程序中,加载类的内存占用能够达到几百MB。
关键是避免无谓地增加类的大小。一个很好的例子是大量字符串常量的定义——例如在GUI应用程序中。这里所有的文本通常存储在常量。而使用常量字符串的方法原则上是一个好的设计方法,内存消耗不应该被忽视。在真实的情况下,在一个国际化应用程序中,所有常量都会被定义为各种语言。一个很不起眼的代码错误都会影响到已经被加载的类。终的结果是,在应用程序的代中,JVM将出现OutOfMemoryError 错误,同时崩溃。
应用服务器还面临着类加载器泄漏的问题。这些泄漏的原因主要是因为类加载器不能被垃圾回收,因为类加载器中的类的一个对象仍然活着。结果,这些类并不打算释放这些内存占用。而现在,这个问题已经被J2EE 应用程序服务器很好的解决了,它似乎更常出现在OSGI-based应用程序环境。
总结
在Java应用程序中内存问题通常是多方面的,这容易导致性能和可扩展性的问题。特别是在有着大量并行用户的J2EE应用程序中,内存管理必须是应用程序体系结构的核心部分。然而垃圾回收器对于那些未使用的对象是否被清理并不关心,所以开发人员还是需要适当的内存管理。此外,应用程序内存管理设计是应用程序配置的核心部分。
你的经验
这些都是我在现实世界应用程序中发现的反模式。如果你有额外的反模式或共同的问题,我很愿意更多地了解他们。
关于作者
这篇文章基于我和codecentric的作者Mirko Novakovic共同研究的性能反模式系列。
其他感兴趣的博客
由于性能反模式是我的爱好,我将定期发布关于反模式的帖子。它们将会从这些帖子里选择你们可能感兴趣的一篇帖子。
关于Hibernate缓存的一些见解
远程问题
如果你想要了解更多关于如何解决像本文中提到的内存问题,和其他一些java运行坏境相关的问题,你也许对我同事近一本关于持续应用程序性能管理的白皮书感兴趣。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
Java性能测试有哪些不为众人所知的原则?Java设计模式??装饰者模式谈谈Java中遍历Map的几种方法Java Web入门必知你需要理解的Java反射机制知识总结编写更好的Java单元测试的7个技巧编程常用的几种时间戳转换(java .net 数据库)适合Java开发者学习的Python入门教程Java webdriver如何获取浏览器新窗口中的元素?Java重写与重载(区别与用途)Java变量的分类与初始化JavaScript有这几种测试分类Java有哪四个核心技术?给 Java开发者的10个大数据工具和框架Java中几个常用设计模式汇总java生态圈常用技术框架、开源中间件,系统架构及经典案例等

sales@spasvo.com