我正在使用 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 应用程序会慢慢占用所有内存,然后由于 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]
如您所见,仅释放了几 kB,GC 速度非常慢(超过 2 秒)。
这个jmap直方图显示了最大的消费者:
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 不能做任何有用的事情),那就太好了。
我们也在考虑将关键代码作为较小的文档组的单独进程运行,但这并不能解决问题的原因。
最佳答案
您很可能存在存储泄漏,要么是由于库本身存在问题,要么是因为您没有正确使用它。我们需要一个正确构造的 minimal reproducible example知道哪个。
您看到的糟糕性能是典型的“GC 死亡螺旋”行为,应用程序运行 GC 的时间越来越多,回收的内存越来越少。在 GC 反复运行几分钟或几小时后,它很可能最终导致 OOME。
处理死亡螺旋的方法是使用 UseGCOverheadLimit
JVM 开关来限制在垃圾收集上花费的时间。如果 GC 占用的时间超过指定比例,JVM 会主动抛出 OOME 消息“超出 GC 开销限制”。这是一件好事……一般来说。
然后您尝试追踪存储泄漏。
许多资源都涵盖了追踪 Java 存储泄漏。对于初学者,这里是关于该主题的 StackOverflow 问答:
基本思想是使用一种工具(有很多)来识别正在泄漏的对象,然后通过对象引用链返回以找出它们可以访问的原因什么时候不应该。
关于java - 泄漏的 Java 库慢慢占用所有内存,没有抛出 OutOfMemoryException,解决方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16807576/