无法从堆中收集无法访问的对象

时间:2014-08-25 15:46:22

标签: java tomcat garbage-collection jvm heap

我在JVM堆中遇到无法访问的对象(Java 1.7)。从图片中可以看出(图片中的所有类都无法访问),我们有超过74%的对象没有参考,所以应该收集它。这个状态在我们的tomcat 7服务器上运行3周之后,只运行探测监控应用程序,tomcat管理器和我们的webapp,这可能是问题的根源。

我们的应用程序基于JSF 1.2,在客户端上具有状态保存功能,如下图所示 - 主要使用ViewSaveState的char数组。 当我从jVisualVM手动运行GC时,它会删除所有无法访问的对象,并且在堆达到其限制的3周之前一切正常。

如何清除某些物体?

我们的JVM参数

-Dcom.sun.management.jmxremote=true
-Dcom.sun.management.jmxremote.port=29001
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Djava.rmi.server.hostname=
-Dorg.apache.el.parser.SKIP_IDENTIFIER_CHECK=true
-Xms320m
-Xmx2500m
-XX:MaxPermSize=500m
-XX:PermSize=96m
-verbose:gc
-Xloggc:/var/log/gc.log
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails
-Xdebug -Xrunjdwp:transport=dt_socket,address=1044,server=y,suspend=n
-XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC

http://puu.sh/b7mjB/3d23de7d41.png

OutOfMemoryError的STACKTRACES

我认为原因隐藏在其他地方,堆栈跟踪来自app的不同部分。可能存在一些泄漏,但是堆栈跟踪仅报告最后一个组件,当没有任何内存时,它会及时声明一些内存。

    java.lang.OutOfMemoryError: Java heap space
            at java.util.LinkedHashMap.createEntry(LinkedHashMap.java:442)
            at java.util.HashMap.addEntry(HashMap.java:888)
            at java.util.LinkedHashMap.addEntry(LinkedHashMap.java:427)
            at java.util.HashMap.put(HashMap.java:509)
            at sun.util.resources.OpenListResourceBundle.loadLookup(OpenListResourceBundle.java:134)
            at sun.util.resources.OpenListResourceBundle.loadLookupTablesIfNecessary(OpenListResourceBundle.java:113)
            at sun.util.resources.OpenListResourceBundle.handleGetObject(OpenListResourceBundle.java:74)
            at sun.util.resources.TimeZoneNamesBundle.handleGetObject(TimeZoneNamesBundle.java:75)
            at java.util.ResourceBundle.getObject(ResourceBundle.java:389)
            at java.util.ResourceBundle.getObject(ResourceBundle.java:392)
------------------
Exception in thread "Timer-22" Exception in thread "com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#2" java.lang.OutOfMemoryError: Java heap space
Exception in thread "com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#1" java.lang.OutOfMemoryError: Java heap space
------------------
Caused by: java.lang.OutOfMemoryError: Java heap space
        at java.util.Arrays.copyOf(Arrays.java:2219)
        at java.util.ArrayList.grow(ArrayList.java:242)
        at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)
        at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)
        at java.util.ArrayList.add(ArrayList.java:440)
        at org.hibernate.loader.Loader.instanceNotYetLoaded(Loader.java:1468)
        at org.hibernate.loader.Loader.getRow(Loader.java:1355)
        at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:611)
        at org.hibernate.loader.Loader.doQuery(Loader.java:829)
        at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:274)
         at org.ajax4jsf.component.AjaxActionComponent.broadcast(AjaxActionComponent.java:55)
        at org.ajax4jsf.component.AjaxViewRoot.processEvents(AjaxViewRoot.java:329)
        at org.ajax4jsf.component.AjaxViewRoot.broadcastEventsForPhase(AjaxViewRoot.java:304)
        at org.ajax4jsf.component.AjaxViewRoot.processPhase(AjaxViewRoot.java:261)
        at org.ajax4jsf.component.AjaxViewRoot.processApplication(AjaxViewRoot.java:474)
        at org.apache.myfaces.lifecycle.InvokeApplicationExecutor.execute(InvokeApplicationExecutor.java:32)
        at org.apache.myfaces.lifecycle.LifecycleImpl.executePhase(LifecycleImpl.java:103)
        at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:76)
        at javax.faces.webapp.FacesServlet.service(FacesServlet.java:183)
        ... 74 more
--------------
Caused by: java.lang.OutOfMemoryError: Java heap space
        at java.nio.ByteBuffer.wrap(ByteBuffer.java:350)
        at java.lang.StringCoding$StringDecoder.decode(StringCoding.java:137)
        at java.lang.StringCoding.decode(StringCoding.java:173)
        at java.lang.String.<init>(String.java:443)
        at com.ibm.db2.jcc.a.a.a(a.java:632)
        at com.ibm.db2.jcc.a.a.a(a.java:355)
        at com.ibm.db2.jcc.am.fc.e(fc.java:682)
        at com.ibm.db2.jcc.am.fc.k(fc.java:1481)
        at com.ibm.db2.jcc.am.ResultSet.getTimestampX(ResultSet.java:1075)
        at com.ibm.db2.jcc.am.ResultSet.getTimestamp(ResultSet.java:1034)

3 个答案:

答案 0 :(得分:5)

一种可能性是您在finalize()方法中使用病态行为重载JVM。如果你有覆盖Object.finalize()的类,那么JVM必须做大量工作才能真正清理它们(反过来,清理它们所有引用的对象)。如果您创建此类对象的速度快于垃圾收集器可以处理它们,那么您很快就会遇到麻烦。

本文将详细介绍pathological finalizer example,但总结一下:

  1. 使用finalize()方法的对象超出范围且(从概念上)符合GC的条件。
  2. 与可以简单释放的普通对象不同,JVM通过Finalizer保存对对象的特殊引用,防止这些对象被轻易收集。即使是短期对象仍然存在于初始GC中,并且如果与finalize()相关联,则会移动到堆的较长寿命部分。
  3. 现在这些对象将由专用的Finalizer线程处理,该线程将依次调用每个对象上的.finalize()。虽然它们无法访问,但尚未轮到它们的对象仍然存在。
  4. 一旦Finalizer线程处理了该对象,就会删除该最后一个引用,并且该对象最终可以实际为GC。由于该对象在一个或多个收集轮中存活,因此GC可能需要一些时间来解决它。
  5. 依次引用的可终结对象的任何对象现在才有资格进行收集。
  6. 如果您的.finalize()方法花费了很长时间,或者您创建了大量此类对象,则Finalizer线程无法跟上需求,对象将继续排队,最终填满你的整个堆。

    还有其他可能的解释,但过度使用finalize()可能是个原因。 Effective Java Item 7 strongly discourages finalizers

      

    终结器是不可预测的,通常是危险的,而且通常是不必要的。它们的使用会导致行为不稳定,性能不佳以及便携性问题....

         

    在极少数情况下,为某个类提供终结器可以任意延迟其实例的回收。

答案 1 :(得分:1)

这个问题可能与此问题有关

Java heap overwhelmed by unreachable objects

我发现它是因为我在AIX 6.1上的IBM JVM 5 runninf中面临同样的问题

在两个fullgcs之间一致增长的保留堆的唯一数量是无法访问的对象,如果我没有错,则标记为ROOT作为Eclipse MAT中的支配者。

为了获得这些转储,我将JVM配置为使用Xdump选项在fullgc之后创建一个sysdump(我认为仅适用于IBM JVM)

希望这会有所帮助,如果有人知道GC详细日志是否留下了无法清除的无法访问的对象的痕迹,请告诉我们!

卡洛斯

答案 2 :(得分:0)

您面临的行为有几个方面。首先,在任何给定时间内堆内都有无法访问的对象是完全正常的。垃圾收集将在下次运行期间清除无法访问的对象,并从中清除堆。因此,在堆转储中看到无法访问的数据结构不是恶意也不是异常。

但是当遇到java.lang.OutOfMemoryError:Java堆空间错误时,应该清除这些无法访问的引用。您可以通过将 -XX:+ HeapDumpOnOutOfMemoryError 添加到启动参数来检查这一点,在生成的下一个OutOfMemoryError上触发堆转储。当您现在对此转储进行爬网时,您不应该看到无法访问的对象,至少没有任何大小的对象。

在理解了这一点之后,潜在的问题仍然不明确,其中一个可能的原因肯定是堆泄漏。为此,您可以尝试使用OutOfMemoryError在JVM崩溃期间获得相同的内存转储,或者您可以使您的生活更轻松,并附加Java内存泄漏检测器以查找源代码中泄漏的确切位置。