如何处理" java.lang.OutOfMemoryError:Java堆空间"错误(堆大小为64MB)
我正在Java 5上编写客户端Swing应用程序(图形字体设计器)。最近,我遇到了" java.lang.OutOfMemoryError:Java堆空间"错误,因为我对内存的使用并不保守。用户可以打开无限数量的文件,并且程序将打开的对象保留在内存中。经过快速研究,我在5.0 Java虚拟机中发现了人机工程学,其他人则说在Windows计算机上,JVM默认将最大堆大小定为64MB。
在这种情况下,我应该如何处理这种限制?
我可以使用java的命令行选项来增加最大堆大小,但这需要找出可用的RAM并编写一些启动程序或者脚本。此外,增加到某个有限的最大值并不能最终解决这个问题。
我可以重写一些代码以将对象频繁地持久保存到文件系统中(使用数据库是同一回事)以释放内存。它可以工作,但是可能也很多。
如果我们可以将上述想法的细节或者自动虚拟内存等其他替代方案的细节动态地扩展到堆大小,那将是很棒的。
解决方案
回答
最终,无论我们在什么平台上运行,都始终有一个有限的最大堆可以使用。在Windows 32位中,这大约为2gb(不是专门用于堆,而是每个进程的内存总量)。恰恰是Java选择减小默认值(大概是为了让程序员在没有遇到这个问题且不必仔细检查它们在做什么的情况下,无法创建内存分配失控的程序)。
因此,鉴于这种情况,我们可以采用几种方法来确定所需的内存量或者减少正在使用的内存量。 Java或者Cis等垃圾收集语言的一个常见错误是使对我们不再使用的对象的引用保持不变,或者当我们可以重用它们时分配许多对象。只要对象引用了它们,它们将继续使用堆空间,因为垃圾收集器不会删除它们。
在这种情况下,我们可以使用Java内存探查器来确定程序中哪些方法正在分配大量对象,然后确定是否有办法确保不再引用它们,或者首先不分配它们。我过去使用的一个选项是" JMP" http://www.khelekore.org/jmp/。
如果确定出于某种原因分配这些对象,并且需要保留引用(取决于实际情况),则只需在启动程序时增加最大堆大小。但是,一旦执行了内存分析并了解了对象的分配方式,就应该对所需的内存有一个更好的了解。
通常,如果我们不能保证程序将在一定数量的内存中运行(也许取决于输入大小),则总是会遇到此问题。仅在耗尽所有这些之后,我们才需要研究将对象缓存到磁盘等中。在这一点上,我们应该有一个很好的理由说"我需要Xgb的内存",并且我们无法通过改进来解决它算法或者内存分配模式。通常,只有在大型数据集(例如数据库或者某些科学分析程序)上运行的算法才会出现这种情况,然后诸如缓存和内存映射IO之类的技术就变得有用。
回答
使用命令行选项" -Xmx"运行Java,该选项设置堆的最大大小。
有关详细信息,请参见此处。
回答
是的,使用-Xmx可以为JVM配置更多的内存。
为确保我们不会泄漏或者浪费内存。进行堆转储,然后使用Eclipse Memory Analyzer分析内存消耗。
回答
如果我们需要在运行时监视内存使用情况,则java.lang.management包提供了MBean,可用于监视VM中的内存池(例如,eden空间,保有期限的生成等)以及垃圾回收行为。
这些MBean报告的可用堆空间将根据GC行为而有很大的不同,特别是如果应用程序生成许多对象,这些对象随后将由GC处理。一种可能的方法是监视每个完整GC之后的可用堆空间,我们可以使用它来决定是否通过持久化对象来释放内存。
最终,最好的选择是在性能仍然可以接受的同时,尽可能地限制内存的保留。如之前的评论所述,内存总是有限的,但是应用程序应具有应对内存耗尽的策略。
回答
请注意,在我的办公室里,我们发现(在某些Windows机器上)我们不能为Java堆分配超过512m的内存。原来,这是由于其中一些计算机上安装了卡巴斯基反病毒产品所致。卸载该AV产品后,我们发现至少可以分配1.6gb,即-Xmx1600m(否则m是必需的,否则将导致另一个错误"初始堆太小")。
不知道其他AV产品是否会发生这种情况,但是大概是因为AV程序在每个地址空间中都保留了一小块内存,从而阻止了一个非常大的分配,所以这种情况可能正在发生。
回答
请注意,如果在部署情况下需要这样做,请考虑使用Java WebStart(具有" ondisk"版本,而不是Java 6u10及更高版本中可能使用的网络),因为它允许我们在跨平台中为JVM指定各种参数。道路。
否则,我们将需要特定于操作系统的启动器,该启动器可设置所需的参数。
回答
我在其他地方读到了我们可以尝试捕获java.lang.OutOfMemoryError的信息,并且在catch块上,可以释放所有我们可能会使用大量内存的资源,关闭连接等等,然后执行System.gc()然后重新-尝试任何我们想做的。
尽管我不知道这是否行得通,但这是另一种方式,但是我目前正在测试它是否可以在我的应用程序中运行。
这个想法是通过调用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();
回答
如果继续分配和保留对对象的引用,则将填满我们拥有的所有内存。
一种选择是在它们切换选项卡时关闭并打开透明文件(我们仅保持指向文件的指针,而当用户切换选项卡时,关闭并清理所有对象...这会使文件更改速度变慢) ...但是...),并且可能只在内存中保留3或者4个文件。
我们应该做的另一件事是,当用户打开文件,加载文件并拦截任何OutOfMemoryError时,然后(由于无法打开文件)关闭该文件,清理其对象并警告用户应关闭未使用的文件文件。
我们动态扩展虚拟内存的想法无法解决问题,因为计算机的资源有限,因此我们应谨慎处理内存问题(或者至少要谨慎对待它们)。
我看到的有关内存泄漏的一些提示是:
->请记住,如果我们将某个内容放入集合中,然后忘记了它,我们仍然对它有很强的引用,因此可以取消集合,清理它或者对其进行处理...如果没有,我们会发现一个内存泄漏很难找到。
->也许,使用带有弱引用的集合(weakhashmap ...)可以解决内存问题,但是我们必须格外小心,因为我们可能会发现所寻找的对象已被收集。
->我发现的另一个想法是开发一个持久性集合,该集合存储在最少使用和透明加载的数据库对象上。这可能是最好的方法...