JavaME - LWUIT 图像占用了所有内存
Posted
技术标签:
【中文标题】JavaME - LWUIT 图像占用了所有内存【英文标题】:JavaME - LWUIT images eat up all the memory 【发布时间】:2009-09-07 16:26:16 【问题描述】:我正在使用 LWUIT 编写一个 MIDlet,而图像似乎占用了难以置信的大量内存。我使用的所有图像都是 PNG,并被打包在 JAR 文件中。我使用标准的 Image.createImage(URL) 方法加载它们。该应用程序有许多表单,每个表单都有几个标签和按钮,但是我相当确定只有活动表单保存在内存中(我知道它不是很值得信赖,但 Runtime.freeMemory() 似乎可以确认这个)。
该应用程序在 240x320 分辨率下运行良好,但将其移至 480x640 并为 UI 使用适当更大的图像开始导致出现内存不足错误。除其他外,该应用程序的作用是下载远程图像。该应用程序似乎可以正常工作,直到达到这一点。下载几个PNG并返回主菜单后,遇到内存不足错误。当然,我查看了主菜单使用的内存量,这非常令人震惊。它只是两个带有图像的标签和四个按钮。每个按钮都有用于 style.setIcon、setPressedIcon 和 setRolloverIcon 的三个图像。图片大小从 15 到 25KB 不等,但删除了每个按钮使用的三个图片中的两个(总共 8 个图片),Runtime.freeMemory() 显示内存使用量减少了惊人的 1MB。
在我看来,我要么有很多内存泄漏(我认为我没有,但内存泄漏并不完全知道很容易追踪),我做错了什么图像处理或真的没有问题,我只需要按比例缩小。
如果有人能提供任何见解,我将不胜感激。
【问题讨论】:
不是一个解决方案,而是一个提示:使用 optipng (optipng.sourceforge.net) 优化您的 png - 将具有少量颜色的图像通常剪切到其大小的 50%,而不会降低质量。 【参考方案1】:移动设备的内存通常非常低。所以你必须使用一些技巧来节省和使用内存。
我们在一个项目中遇到了同样的问题,我们是这样解决的。
对于下载的图像: 在放置图像的位置创建缓存。如果您需要图像,请检查它是否在缓存映射中,如果没有,请下载并将其放在那里,如果是,请使用它。如果内存已满,请删除缓存映射中最旧的图像并重试。
对于其他资源图像: 只要您能看到它们,就将它们保留在内存中,如果您看不到它们,请中断引用,gc 将为您进行清理。
希望这会有所帮助。
【讨论】:
【参考方案2】:这里可能会发生一些事情:
您可能已经看到垃圾回收之前使用的内存,它与您的应用实际使用的内存不对应。 您正在运行的某些第三方代码可能正在汇集一些内部数据结构以最小化分配。虽然池化是一种可行的策略,但有时它看起来确实像泄漏。在这种情况下,请查看是否有 API 可以“关闭”或“处置”您不需要的对象。 最后,您可能真的有泄漏。在这种情况下,您需要了解有关模拟器 VM 中发生的情况的更多详细信息(但请记住,它不一定与手机 VM 相同)。确保您的模拟器使用 JRE 1.6 作为支持 JVM。如果您需要它来使用来自 erlyer JDK 的运行时库,请使用 -Xbootclasspath:<path-to-rt.jar>
。
然后,在你的应用程序进入你想看到的状态之后,做%JAVA_HOME%\bin\jmap -dump:format=b,file=heap.bin <pid>
(如果你不知道你的进程的id,使用jps
)
现在您已经获得了 JVM 堆的转储。您可以使用jhat
(JDK 自带,有点难用)或一些第三方分析器(我的偏好是YourKit
- 它是商业的,但他们有时间限制的评估许可证)来分析它
【讨论】:
分析堆转储绝对是发现泄漏和低效内存使用的方法。 Eclipse 有一个内置的。窗口 > 打开透视图 > 内存分析。然后打开转储文件。【参考方案3】:我在 Java DTV 上遇到了与 LWUIT 类似的问题。您是否在不再需要图像时尝试刷新它们(getAWTImage().flush())?
【讨论】:
【参考方案4】:尽可能使用EncodedImage
和资源文件(资源文件默认使用EncodedImage
。请阅读javadoc。其他cmets也是正确的,您需要实际观察内存量,即使是高RAM android / ios设备在使用多个图像时会很快耗尽内存。
避免缩放会有效消除EncodedImage
。
【讨论】:
【参考方案5】:您是否想过这样一个事实,即从 JAR 中多次加载相同的图像会导致创建许多单独的图像对象(具有相同的内容),而不是为每个图像重用一个实例?这是我的第一个猜测。
【讨论】:
我多次使用的每个图像(其中有很多)仅作为 Image 对象加载一次。整个应用程序中的多个 Button 和 Label 对象是使用相同的 Image 对象创建的,但这是我无法解决的问题。自从发布问题以来,我确实设法找到了一种优化方法 - 显然 HttpConnection 也占用了相当多的内存。将所有 HTTP 通信转移到一个单独的线程并整理代码以确保它终止并在完成后立即被垃圾收集。以上是关于JavaME - LWUIT 图像占用了所有内存的主要内容,如果未能解决你的问题,请参考以下文章