本文包括JVM内存管理、错误产生的原因、内存泄漏的代码示例,最后还会介绍怎么解决这些问题,特别会提到一些性能诊断工具,让你快速的知道问题发生的根本原因。
java的应用程序只被允许使用限定好的memory。在java的application启动的时候,这个内存大小就被规定好了。为了让内存管理更加的智慧,java的memory被分为了两个不同的区域。这两个区域分别被叫做heap space和Permanent Generation(很多书中叫“永久代”)。
这两个区域的大小在JVM启动的时候就被设置好了,是通过两个JVM参数-Xmx and -XX:MaxPermSize来设置的,如果你不专门去设置这两个的大小,那么将会使用平台定义好的默认值。
java.lang.OutOfMemoryError:java heap space 这个错误是在什么情况下发生呢?就是当你的application想要往heap那个空间里添加更多的数据的时候,但heap里却没有足够的空置区域的时候就会发生java heap space错误。
请注意,也许还有足够的物理内存,但是当JVM达到heap的大小限制时,就会抛出java.lang.OutOfMemoryError: Java heap space 错误。
java.lang.OutOfMemoryError这个错误的产生最常见的原因其实很简单:就是你的应用程序需要更多的 heap 空间。引起这个错误的其他原因就比较复杂了,可能是因为你的编程错误引起的,比如下面两种情况:
上代码 简单的例子:
第一个例子很简单 - 下面的Java代码试图分配一个2M整数的数组。 当你编译它并使用12MB的Java堆空间(java -Xmx12m OOM)启动时,它会失败,并返回java.lang.OutOfMemoryError:Java heap space 消息。 使用13MB的Java堆空间,程序就运行正常了。
class OOM {
static final int SIZE=2*1024*1024;
public static void main(String[] a) {
int[] i = new int[SIZE];
}
}
内存泄露的例子:
第二个更现实的例子是内存泄漏。在Java中,当开发人员创建和使用新对象new Integer(5),他们不必自己分配内存 - 这是由Java虚拟机(JVM)来处理。在应用程序的生命周期中,JVM会定期检查内存中的哪些对象仍在使用,哪些不是。未使用的对象可以被丢弃,内存被回收并再次使用。这个过程称为垃圾收集。 JVM中处理集合的相应模块称为垃圾回收器(GC)。
Java的自动内存管理依赖于GC定期查找未使用的对象并删除它们。简化一点我们可以说,Java中的内存泄漏是一种情况,其中一些对象不再由应用程序使用,但垃圾收集无法识别它。因此,这些未使用的对象将无限期地保留在Java堆空间中。这个堆积将最终触发java.lang.OutOfMemoryError:Java heap space 错误。
构造一个满足内存泄漏定义的Java程序是相当容易的,像下面这样:
class KeylessEntry {
static class Key {
Integer id;
Key(Integer id) {
this.id = id;
}
@Override
public int hashCode() {
return id.hashCode();
}
}
public static void main(String[] args) {
Map m = new HashMap();
while (true)
for (int i = 0; i < 10000; i++)
if (!m.containsKey(new Key(i)))
m.put(new Key(i), "Number:" + i);
}
}
当你执行上面的代码,你也许希望它一直没问题的运行下去,并且认为这个缓存方案仅仅会增加到10000个key。然而事实是 key会一直被增加,因为Key这个class里边并没有包含equals()的实现,只是override了hashCode()方法。
最后,随着时间的推移,随着泄漏代码的不断使用,“缓存”结果最终消耗了大量的Java堆空间。 当泄漏的内存填满堆区域中的所有可用内存,并且Garbage Collection无法清除它时,会抛出java.lang.OutOfMemoryError:Java heap space 。
要想内存不再泄漏,其实办法很简单-就是加上 equals()方法的实现,像下面这样:
@Override
public boolean equals(Object o) {
boolean response = false;
if (o instanceof Key) {
response = (((Key)o).id).equals(this.id);
}
return response;
}
怎么解决呢?
在某些情况下,你分配给JVM的heap数量不足以满足在该JVM上运行的应用程序的需求。 在这种情况下,你只需要分配更多的heap就可以了 - 请参见本章末尾如何实现。
然而,在许多情况下,提供更多的Java堆空间不会解决问题。 例如,如果您的应用程序包含内存泄漏,添加更多堆将只是推迟java.lang.OutOfMemoryError:Java heap space 错误。 此外,增加Java堆空间量也会增加影响应用程序吞吐量或延迟的GC暂停时间。
如果你想解决Java堆空间的根本问题,而不是掩盖症状,你需要找出那些分配了最多的内存的那些代码。 换句话说,你需要回答这些问题:
1.哪些对象占用了heap的大部分?
2.这些对象分布在源码中的哪个地方?
此时,你要花上一些时间来解决这些问题了。这里是一个粗略的过程大纲,将帮助您回答上述问题:
获取安全许可,以便从JVM中把heap中的内容dump出来存储到另外一个地方。 “Dumps”基本上就是堆内容的一个快照,你稍后就是在这些dumps中进行分析。由于这些快照可能包含机密信息,例如密码,银行卡号等,所以你必须要获得安全部门的允许。
选择在一个合适的时间进行dump(转储)操作。如果时机不对,堆垃圾可能包含大量的噪音,甚至可能几乎没有什么有用的信息。另一方面,每个堆的dump(转储)会完全“冻结”JVM,这样会占用过多的JVM,这种情况下很可能会影响正常业务的访问,会出现一些性能问题。
专门找一台机器用作dump(转储)。当你要分析8GB的堆,那你就要一台超过8GB的机器来分析堆内容。然后选择一个转储分析软件(我们建议使用Eclipse MAT,你也可以选择其他转储分析软件)。
检测出堆的最大消费者的GC根的路径。我们在这里单独的一篇文章中介绍了这项活动。
接下来,你需要确定源代码中的哪些位置正在分配潜在危险的大量对象。如果你对应用程序的源代码很了解的话,你将能够在几次搜索中做到这一点。
或者,我们建议你使用Plumbr,这是一个jvm性能检测工具,它可以自动检测出问题的根本原因。 在其他性能问题上,它同样可以捕获所有java.lang.OutOfMemoryErrors,并且可以自动的为你整理出那些非常“内存饥饿”(memory-hungry)的数据结构的信息。
Plumbr会在幕后为你收集必要的数据 - 包括有关堆使用的相关数据(只有对象布局图,没有实际数据),以及一些在“堆转储”(heap dump)中找不到的数据。 如果JVM遇到java.lang.OutOfMemoryError,Plumbr还会为您执行必要的数据处理 。 下面是一个Plumbr的有关java.lang.OutOfMemoryError错误的一个例子(分析结果):
没有任何额外的工具或分析,你就可以看到:
有了这些信息,您就可以找到问题的根本原因了,然后把数据结构修剪到它们适合在您的内存池中的级别。
但是,当您从内存分析或阅读Plumbr报告得出的结论是内存使用是合法的,那么就没必要修改源代码了,这时候你就要设置更大的java heap 空间来保证应用程序的运行了。 请更改JVM启动配置,并添加(或增加值,如果存在)以下内容:
-Xmx1024m
上面的配置将给应用程序1024MB的 Java heap space。 您可以使用g或G表示GB,m或M表示MB,k或K表示KB。 例如,下面的几种方式都表示最大Java heap space为1GB:
java -Xmx1073741824 com.mycompany.MyClass
java -Xmx1048576k com.mycompany.MyClass
java -Xmx1024m com.mycompany.MyClass
java -Xmx1g com.mycompany.MyClass
本文分享自 ImportSource 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有