避免"GC overhead limit exceeded"错误



经过很多努力,我似乎无法克服获得

超出 GC 开销限制

我的 Java 程序中的错误。

它发生在一个包含大型字符串操作、许多对象列表和对 DB 的访问的大型方法中。 我尝试了以下方法:

  1. 使用每个 ArrayList 后,我添加了:list=new ArrayList<>(); list=null;
  2. 对于字符串,而不是例如 50 个附加 (str+="....) 我尝试在总文本中附加一个
  3. 每次访问数据库后,我都会关闭语句和结果集。

此方法从 main 调用,如下所示:

for(int i=0; i<L; i++) {
cns = new Console(i);
cns.processData();//this is the method
cns=null;
}

当这个循环被执行 1 或 2 次时,一切都很好。对于 L>=3,几乎可以肯定我会收到垃圾收集器错误。

难道不应该在每次执行方法后我有一个cns=null的事实,强制 GC 并从之前的执行中释放所有内容吗?

在将对象设置为 null 之前,我是否也应该删除对象的所有私有属性?也许放一个Thread.sleep()可以在每次循环后强制 GC

实际上没有理由在每个循环结束时将cns设置为 null。 无论如何,您在循环开始时将其设置为new Console()- 如果可以通过将其设置为 null 来释放任何内容,那么它也将通过将其设置为新对象来释放。

您可以尝试System.gc();调用来建议系统进行垃圾收集,但我不知道这是否会对您有所帮助或使情况变得更糟。 系统已经在尝试垃圾回收 - 如果不是,您就不会收到此错误。

您没有向我们展示您如何构建字符串,但请记住,+=并不是唯一的罪魁祸首。 如果你有类似的东西String s = "Hello " + "to the" + " world";,那就像把它放在三行上并使用+=一样糟糕。 如果这是一个问题,StringBuilder可能是你的朋友。

您可以在错误 java.lang.OutOfMemoryError: 超出 GC 开销限制中阅读答案,以获取有关如何避免此错误的其他一些建议。 对于某些人来说,它似乎是在你几乎(但不完全)失去记忆时触发的。 因此,增加 Java 可用的内存量可能会(也可能不会)有所帮助。

基本上,"超出 GC 开销限制"是可访问数据过多的症状。 堆里装满了无法垃圾收集的东西......"因为他们不是垃圾! JVM一次又一次地运行GC,试图腾出空间。 最终,它决定花太多时间收集垃圾,于是放弃了。 这通常是正确的做法。


您的问题(和其他答案)中的以下想法不是解决方案

  1. 通过调用System.gc()强制 GC 运行无济于事。 GC 已经运行得太频繁了。

  2. null分配给cns将无济于事。 它会立即获得分配给它的其他内容。 此外,没有证据表明Console对象占用了大量内存。

    (请注意,java.io.Console类的构造函数不是public,因此您的示例在编写时没有意义。 也许你真的在打电话给System.getConsole()? 或者也许这是一个不同的Console类?

  3. 在将对象设置为null之前清除对象的私有属性不太可能产生任何影响。 如果对象不可访问,则其属性的值无关紧要。 GC甚至不会看他们。

  4. 打电话给Thread.sleep()不会有什么区别。 GC 在它认为需要时运行。


真正的问题是...我们无法从您提供的证据中确定的内容。 为什么有这么多可访问的数据?

一般而言,两种最可能的解释如下:

  1. 您的应用程序(或您的某个库)在某些数据结构中积累了越来越多的对象,这些数据结构在for循环的单次迭代之后幸存下来。 简而言之,您有内存泄漏。

    要解决此问题,您需要查找存储泄漏;请参阅如何查找 Java 内存泄漏。 (泄漏可能与数据库连接、语句或结果集未关闭有关,但我对此表示怀疑。 如果这些资源变得无法访问,GC 应查找并关闭它们。

  2. 您的应用程序只需要更多内存。 例如,如果对processData的单个调用需要的内存多于可用内存,则无论您尝试让 GC 执行什么操作,您都将获得 OOME。 它无法删除可访问的对象,并且显然无法找到足够的垃圾来足够快。

    要解决这个问题,首先看看是否有办法修改程序,使其需要更少(可访问)内存,这里有几个想法:

    • 如果你在将输出写入OutputStream之前构建一个巨大的字符串来表示输出,Writer或类似。 如果直接写入输出接收器,则会节省内存。

    • 在某些情况下,在组装大字符串时,请考虑使用StringBuilder而不是String串联。 特别是当串联是循环的。

      但是,请注意,1) 在 Java 8 及更早版本中,javac已经为您发出了StringBuilder个序列用于连接表达式,以及 2) 在 Java 9+ 中,javac发出比使用StringBuilder更好的invokedynamic代码;

      • JDK 9/JEP 280:字符串连接永远不会相同

如果这没有帮助,请增加 JVM 的堆大小或将问题拆分为较小的问题。 有些问题只需要大量的内存来解决。

相关内容

  • 没有找到相关文章

最新更新