我正在使用java odftoolkit库(simple-odf-0.6.6)进行odf文档操作。我们迭代循环中的所有文档:
TextDocument textdoc = TextDocument.loadDocument(odtFileName);
.
changing content of document
.
textdoc.save(anotherOdtFileName);
textdoc.close();
//then all resources/streams are correctly closed, checked that many times by my colleagues :)
当我们迭代数千个文档时,java app慢慢占用所有内存,然后一切都变慢,因为GC试图释放一些内存。我们没有得到OutOfMemoryException。
我尝试调整JVM内存大小和GC选项(http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html) - 应用程序支持几分钟但后来又耗尽了所有内存。
当应用程序到达所有可用内存时,这是从转储中获取的示例:
652.147: [Full GC 652.147: [Tenured: 454655K->454655K(454656K), 2.2387530 secs] 659263K->659216K(659264K), [Perm : 41836K->41836K(42112K)], 2.2388570 secs] [Times: user=2.25 sys=0.00, real=2.23 secs]
654.387: [Full GC 654.387: [Tenured: 454656K->454656K(454656K), 2.2661510 secs] 659263K->659223K(659264K), [Perm : 41836K->41836K(42112K)], 2.2663190 secs] [Times: user=2.26 sys=0.00, real=2.26 secs]
656.654: [Full GC 656.654: [Tenured: 454656K->454656K(454656K), 2.4117680 secs] 659263K->659229K(659264K), [Perm : 41836K->41836K(42112K)], 2.4118970 secs] [Times: user=2.41 sys=0.00, real=2.41 secs]
正如您所看到的,只有少数几个已经发布且GC非常慢(超过2秒)。
jmap histogram 显示了最大的消费者:
num #instances #bytes class name
----------------------------------------------
1: 2535190 99077856 [C
2: 2529791 60714984 java.lang.String
3: 21085 27956544 [B
4: 389820 16181680 [Ljava.lang.Object;
5: 147111 13373896 [Ljava.util.HashMap$Entry;
6: 108426 13180496 <constMethodKlass>
7: 518834 12452016 java.util.HashMap$Entry
8: 108426 9547136 <methodKlass>
9: 321713 7721112 java.util.Vector
10: 306308 7351392 org.apache.xerces.dom.AttributeMap
11: 144353 6928944 java.util.HashMap
12: 10230 5879960 <constantPoolKlass>
13: 10230 5089344 <instanceKlassKlass>
14: 114065 4562600 org.odftoolkit.odfdom.dom.attribute.text.TextStyleNameAttribute
15: 58248 4193856 org.odftoolkit.odfdom.incubator.doc.text.OdfTextParagraph
16: 90041 3601640 org.odftoolkit.odfdom.pkg.OdfAlienAttribute
17: 36437 3459000 [I
18: 48609 3110976 java.util.zip.ZipEntry
19: 7454 2939616 <constantPoolCacheKlass>
20: 36491 2627352 org.odftoolkit.odfdom.incubator.doc.style.OdfStyle
21: 36399 2620728 org.odftoolkit.odfdom.incubator.doc.text.OdfTextSpan
22: 58397 2335880 org.odftoolkit.odfdom.dom.attribute.style.StyleNameAttribute
23: 65517 2096544 org.apache.xerces.dom.TextImpl
24: 24270 1747440 org.odftoolkit.odfdom.incubator.doc.text.OdfTextListLevelStyleBullet
25: 36511 1460440 org.odftoolkit.odfdom.dom.attribute.style.StyleFamilyAttribute
26: 24335 1362760 org.odftoolkit.odfdom.dom.element.style.StyleParagraphPropertiesElement
27: 24320 1361920 org.odftoolkit.odfdom.dom.element.style.StyleListLevelPropertiesElement
28: 24320 1361920 org.odftoolkit.odfdom.dom.element.style.StyleListLevelLabelAlignmentElement
29: 10933 1316952 java.lang.Class
30: 29175 1167000 org.odftoolkit.odfdom.dom.attribute.style.StyleParentStyleNameAttribute
31: 19464 1089984 org.odftoolkit.odfdom.dom.element.style.StyleFontFaceElement
32: 68003 1088048 java.lang.Integer
33: 3531 1082640 <methodDataKlass>
34: 26757 1070280 org.odftoolkit.odfdom.dom.attribute.fo.FoMarginLeftAttribute
35: 26752 1070080 org.odftoolkit.odfdom.dom.attribute.fo.FoTextIndentAttribute
36: 24330 973200 org.odftoolkit.odfdom.dom.attribute.style.StyleWritingModeAttribute
32: 68003 1088048 java.lang.Integer
33: 3531 1082640 <methodDataKlass>
34: 26757 1070280 org.odftoolkit.odfdom.dom.attribute.fo.FoMarginLeftAttribute
35: 26752 1070080 org.odftoolkit.odfdom.dom.attribute.fo.FoTextIndentAttribute
36: 24330 973200 org.odftoolkit.odfdom.dom.attribute.style.StyleWritingModeAttribute
37: 24320 972800 org.odftoolkit.odfdom.dom.attribute.text.TextListLevelPositionAndSpaceModeAttribute
38: 24320 972800 org.odftoolkit.odfdom.dom.attribute.text.TextLevelAttribute
39: 24320 972800 org.odftoolkit.odfdom.dom.attribute.text.TextListTabStopPositionAttribute
40: 24320 972800 org.odftoolkit.odfdom.dom.attribute.text.TextLabelFollowedByAttribute
41: 24315 972600 org.odftoolkit.odfdom.dom.attribute.fo.FoLineHeightAttribute
42: 24270 970800 org.odftoolkit.odfdom.dom.attribute.text.TextBulletCharAttribute
43: 17064 955584 org.odftoolkit.odfdom.dom.element.style.StyleTextPropertiesElement
44: 38372 920928 java.util.ArrayList
45: 12135 873720 org.odftoolkit.odfdom.dom.element.text.TextAElement
你可以看到内存中有很多与odftoolkit相关的类。
有没有有效的方法来解决这个问题?很高兴有可能在运行时从我们的应用程序卸载odftoolkit并再次加载它以摆脱内存中的所有对象(显然它链接在一起,GC无法做任何有用的事情)。
我们还考虑将关键代码作为较小的文档组的单独进程运行,但这并不能解决问题的原因。
答案 0 :(得分:4)
您很可能在库中存在泄漏,或者因为您没有正确使用它。
您所看到的糟糕表现是经典的“GC死亡螺旋”行为,其中应用程序花费越来越多的时间运行GC并回收越来越少的内存。在GC发生数分钟或数小时之后,你很可能最终获得OOME。
处理死亡螺旋的方法是限制使用UseGCOverheadLimit
JVM切换器在垃圾收集中花费的时间。如果GC占用指定时间的更多时间,则JVM会主动向OOME发送消息“超出GC开销限制”。这是一件好事...一般来说。
然后您尝试追踪存储泄漏。
许多资源都在追踪Java存储泄漏。对于初学者,这里有关于主题的StackOverflow Q&amp; A:
基本思想是使用一个工具(并且有许多工具)来识别泄漏的对象,并通过对象引用链返回来找出为什么可以访问它们什么时候不应该。
答案 1 :(得分:2)
您可以编写一些测试来验证一些事项:
lib本身是否在最简单的情况下泄漏?使用例如代码如:
public void loadSaveDocument(String fileInName, String fileOutName) throws Exception {
OutputStream fileOutStream = new FileOutputStream(fileOutName);
TextDocument textdoc = TextDocument.loadDocument(fileInName);
textdoc.addParagraph("added text");
textdoc.save(fileOutStream);
textdoc.close();
}
如果是的话,一个务实的解决方案是找到使用前面建议的单独流程的解决方案。
我还建议使用像JProfiler这样的分析器并拍摄一些快照。有关如何使用它的答案,请参阅How to find memory leak in java using JProfiler?。
答案 2 :(得分:0)
您可以查看odf库,找出您正在使用的版本的任何预先存在的问题。
另外,一种解决方法是通过System.exec调用将所有文档处理分成另一个vm