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


416

我正在Java 5上编写客户端Swing应用程序(图形字体设计器)。最近,我遇到了错误,因为我对内存的使用并不保守。用户可以打开无限数量的文件,并且程序将打开的对象保留在内存中。经过快速研究,我在5.0 Java虚拟机中发现了人机工程学,其他人则说在Windows计算机上,JVM默认将最大堆大小设置为。java.lang.OutOfMemoryError: Java heap space64MB

在这种情况下,我应该如何处理这种限制?

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

我可以重写一些代码以将对象频繁地持久保存到文件系统(使用数据库是同一回事)以释放内存。它可以工作,但是可能也很多。

如果您可以将上述想法的细节或诸如自动虚拟内存,动态扩展堆大小之类的替代方法的细节介绍给我,那将会很棒。


默认的最大堆大小为64 MB,来自J2SE 5.0之前的版本。有关J2SE 8.0的信息,请参阅docs.oracle.com/javase/8/docs/technotes/guides/vm/…上的 “垃圾收集器人体工程学” 。
安迪·托马斯

如果由于每个OOM问题都涉及到这个问题而落在这里,请确保还签出:stackoverflow.com/questions/299659/… 它提供了在OOM之前“及时”清理内存引用的解决方案。SoftReferences可能是解决您实际问题的工具。
史蒂夫·斯坦纳

Answers:


244

最终,无论您在什么平台上运行,都始终可以使用有限的最大堆。在Windows 32位环境中2GB(不是专门用于堆,而是每个进程的内存总量)。碰巧的是Java选择减小默认值(大概是为了让程序员在没有遇到这个问题且不必仔细检查它们在做什么的情况下,无法创建内存分配失控的程序)。

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

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

如果确定您出于某种原因分配这些对象,并且需要保留引用(取决于实际情况),则仅需要在启动程序时增加最大堆大小。但是,一旦执行了内存分析并了解了如何分配对象,就应该对所需的内存有一个更好的了解。

通常,如果您不能保证程序将在一定数量的内存中运行(也许取决于输入大小),则总是会遇到此问题。仅在用尽所有这些之后,您才需要研究将对象缓存到磁盘等中。在这一点上,您应该有一个很好的理由说“我需要Xgb的内存”,您不能通过改进来解决它。您的算法或内存分配模式。通常,只有在大型数据集(例如数据库或某些科学分析程序)上运行的算法才会出现这种情况,然后诸如缓存和内存映射IO之类的技术就变得有用。


6
OpenJDK和OracleJDK捆绑了分析器-jvisualvm。如果您想获得更多便利,建议您购买商业版Yourkit。
Petr Gladkikh


85

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

以下是Eclipse Helios / Juno / Kepler的代码

右键点击

 Run As - Run Configuration - Arguments - Vm Arguments, 

然后添加这个

-Xmx2048m

1
嗨,bighostkim和cuongHuyTo,“ Arguments”在哪里。我可以看到运行配置。请给我打电话。我需要从gmail下载和存储近2000个联系人。由于内存
不足

@AndroiRaji:右键单击具有可运行主程序(即“ public static void main(String [] args)”)的Java类,然后选择“运行方式-运行配置”。然后“ Arguments”是Main之后的选项卡(您可以看到Main,Arguments,JRE,Classpath,Source,Environment,Common等选项卡)。
CuongHuy14年

47

增大堆大小不是“固定”,而是“石膏”,100%临时。它将在其他地方再次崩溃。为避免这些问题,请编写高性能代码。

  1. 尽可能使用局部变量。
  2. 确保选择正确的对象(例如:在String,StringBuffer和StringBuilder之间选择)
  3. 为您的程序使用良好的代码系统(例如:使用静态变量VS非静态变量)
  4. 其他可能在您的代码上起作用的东西。
  5. 尝试移动多线程

这是真的。我正在尝试解决一个在AWT线程上获取OOM的问题,但是如果我使用其他新线程,则不会出现OOM问题。我在网上只能找到增加AWT线程的堆大小的信息。
Ashish

@Ash:是的,解决核心问题而不是寻找灰泥。
柠檬汁

Java中的垃圾回收和内存管理方法应该解决了其前辈的所有这些malloc-dealloc复杂问题:(当然,我完全同意这个答案,这是一个耻辱,默认值不能使使用精益数据编写代码变得容易
达沃斯

31

请注意,在我的办公室,我们发现(在某些Windows机器上)我们不能为Java堆分配超过512m的内存。原来,这是由于其中一些计算机上安装了卡巴斯基反病毒产品所致。卸载该AV产品后,我们发现可以分配至少1.6gb,即 -Xmx1600m(m是强制性的,否则将导致另一个错误“初始堆太小”)。

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


22

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

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

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

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


18

是的,-Xmx您可以为JVM配置更多的内存。为确保您不会泄漏或浪费内存。进行堆转储,然后使用Eclipse Memory Analyzer分析您的内存消耗。


JVMJ9VM007E无法识别的命令行选项:-Xmx无法创建Java虚拟机。Downvote
Philip Rego

17

我想从oracle 故障排除文章中添加建议。

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

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

可能的原因:

  1. 简单配置问题,其中指定的堆大小不足以用于应用程序。

  2. 应用程序无意间持有对对象的引用,这可以防止对象被垃圾回收。

  3. 过多使用终结器

过度使用终结器的应用程序可能导致此错误。如果类具有finalize方法,则该类型的对象在垃圾回收时不会回收其空间

以后垃圾收集,对象排队等待最后确定,发生在稍后的时间。 终结器由为终结队列提供服务的守护程序线程执行。如果终结器线程无法跟上终结器队列的速度,则Java堆可能会填满,并且会抛出此类OutOfMemoryError异常。

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


9

请按照以下步骤操作:

  1. catalina.sh从tomcat / bin 打开。

  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


8

我在其他地方可以尝试读取-catch 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();

6
通常,我认为JVM会更喜欢垃圾回收(GC),而不是抛出OutOfMemoryError。在OutOfMemoryError之后显式调用System.gc()可能对某些VM /配置有所帮助,但我不希望它在一般情况下能很好地工作。但是,删除不必要的对象引用绝对可以在几乎所有情况下提供帮助。
Mike Clark 2010年

6
@mwangi直接从代码中调用System.gc()通常是一个坏主意。只是建议 JVM应该执行GC,但是绝对不能保证会执行GC 。

7

OutOfMemoryError用Java 解决的一个简单方法是使用JVM选项增加最大堆大小-Xmx512M,这将立即解决您的OutOfMemoryError。当我在构建项目时在Eclipse,Maven或ANT中遇到OutOfMemoryError时,这是我的首选解决方案,因为根据项目的大小,您很容易耗尽内存。

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

export JVM_ARGS="-Xms1024m -Xmx1024m"

参考链接


1
知道为什么我们需要保持1:1或1:1.5的比例吗?
ernesto 2012年

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.management软件包提供的MBeans内容可用于监视VM中的内存池(例如,eden空间,使用期限生成等)以及垃圾回收行为。

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

最终,最好的选择是在性能仍然可以接受的同时,尽可能地限制内存的保留。如之前的评论所述,内存始终有限,但是您的应用程序应具有应对内存耗尽的策略。


5

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

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


Java WebStart正在逐步淘汰。我还没有合适的替代品。
托尔比约恩Ravn的安徒生

1

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

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

有关更多信息,请检查Wildfly堆问题


1

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

转到“运行”,然后->“设置项目配置”->“自定义”->弹出窗口的“运行”->“ VM Option”->填写“ -Xms2048m -Xmx2048m” 。


1

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

一种选择是在它们切换选项卡时关闭并打开透明文件(您仅保持指向文件的指针,而当用户切换选项卡时,关闭并清理所有对象...这会使文件更改速度变慢) ...但是...),并且可能只在内存中保留3或4个文件。

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

您动态扩展虚拟内存的想法无法解决问题,因为计算机的资源有限,因此您应谨慎处理内存问题(或至少要谨慎对待它们)。

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

->请记住,如果您将某个内容放入集合中,然后忘记了它,您仍然对它有很强的引用,因此可以取消集合,清理它或对其进行处理...如果没有,您会发现一个内存泄漏很难找到。

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

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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.