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

How to deal with “java.lang.OutOfMemoryError: Java heap space” error?

我在Java 5上编写了一个客户端Swing应用程序(图形字体设计器)。最近,我遇到了java.lang.OutOfMemoryError: Java heap space错误,因为我在内存使用上并不保守。用户可以打开无限数量的文件,程序将打开的对象保存在内存中。在快速研究之后,我在5 Java虚拟机中发现了人机工程学,还有一些人在Windows机器上说JVM默认最大堆大小为EDCOX1(1)。

在这种情况下,我应该如何处理这种约束?

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

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

如果您能向我指出上述想法的细节,或者其他一些选择,比如自动虚拟内存、动态扩展堆大小,那就太好了。


最终,无论您在哪个平台上运行,您都可以使用有限的堆最大值。在Windows32位中,这大约是2GB(不是专门堆,而是每个进程的内存总量)。JAVA选择使默认值更小(可能是程序员不能创建具有失控内存分配的程序而不碰到这个问题,必须精确地检查他们正在做什么)。

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

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

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

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


用命令行选项EDCOX1(1)来运行Java,它设置堆的最大大小。

详情请参阅此处。


您可以指定每个项目需要多少堆空间

以下为Eclipse Helios/Juno/Kepler:

鼠标右键单击

1
 Run As - Run Configuration - Arguments - Vm Arguments,

然后加入这个

1
-Xmx2048m


增加堆的大小不是"修复",而是"灰泥",100%是临时的。它会在别的地方再次坠毁。要避免这些问题,请编写高性能代码。

  • 尽可能使用局部变量。
  • 确保选择正确的对象(例如:在string、stringbuffer和stringbuilder之间进行选择)
  • 为程序使用良好的代码系统(例如:使用静态变量与非静态变量)
  • 其他可以处理代码的东西。
  • 尝试多线程移动

  • 在我的办公室里,我们发现(在一些Windows机器上)我们不能为Java堆分配超过512M。这是由于卡巴斯基的反病毒产品安装在其中一些机器上。卸载该AV产品后,我们发现至少可以分配1.6GB,即-Xmx1600m(M是强制性的,否则会导致另一个错误"初始堆太小")工作。

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


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

    转到Run --> Run Configurations -->,然后选择maven build(maven build)->下的项目,然后选择选项卡"jre"-->然后输入-Xmx1024m

    或者,您可以执行Run --> Run Configurations --> select the"JRE" tab -->,然后输入-Xmx1024m

    这将增加所有生成/项目的内存堆。上述内存大小为1 GB。你可以优化你想要的方式。


    是的,使用-Xmx,您可以为您的JVM配置更多的内存。以确保不会泄漏或浪费内存。获取一个堆转储并使用Eclipse内存分析器来分析内存消耗。


    我想添加Oracle故障排除文章中的建议。

    线程线程名称中的异常:java. Lang.OutOfMeMyLogyError:Java堆空间

    The detail message Java heap space indicates object could not be allocated in the Java heap. This error does not necessarily imply a memory leak

    可能的原因:

  • 简单配置问题,其中指定的堆大小不适合应用程序。

  • 应用程序无意中持有对对象的引用,这可以防止对象被垃圾收集。

  • 定稿机使用过度。

  • One other potential source of this error arises with applications that make excessive use of finalizers. If a class has a finalize method, then objects of that type do not have their space reclaimed at garbage collection time

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

    可能导致这种情况的一种情况是,当应用程序创建高优先级线程时,会导致终结队列以比终结器线程为该队列提供服务的速度更快的速度增长。


    遵循以下步骤:

  • 从Tomcat/bin打开catalina.sh

  • 将Java选项更改为

    1
    2
    3
    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"
  • 重新启动你的Tomcat


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

    根据GC行为,这些MBean报告的可用堆空间会有很大的变化,特别是如果应用程序生成了许多对象,这些对象后来是GC-ED。一种可能的方法是监视每个完整GC之后的可用堆空间,您可以使用这些空间来决定通过持久化对象释放内存。

    最后,您最好的选择是尽可能地限制内存保留,而性能仍然可以接受。正如前面提到的,内存总是有限的,但是你的应用程序应该有一个处理内存耗尽的策略。


    我在其他地方读到过,你可以尝试-catch java.lang.outofmemory错误,在catch块上,你可以释放所有你知道可能使用大量内存的资源,关闭连接等等,然后执行System.gc(),然后重新尝试你要做的事情。

    另一种方法是,虽然,我不知道这是否有效,但我目前正在测试它是否在我的应用程序上有效。

    其思想是通过调用System.gc()来进行垃圾收集,众所周知,这会增加可用内存。在内存占用代码执行之后,您可以继续检查这个问题。

    1
    2
    3
    4
    5
    6
    7
    //Mimimum acceptable free memory you think your app needs
    long minRunningMemory = (1024*1024);

    Runtime runtime = Runtime.getRuntime();

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


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

    如果你使用Java 5(1.5),我有两个解决方案。

  • 只需安装jdk1.6并转到Eclipse的首选项,并按照您安装的那样设置jav1.6的jre路径。

  • 检查你的虚拟机参数,不管它是什么。只需在VM参数中所有参数的下面添加一行-xms512m-xmx512m-xx:maxpermsize=..m(192m)。

  • 我想它会起作用…


    在Java中解决EDOCX1 4的简单方法是通过使用JVM选项EDCOX1(5)来增加最大堆大小,这将立即解决OutOfMeMyCyror错误。当我在Eclipse、Maven或Ant中使用内存错误时,这是我的首选解决方案,因为根据项目的大小,您很容易耗尽内存。

    这里是一个增加JVM的最大堆大小的例子,如果你在Java应用程序中设置堆大小,那么最好保持Xmx -XMS的比例为1:1或1:1.5。

    export JVM_ARGS="-Xms1024m -Xmx1024m"

    参考链路


    默认情况下,对于开发,JVM对其他与性能相关的特性使用较小的大小和较小的配置。但对于生产,您可以进行优化,例如(此外,还可以存在IT应用程序服务器特定的配置)->(如果仍然没有足够的内存来满足请求,并且堆已达到最大大小,则会发生OutOfMemoryError)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    -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文件/

    1
       touch /opt/tomcat/bin/setenv.sh

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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

    Note that the JVM uses more memory than just the heap. For example
    Java methods, thread stacks and native handles are allocated in memory
    separate from the heap, as well as JVM internal data structures.


    请注意,如果在部署情况下需要此,请考虑使用JavaWebScript(带有"OnDead"版本,而不是Java 6U10和以后的网络之一),因为它允许您以跨平台的方式指定JVM的各种参数。

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


    对于netbeans,可以设置最大堆大小来解决这个问题。

    转到"运行",然后在弹出的窗口中选择"-->set project configuration"(设置项目配置)-->"customise"(自定义)-->"run"(运行)-->"vm option"(虚拟机选项)-->"fill in"-xms2048m-xmx2048m)。


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

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

    有关详细信息,请检查WildFly堆问题


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

    一个选项是在切换选项卡时执行透明的文件关闭和打开(您只保留指向该文件的指针,当用户切换选项卡时,关闭和清除所有对象…它会使文件更改变慢…但是…),可能在内存中只保留3或4个文件。

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

    动态扩展虚拟内存的想法并不能解决这个问题,因为机器的资源有限,所以您应该小心处理内存问题(或者至少小心处理它们)。

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

    -->否则,您将发现难以找到的内存泄漏。

    ->也许,使用具有弱引用的集合(weakhashmap…)可以帮助解决内存问题,但您必须小心处理,因为您可能会发现您要查找的对象已被收集。

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