我在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
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)
答案 0 :(得分:5)
一种可能性是您在finalize()
方法中使用病态行为重载JVM。如果你有覆盖Object.finalize()
的类,那么JVM必须做大量工作才能真正清理它们(反过来,清理它们所有引用的对象)。如果您创建此类对象的速度快于垃圾收集器可以处理它们,那么您很快就会遇到麻烦。
本文将详细介绍pathological finalizer example,但总结一下:
finalize()
方法的对象超出范围且(从概念上)符合GC的条件。Finalizer
保存对对象的特殊引用,防止这些对象被轻易收集。即使是短期对象仍然存在于初始GC中,并且如果与finalize()
相关联,则会移动到堆的较长寿命部分。Finalizer
线程处理,该线程将依次调用每个对象上的.finalize()
。虽然它们无法访问,但尚未轮到它们的对象仍然存在。Finalizer
线程处理了该对象,就会删除该最后一个引用,并且该对象最终可以实际为GC。由于该对象在一个或多个收集轮中存活,因此GC可能需要一些时间来解决它。如果您的.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内存泄漏检测器以查找源代码中泄漏的确切位置。