java - 在 Java 中分配大量数组时避免内存碎片

标签 java memory windows-mobile memory-management jvm

我正在开发一个在 Windows Mobile 设备上运行的 Java 应用程序。为了实现这一点,我们一直在使用 Esmertec JBed JVM,它并不完美,但我们现在坚持使用它。最近我们收到了客户关于 OutOfMemoryErrors 的投诉。在玩了很多东西后,我发现该设备有足够的可用内存(大约 4MB)。

OutOfMemoryErrors 总是发生在代码中的同一点,即在扩展 StringBuffer 以向其附加一些字符时。在这个区域周围添加了一些日志之后,我发现我的 StringBuffer 中有大约 290000 个字符,容量大约为 290500。内部字符数组的扩展策略只是将大小加倍,所以它会尝试分配一个数组大约 580000 个字符。我也打印了这段时间的内存使用情况,发现它使用了大约 3.8MB,总共大约 6.8MB(尽管我看到总可用内存有时会上升到大约 12MB,所以有很大的扩展空间)。因此,此时应用程序报告了 OutOfMemoryError,考虑到还有多少可用空间,这没有多大意义。

到目前为止,我开始考虑应用程序的操作。基本上正在发生的事情是我正在使用 MinML(一个小型​​ XML Sax 解析器)解析一个 XML 文件。 XML 中的一个字段包含大约 300k 个字符。解析器从磁盘流式传输数据,默认情况下一次只加载 256 个字符。因此,当它到达有问题的字段时,解析器将调用处理程序的“characters()”方法超过 1000 次。每次它将创建一个包含 256 个字符的新 char[]。处理程序只是将这些字符附加到 StringBuffer。 StringBuffer 的默认初始大小仅为 12,因此当字符被附加到缓冲区时,它将不得不增长多次(每次创建一个新的 char[])。

我的假设是,虽然有足够的空闲内存,因为以前的 char[] 可以被垃圾收集,但可能没有足够大的连续内存块来容纳我试图分配的新数组.并且可能 JVM 不够聪明,无法扩展堆大小,因为它很愚蠢,认为没有必要,因为显然有足够的空闲内存。

所以我的问题是:是否有人对此 JVM 有任何经验,并且可能能够最终证实或反驳我对内存分配的假设?另外,有没有人有任何想法(假设我的假设是正确的)关于如何改进数组的分配以使内存不会变得碎片化?

注意:我已经尝试过的事情:

  • 我增加了 StringBuffer 的初始数组大小,并增加了解析器的读取大小,因此它不需要创建这么多数组。
  • 我更改了 StringBuffer 的扩展策略,使其在达到一定大小阈值后仅扩展 25% 而不是 100%。

做这两件事有点帮助,但是当我增加输入的 xml 数据的大小时,我仍然会在相当小的大小(大约 350kb)处得到 OutOfMemoryErrors。

要补充一点:所有这些测试都是在使用相关 JVM 的设备上执行的。如果我使用 Java SE 1.2 JVM 在桌面上运行相同的代码,我没有任何问题,或者至少在我的数据大小达到大约 4MB 之前我不会遇到问题。

编辑:

我刚刚尝试过的另一件事是我将 Xms 设置为 10M。所以这解决了 JVM 没有在应该扩展堆的时候扩展堆的问题,并允许我在错误发生之前处理更多数据。

最佳答案

也许你可以试试VTD光。它似乎比 SAX 更节省内存。 (我知道这是一个巨大的变化。)

关于java - 在 Java 中分配大量数组时避免内存碎片,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2066808/

相关文章:

java - Jenkins 未通过 gradle 测试任务,找不到导入文件

c - 在 C 中释放内存需要什么?

c# - 大型多维数组(锯齿状数组)C# 的解决方法?

java - 如何将 BufferedImage 写为 PNG 且不进行压缩?

java - 如何使用不同于 super 用户(postgres)的用户访问postgresql数据库?

java - Spring Cloud Stream不使用Kafka channel 绑定(bind)器发送消息

c - 如何从不是字节对齐的源复制内存(移位)

c# - 在 .net Compact Framework 中使用 WEB API 身份验证

c# - Windows Mobile - 串行端口通信

c# - icon转byte[] C#紧凑框架