如何处理“java.lang.OutOfMemoryError:Java 堆空间”错误?
-
09-06-2019 - |
题
我正在写一个客户端 摇摆 应用程序(图形字体设计器) 爪哇5. 。最近,我遇到了 java.lang.OutOfMemoryError: Java heap space
错误是因为我对内存使用并不保守。用户可以打开无限数量的文件,并且程序将打开的对象保留在内存中。经过快速研究后我发现 5.0 Java 虚拟机中的人体工程学 和其他人说在 Windows 机器上 JVM 默认最大堆大小为 64MB
.
鉴于这种情况,我应该如何处理这个约束?
我可以增加 最大堆大小 使用 命令行 java 的选项,但这需要弄清楚可用的 RAM 并编写一些启动程序或脚本。此外,增加一些 有限 最大没有 最终 摆脱这个问题。
我可以重写一些代码以频繁地将对象持久保存到文件系统(使用数据库是同一件事)以释放内存。它可能有效,但可能也需要很多工作。
如果您可以向我指出上述想法的细节或一些替代方案,例如 自动虚拟内存,动态扩展堆大小, , 那挺棒的。
解决方案
最终,无论您在什么平台上运行,您始终可以使用有限的最大堆。在 Windows 32 位中,大约是这样 2GB
(不是特定的堆,而是每个进程的内存总量)。碰巧的是,Java 选择将默认值变小(大概是这样,程序员就无法创建内存分配失控的程序,而不会遇到此问题,并且必须准确检查它们在做什么)。
因此,您可以采取多种方法来确定所需的内存量或减少正在使用的内存量。Java 或 C# 等垃圾收集语言的一个常见错误是保留对您所使用的对象的引用。 不再 正在使用或分配许多对象 重用 而是他们。只要对象有对它们的引用,它们就会继续使用堆空间,因为垃圾收集器不会删除它们。
在这种情况下,您可以使用 Java 内存分析器来确定程序中的哪些方法正在分配大量对象,然后确定是否有办法确保它们不再被引用,或者首先不分配它们。我过去使用过的一个选项是“JMP” http://www.khelekore.org/jmp/.
如果您确定分配这些对象是有原因的,并且需要保留引用(这取决于您正在执行的操作,可能是这种情况),那么您只需要在启动程序时增加最大堆大小。然而,一旦您进行了内存分析并了解了对象是如何分配的,您应该更好地了解您需要多少内存。
一般来说,如果您不能保证您的程序将在一定数量的内存中运行(可能取决于输入大小),您总是会遇到这个问题。只有在耗尽所有这些之后,您才需要研究将对象缓存到磁盘等。此时,您应该有一个很好的理由说“我需要 Xgb 内存”,并且您无法通过改进算法或内存分配模式来解决它。一般来说,只有在大型数据集(例如数据库或某些科学分析程序)上运行的算法才会出现这种情况,然后缓存和内存映射 IO 等技术就会变得有用。
其他提示
使用命令行选项运行 Java -Xmx
, ,这设置了 最大限度 堆的大小。
你可以指定 每 预测您的项目需要多少堆空间
以下是针对 日食太阳神/朱诺/开普勒:
鼠标右键单击
Run As - Run Configuration - Arguments - Vm Arguments,
然后添加这个
-Xmx2048m
增加堆大小并不是“修复”,而是“膏药”,100% 是临时的。它会在其他地方再次崩溃。为了避免这些问题,请编写高性能代码。
- 尽可能使用局部变量。
- 确保选择正确的对象(例如:String、StringBuffer 和 StringBuilder 之间的选择)
- 为您的程序使用良好的代码系统(例如:使用静态变量VS非静态变量)
- 其他可以在您的代码上工作的东西。
- 尝试使用多线程移动
重要警告 ---- 在我的办公室,我们发现(在某些 Windows 机器上)我们无法为 Java 堆分配超过 512m 的空间。事实证明,这是由于其中一些计算机上安装了卡巴斯基反病毒产品所致。卸载该AV产品后,我们发现至少可以分配1.6GB,即 -Xmx1600m
(m 是强制性的,否则会导致另一个错误“初始堆太小”)有效。
不知道其他 AV 产品是否会发生这种情况,但可能会发生这种情况,因为 AV 程序在每个地址空间中保留一小块内存,从而防止单个非常大的分配。
VM 参数在 Eclipse 中对我有用。如果您使用的是 eclipse 3.4 版本,请执行以下操作
去 Run --> Run Configurations -->
然后选择maven build下的项目-->然后选择选项卡“JRE”-->然后输入 -Xmx1024m
.
或者你可以这样做 Run --> Run Configurations --> select the "JRE" tab -->
然后输入-Xmx1024m
这应该会增加所有构建/项目的内存堆。上述内存大小为1GB。您可以按照您想要的方式进行优化。
是的,与 -Xmx
您可以为 JVM 配置更多内存。确保您不会泄漏或浪费内存。进行堆转储并使用 Eclipse 内存分析器 分析您的内存消耗。
我想添加来自 oracle 的建议 故障排除 文章。
线程 thread_name 中出现异常: java.lang.OutOfMemory错误:Java堆空间
详细消息 Java 堆空间指示无法在 Java 堆中分配对象。此错误并不一定意味着内存泄漏
可能的原因:
简单的配置问题, ,其中指定的堆大小不足以满足应用程序的需要。
应用程序无意中保存了对对象的引用, ,这可以防止对象被垃圾收集。
过度使用终结器.
此错误的另一个潜在来源是过度使用终结器的应用程序。如果一个类具有 Finalize 方法,则该类型的对象在垃圾收集时不会回收其空间
后 垃圾收集, ,对象排队等待 定稿, ,这发生在稍后的时间。 终结器 由为终结队列提供服务的守护线程执行。如果 终结器 线程无法跟上终结队列,那么 Java 堆可能会被填满,这种类型的 内存不足错误 将抛出异常。
可能导致这种情况的一种情况是当应用程序创建 高优先级线程 导致 定稿 队列以比终结器线程为该队列提供服务的速率更快的速率增加。
请按照以下步骤操作:
打开
catalina.sh
来自 tomcat/bin。将 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"
重新启动你的tomcat
简单的方法解决 OutOfMemoryError
在java中是通过使用JVM选项来增加最大堆大小 -Xmx512M
, ,这将立即解决您的 OutOfMemoryError。当我在构建项目时在 Eclipse、Maven 或 ANT 中遇到 OutOfMemoryError 时,这是我的首选解决方案,因为根据项目的大小,您很容易耗尽内存。
下面是增加 JVM 最大堆大小的示例,如果您在 Java 应用程序中设置堆大小,则最好保持 -Xmx 与 -Xms 的比例为 1:1 或 1:1.5。
export JVM_ARGS="-Xms1024m -Xmx1024m"
默认情况下,开发 JVM 使用小尺寸和小配置来实现其他性能相关的功能。但对于生产,您可以调整,例如(此外,可以存在应用程序服务器特定的配置)->(如果仍然没有足够的内存来满足请求并且堆已经达到最大大小,则会发生 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内部数据结构。
我也遇到过与java堆大小相同的问题。
如果您使用 java 5(1.5),我有两个解决方案。
只需安装jdk1.6并进入eclipse的首选项并按照您安装的方式设置jav1 1.6的jre路径即可。
检查你的VM参数并让它成为任何东西。只需在VM参数中的所有参数中添加一行,即-XMS512M -XMX512M -XX:MaxPermsize = ... M(192m)。
我认为它会起作用...
我在其他地方读到您可以尝试 - 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();
如果您需要在运行时监控内存使用情况, java.lang.management
套餐优惠 MBeans
可用于监视虚拟机中的内存池(例如,伊甸园空间、终身代等)以及垃圾收集行为。
这些 MBean 报告的可用堆空间将根据 GC 行为而有很大差异,特别是当您的应用程序生成大量稍后进行 GC 的对象时。一种可能的方法是在每次 full-GC 之后监视可用堆空间,您可以使用它来决定是否通过持久化对象来释放内存。
最终,最好的选择是在性能保持可接受的情况下尽可能限制内存保留。正如之前的评论所指出的,内存总是有限的,但您的应用程序应该有一个处理内存耗尽的策略。
请注意,如果您在部署情况下需要此功能,请考虑使用 Java WebStart(带有“ondisk”版本,而不是网络版本 - 在 Java 6u10 及更高版本中可能),因为它允许您以交叉方式指定 JVM 的各种参数。平台方式。
否则,您将需要一个特定于操作系统的启动器来设置您需要的参数。
如果这个问题发生在Wildfly 8和JDK1.8中,那么我们需要指定MaxMetaSpace设置而不是PermGen设置。
例如我们需要在wildfly的setenv.sh文件中添加以下配置。JAVA_OPTS="$JAVA_OPTS -XX:MaxMetaspaceSize=256M"
欲了解更多信息,请查看 Wildfly 堆问题
对于netbeans,您可以设置最大堆大小来解决问题。
转到“运行”,然后-->“设置项目配置”-->“自定义”-->弹出窗口的“运行”-->“VM选项”-->填写“-Xms2048m -Xmx2048m” 。
如果您继续分配和保留对对象的引用,您将填满您拥有的任何内存量。
一种选择是在切换选项卡时执行透明文件关闭和打开(您只保留指向文件的指针,当用户切换选项卡时,您关闭并清除所有对象...它会使文件更改速度变慢......但是...),并且内存中可能只保留 3 或 4 个文件。
您应该做的另一件事是,当用户打开文件时,加载它并拦截任何 OutOfMemoryError,然后(因为不可能打开该文件)关闭该文件,清理其对象并警告用户应该关闭未使用的文件文件。
您动态扩展虚拟内存的想法并不能解决问题,因为机器的资源有限,因此您应该小心并处理内存问题(或者至少要小心它们)。
我看到的有关内存泄漏的一些提示是:
--> 请记住,如果您将某些内容放入集合中,然后忘记了它,那么您仍然对它有很强的引用,因此使集合无效,清理它或用它做一些事情......如果没有的话你会发现内存泄漏很难发现。
--> 也许,使用带有弱引用的集合(weakhashmap...)可以帮助解决内存问题,但是你 必须 小心一点,因为你可能会发现你要找的东西已经被收集了。
--> 我发现的另一个想法是开发一个持久集合,存储在最少使用和透明加载的数据库对象上。这可能是最好的方法......