如何处理“java.lang.OutOfMemoryError:Java堆空间”错误?

问题描述 投票:368回答:18

我正在Java 5上编写一个客户端Swing应用程序(图形字体设计器)。最近,我遇到java.lang.OutOfMemoryError: Java heap space错误,因为我对内存使用情况并不保守。用户可以打开无限数量的文件,程序将打开的对象保存在内存中。经过快速研究后,我发现Ergonomics in the 5.0 Java Virtual Machine和其他人在Windows机器上说JVM默认最大堆大小为64MB

鉴于这种情况,我该如何处理这种约束?

我可以使用java的命令行选项增加最大堆大小,但这需要找出可用的RAM并编写一些启动程序或脚本。此外,增加到一些有限的最大值并不能最终摆脱这个问题。

我可以重写我的一些代码来经常将对象持久化到文件系统(使用数据库是一回事)来释放内存。它可以工作,但它可能也很重要。

如果您可以向我指出上述想法的细节或某些替代方案,如自动虚拟内存,动态扩展堆大小,这将是很好的。

java java-ee jvm out-of-memory heap-memory
18个回答
224
投票

无论您运行的是什么平台,最终都可以使用有限的最大堆。在Windows 32位中,这是围绕2GB(不是专门堆,而是每个进程的总内存量)。只是Java选择使默认值更小(可能是因为程序员无法创建具有失控内存分配的程序而不会遇到此问题并且必须仔细检查他们正在做什么)。

因此,您可以采用多种方法来确定所需的内存量或减少所使用的内存量。垃圾收集语言(如Java或C#)的一个常见错误是保留对不再使用的对象的引用,或者在可以重用它们时分配许多对象。只要对象具有对它们的引用,它们将继续使用堆空间,因为垃圾收集器不会删除它们。

在这种情况下,您可以使用Java内存分析器来确定程序中的哪些方法正在分配大量对象,然后确定是否有方法确保它们不再被引用,或者首先不分配它们。我过去使用的一个选项是“JMP”http://www.khelekore.org/jmp/

如果由于某种原因确定要分配这些对象,并且需要保留引用(取决于您正在执行的操作,可能就是这种情况),则只需在启动程序时增加最大堆大小。但是,一旦进行了内存分析并了解了对象的分配方式,您应该更好地了解所需的内存量。

一般情况下,如果您不能保证您的程序将在某些有限的内存中运行(可能取决于输入大小),您将始终遇到此问题。只有在耗尽所有这些后才需要查看缓存对象到磁盘等。此时你应该有一个很好的理由说“我需要Xgb内存”,你不能通过改进来解决它你的算法或内存分配模式。通常,这通常只适用于在大型数据集(如数据库或某些科学分析程序)上运行的算法,然后缓存和内存映射IO等技术变得有用。


7
投票

在java中解决OutOfMemoryError的简单方法是通过使用JVM选项-Xmx512M来增加最大堆大小,这将立即解决您的OutOfMemoryError。当我在构建项目时在Eclipse,Maven或ANT中获得OutOfMemoryError时,这是我的首选解决方案,因为根据项目的大小,您可以轻松地耗尽内存。

下面是增加JVM最大堆大小的示例,如果在java应用程序中设置堆大小,最好将-Xmx保持为-Xms比例为1:1或1:1.5。

export JVM_ARGS="-Xms1024m -Xmx1024m"

Reference Link


7
投票

默认情况下,开发JVM使用小尺寸和小配置来处理其他与性能相关的功能。但对于生产,您可以调整,例如(另外它可以存在特定于Application Server的配置) - >(如果仍然没有足够的内存来满足请求并且堆已经达到最大大小,则会发生OutOfMemoryError)

-Xms<size>        set initial Java heap size
-Xmx<size>        set maximum Java heap size
-Xss<size>        set java thread stack size

-XX:ParallelGCThreads=8
-XX:+CMSClassUnloadingEnabled
-XX:InitiatingHeapOccupancyPercent=70
-XX:+UnlockDiagnosticVMOptions
-XX:+UseConcMarkSweepGC
-Xms512m
-Xmx8192m
-XX:MaxPermSize=256m (in java 8 optional)

例如:在Linux平台上用于生产模式的首选设置。

用这种方式下载和配置服务器http://www.ehowstuff.com/how-to-install-and-setup-apache-tomcat-8-on-centos-7-1-rhel-7/

1.在文件夹/ opt / tomcat / bin /上创建setenv.sh文件

   touch /opt/tomcat/bin/setenv.sh

2.打开并写入此参数以设置优选模式。

nano  /opt/tomcat/bin/setenv.sh 

export CATALINA_OPTS="$CATALINA_OPTS -XX:ParallelGCThreads=8"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+CMSClassUnloadingEnabled"
export CATALINA_OPTS="$CATALINA_OPTS -XX:InitiatingHeapOccupancyPercent=70"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UnlockDiagnosticVMOptions"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseConcMarkSweepGC"
export CATALINA_OPTS="$CATALINA_OPTS -Xms512m"
export CATALINA_OPTS="$CATALINA_OPTS -Xmx8192m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxMetaspaceSize=256M"

3.service tomcat restart

请注意,JVM使用的内存多于堆。例如,Java方法,线程堆栈和本机句柄分配在与堆不同的内存中,以及JVM内部数据结构中。


7
投票

我从Java堆大小遇到了同样的问题。

如果您使用的是java 5(1.5),我有两个解决方案。

  1. 只需安装jdk1.6并转到eclipse的首选项并设置jav1 1.6的jre路径。
  2. 检查你的VM参数,让它成为它的任何东西。只需在VM参数中的所有参数下面添加一行-Xms512m -Xmx512m -XX:MaxPermSize = ... m(192m)。

我认为它会起作用......


7
投票

我在其他地方读到你可以尝试 - 捕获java.lang.OutOfMemoryError并在catch块上,你可以释放你知道可能使用大量内存,关闭连接等所有资源,然后做一个System.gc()然后重试无论你打算做什么。

另一种方式是,虽然,我不知道这是否可行,但我目前正在测试它是否适用于我的应用程序。

想法是通过调用System.gc()来进行垃圾收集,这已知会增加可用内存。执行内存吞噬代码后,您可以继续检查这一点。

//Mimimum acceptable free memory you think your app needs
long minRunningMemory = (1024*1024);

Runtime runtime = Runtime.getRuntime();

if(runtime.freeMemory()<minRunningMemory)
 System.gc();

7
投票

如果您需要在运行时监视内存使用情况,java.lang.management包提供了MBeans,可用于监视VM中的内存池(例如,eden空间,tenured generation等),以及垃圾收集行为。

这些MBean报告的可用堆空间将根据GC行为而有很大差异,特别是如果您的应用程序生成大量稍后GC编辑的对象。一种可能的方法是在每个完整GC之后监视可用堆空间,您可以使用它来通过持久化对象来决定是否释放内存。

最终,您最好的选择是尽可能地限制记忆力,同时保持性能可接受。正如先前的评论所指出的那样,内存总是有限的,但你的应用应该有一个处理内存耗尽的策略。


5
投票

请注意,如果在部署情况下需要此项,请考虑使用Java WebStart(具有“ondisk”版本,而不是网络版本 - 可能在Java 6u10及更高版本中),因为它允许您在十字架中指定JVM的各种参数平台方式。

否则,您将需要一个特定于操作系统的启动器来设置您需要的参数。


1
投票

如果在Wildfly 8和JDK1.8中发生此问题,那么我们需要指定MaxMetaSpace设置而不是PermGen设置。

例如,我们需要在wildfly的setenv.sh文件中添加以下配置。 JAVA_OPTS="$JAVA_OPTS -XX:MaxMetaspaceSize=256M"

有关更多信息,请查看Wildfly Heap Issue


1
投票

关于netbeans,您可以设置最大堆大小来解决问题。

转到'运行',然后 - >'设置项目配置' - >'自定义' - >'运行'其弹出窗口 - >'VM选项' - >填写'-Xms2048m -Xmx2048m' 。


1
投票

如果你继续分配和保持对象的引用,你将填满你拥有的任何数量的内存。

一个选项是在切换标签时关闭和打开透明文件(只保留指向文件的指针,当用户切换标签时,关闭并清理所有对象......它会使文件变慢...但是...),并且可能只在内存中保留3或4个文件。

您应该做的其他事情是,当用户打开文件,加载它并拦截任何OutOfMemoryError,然后(因为无法打开文件)关闭该文件,清理其对象并警告用户他应该关闭未使用的文件文件。

您对动态扩展虚拟内存的想法并没有解决问题,因为机器受资源限制,因此您应该小心处理内存问题(或者至少要小心它们)。

我看到内存泄漏的一些提示是:

- >请记住,如果你把东西放入一个集合中然后忘了它,你仍然有一个强烈的参考,所以取消集合,清理它或用它做一些事情......如果不是你会找到一个内存泄漏很难找到。

- >也许,使用弱引用的集合(weakhashmap ...)可以帮助解决内存问题,但是你必须小心它,因为你可能会发现你所寻找的对象已经被收集了。

- >我发现的另一个想法是开发一个存储在最少使用和透明加载的数据库对象上的持久集合。这可能是最好的方法......


110
投票

使用命令行选项-Xmx运行Java,该选项设置堆的最大大小。

See here for details


83
投票

您可以为每个项目指定项目所需的堆空间

以下是Eclipse Helios / Juno / Kepler:

鼠标右键单击

 Run As - Run Configuration - Arguments - Vm Arguments, 

然后加上这个

-Xmx2048m

41
投票

增加堆大小不是“修复”它是“石膏”,100%是临时的。它会在其他地方再次崩溃。要避免这些问题,请编写高性能代码。

  1. 尽可能使用局部变量。
  2. 确保选择正确的对象(EX:String,StringBuffer和StringBuilder之间的选择)
  3. 为您的程序使用一个好的代码系统(EX:使用静态变量VS非静态变量)
  4. 其他可能适用于您的代码的东西。
  5. 尝试使用multiTHREADING移动

31
投票

大警告----在我的办公室,我们发现(在某些Windows机器上)我们无法为Java堆分配超过512米。原来这是因为卡巴斯基反病毒产品安装在其中一些机器上。在卸载该AV产品后,我们发现我们可以分配至少1.6gb,即-Xmx1600m(m是强制性的,否则它将导致另一个错误“太小的初始堆”)工作。

不知道其他AV产品是否会发生这种情况,但可能会发生这种情况,因为AV程序在每个地址空间都保留了一小块内存,从而阻止了单个非常大的分配。


20
投票

VM参数在eclipse中对我有用。如果您使用的是eclipse版本3.4,请执行以下操作

转到Run --> Run Configurations -->然后在maven build下选择项目 - >然后选择选项卡“JRE” - >然后输入-Xmx1024m

或者你可以做Run --> Run Configurations --> select the "JRE" tab -->然后输入-Xmx1024m

这应该增加所有构建/项目的内存堆。以上内存大小为1 GB。您可以优化您想要的方式。


16
投票

是的,使用-Xmx,您可以为JVM配置更多内存。确保不泄漏或浪费记忆。采取堆转储并使用the Eclipse Memory Analyzer分析您的内存消耗。


13
投票

我想添加来自oracle trouble shooting文章的建议。

线程thread_name中的异常:java.lang.OutOfMemoryError:Java堆空间

详细消息Java堆空间表示无法在Java堆中分配对象。此错误不一定意味着内存泄漏

可能的原因:

  1. 简单的配置问题,其中指定的堆大小不足以满足应用程序的需要。
  2. 应用程序无意中持有对象的引用,这可以防止对象被垃圾回收。
  3. 过度使用终结器。

对于过度使用终结器的应用程序,会出现此错误的另一个潜在来源。如果类具有finalize方法,则该类型的对象在垃圾收集时不会回收它们的空间

在垃圾收集之后,对象排队等待完成,这将在以后发生。终结器由为终结队列提供服务的守护程序线程执行。如果终结器线程无法跟上终结队列,那么Java堆可能会填满,并且会抛出这种类型的OutOfMemoryError异常。

可能导致这种情况的一种情况是,应用程序创建高优先级线程,导致最终化队列以比终结器线程为该队列服务的速率更快的速率增加。


8
投票

请遵循以下步骤:

  1. 从tomcat / bin打开catalina.sh
  2. 将JAVA_OPTS更改为 JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1536m -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC"
  3. 重启你的tomcat
© www.soinside.com 2019 - 2024. All rights reserved.