Android:如何在运行时增加堆大小?

16
我在我的应用程序中有一个图像缓存,它使用SoftReferences实现。Dalvik从相对较小的堆开始启动应用程序,然后根据需求增加堆大小。但我希望从一开始就拥有更大的堆大小。因为当我已经有一些图像在缓存中,并且活动开始(例如)或其他峰值内存需求发生时,我的缓存会被清除,以便为该峰值需求腾出内存。结果是,在峰值消失后,我仍然有2-3 MB的可用空间,但我的缓存为空!
我认为解决这个问题的方法是预先分配一个更大的堆,即使在消耗2-3 MB的峰值时,它仍然有一些空间,这样我的SoftReferences就不会被清除。
我发现VMRuntime.getRuntime().setMinimumHeapSize(BIGGER_SIZE)将是有帮助的。特别是,Google在其应用程序中使用它,如here所提到的。然而,VMRuntime类已标记为过时,并表示将在未来的版本中从公共API中删除。因此,setMinimumHeapSize不是永久性的解决方案。
那么我如何使Dalvik在启动时增加我的堆呢?
目前,我使用一个非常直接和简单的技术,只需分配一个大数组并释放它。这使得Dalvik按照我想要的方式增加堆。但是,我相信一定有更优雅的方法来做到这一点。你能告诉我吗?

1
我怀疑你会找到一种使用公共API完成这个任务的方法。 - Falmarri
谷歌地图使用反射和此API,我在搜索调用信息时发现了这篇文章 :) - sherpya
5个回答

6

与其增加堆大小,你可以做一些更好的事情。就像你所说,你正在维护应用程序中使用SoftReferences实现的缓存。最好的方法是使用LruCache,你可以这样做:

private LruCache<String, Bitmap> bitmapCache;
final int memClass;
int cacheSize;

memClass = ((ActivityManager) context.getSystemService(
    Context.ACTIVITY_SERVICE)).getMemoryClass();

返回当前设备大致的每个应用程序内存类别。这可以让您了解您应该对应用程序施加多大的内存限制,以便让整个系统运行最佳。返回的值以兆字节为单位;基线Android内存类别为16(这恰好是这些设备的Java堆限制);一些具有更多内存的设备可能会返回24或更高的数字。

cacheSize = 1024 * 1024 * memClass / 10;
bitmapCache = new LruCache<String, Bitmap>(cacheSize) {
  @Override
  protected int sizeOf(String key, Bitmap value) {
    return value.getHeight() * value.getRowBytes();
 }
};

如果内存超过了LruCache的限定值,它会从LruCache中删除位图图像,并将新图像加载到其中。


4
如果缓存通常很小,你可以自己决定应用的有效占用空间,并且不使用SoftReferences来维护自己的缓存。
例如,通过一个简单的总字节计数器:只需将任何使用的元素添加或移动到列表顶部,如果它是新的,则将其大小加入计数器。如果总字节数超过你的经验法则限制,则从底部删除,从而减少计数器。也许LinkedHashMap类对此很有用:它可以像HashMap一样用作缓存,但也像列表一样有序。

2
问题在于SoftReference对于Java堆空间中的分配很有用,但是图像是在本地分配的,因此这种缓存类型在Android上并不真正起作用。

实际上,我将图像作为字节数组存储在我的缓存中,因为它们是从服务器接收到的(缓存位于连接级别)。但无论如何,这是一个有用的提示 - 谢谢! - JBM
@JBM 如果你把它们存储在那里,实际上你会使用双倍的内存,因为位图对象使用自己的字节。 - android developer
这是一个老话题,但我还是要回复一下。在我的情况下,从服务器获取的所有图像中只有少量图像在任何给定时刻显示。因此,没有双重RAM使用。事实上,我们使用两级缓存,第一级在磁盘上,第二级更小,在内存中。我的问题是关于优化内存的问题。我看到了我的错误:我应该用更抽象的方式来提问;我不应该提及服务器和图像,因为这会让人们陷入思维定势。 - JBM

1

你无法动态增加堆大小。

可以在清单文件中使用android:largeHeap="true"请求更多的堆大小,但是可能无法获得比正常情况下更多的堆大小,因为这只是一个请求。

此外,您可以使用本机内存,从而实际上绕过了堆大小限制。

以下是我写的一些帖子:

这里还有一个我做的库:


0

1
我从未说过我的缓存很大。事实上,它不取决于缓存大小 - 就像我所说的那样,即使是小缓存也无法工作,因为存在内存波动的原因。我保存了约100个项目的缓存,每个项目大小为2-3KB - 这不是一个巨大的缓存,但它仍然会被清除。我发现一些Android版本中的垃圾收集器存在SoftReferences的bug:https://dev59.com/questions/EG865IYBdhLWcg3wCqLI 这使得基于SoftReference的缓存几乎没用。 - JBM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接