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

Zde*_*end 10 java heap tomcat garbage-collection jvm

我在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
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

dim*_*414 7

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

本文详细介绍了病理终结器示例,但总结如下:

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

如果您的.finalize()方法需要很长时间,或者您创建了大量此类对象,则Finalizer线程无法满足需求,对象将继续排队,最终填满整个堆.

还有其他可能的解释,但过度使用finalize()是一个可能的原因.有效的Java Item 7强烈反对终结者:

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

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


小智 1

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

但是当遇到java.lang.OutOfMemoryError: Java堆空间错误时,这些不可达的引用应该已经被清理掉了。您可以通过在启动参数中添加-XX:+HeapDumpOnOutOfMemoryError来检查这一点,并在生成下一个 OutOfMemoryError 时触发堆转储。当您现在爬行此转储时,您不应该看到无法访问的对象,至少不会看到任何显着大小的对象。

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