【问题标题】:LRU Cache: initialize only one for the entire app?LRU Cache:整个应用程序只初始化一个?
【发布时间】:2017-01-25 03:46:46
【问题描述】:

在我的应用程序中,我有不同的列表视图,其中包含一些缩略图。 今天我开始重构,我想实现 LRU 缓存。我正在遵循 Android 指南,但我想知道是否更好地为整个应用程序只初始化一个 LRU 缓存,或者更好地为每个列表视图初始化 LRU 缓存。 我害怕内存不足。 因此,我有以下无法自己回答的问题: - 一个用单例模式初始化的 LRU 缓存是个好主意吗? - 如果内存不足,是否会导致后续LRU Cache初始化出现outOfMemory情况?

 @Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // Get max available VM memory, exceeding this amount will throw an
    // OutOfMemory exception. Stored in kilobytes as LruCache takes an
    // int in its constructor.
    final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

    // Use 1/8th of the available memory for this memory cache.
    final int cacheSize = maxMemory / 8;

    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            // The cache size will be measured in kilobytes rather than
            // number of items.
            return bitmap.getByteCount() / 1024;
        }
    };
    ...
}
  • 如果内存不足,LRU缓存会自动释放吗?我想知道当我使用 LRU Cache 时应用程序是否会出现释放内存的问题(应用程序因内存不足而崩溃?)

  • 整个应用只有一个 LRU Cache,会不会有问题?

  • 整个应用程序有多个 LRU 缓存,它们会不会有问题?

【问题讨论】:

    标签: android performance listview caching android-lru-cache


    【解决方案1】:

    我有很多问题,所以我可以在这里给你一些有用的答案。

    如果内存不足,LRU缓存会自动释放吗?我想知道如果我使用 LRU 缓存,应用程序释放内存是否会出现问题

    LRU 缓存不会自动释放内存。您将需要以编程方式逐出条目。

    整个应用只有一个 LRU Cache,会不会有问题?

    整个应用程序不止一个 LRU 缓存,它们会不会是个问题?

    LruCache 类是一个泛型类,其类型为键,类型为值。我会说您希望为要缓存的每种对象类型拥有一个 LRU 缓存。你正在做我所做的:缓存Bitmaps 并使用Strings 键入。

    附带说明:使用bitmap.getByteCount() 时要小心。 JavaDocs 是这样说的:

    从 KITKAT 开始,此方法的结果不能再用于确定位图的内存使用情况。见getAllocationByteCount()

    我正在为Bitmaps 使用 LRU 缓存。我认为只要调用此方法,覆盖 Application.onTrimMemory() 并清除缓存就足够了。

    另外,我做了和你一样的事情,并根据应用程序可用的堆内存百分比设置缓存大小。

    但是会发生以下情况:当我的应用程序处于内存不足状态并且我的应用程序会尝试下载内存不足的图像时,GC 会运行,但 BitmapFactory 仍然无法为 @ 分配内存987654331@。日志显示onTrimMemory() 方法被异步调用,有时在OutOfMemoryError 被抛出后几乎整整一秒!

    嘿 GOOGLE:如果系统无法告诉我我内存不足,直到 之后 OutOfMemoryError 被抛出,如何我到底应该管理我的记忆吗?

    精神错乱。彻头彻尾的疯狂。

    这就是我最终做的事情:我会在 try 块中捕获 OutOfMemoryError,清除那里的缓存,然后重试对服务器的图像请求。他们告诉你不要做的确切的事情。但它最终解决了我的问题。该应用现在稳定了很多。

    因此,在实现 LRU 缓存后,请确保对应用进行压力测试;尝试让它进入低内存情况并查看它的行为。对我来说,使用模拟器时效果最好。模拟器会有 96M 的小堆限制,但如果它有高屏幕分辨率,图像资源会缩放到相当大,这使得将内存推到最大值相当容易。

    如果您正在显示缩略图,但您从服务器获取图像并且它们可能比您的 ImageView 大,请务必阅读这篇文章:Loading Large Bitmaps Efficiently | Android Developers 以了解如何下载适当大小的位图而不浪费记忆。

    您也可以尝试让系统进行缓存并设置HttpResponseCache

    无论你最终做什么,一定要给你的应用施加压力,看看它在没有多少堆剩余的情况下表现如何。

    并准备好应对一点挫折。

    【讨论】:

    • 我刚刚看了WeakHashMap。我没有意识到在该类中具有弱引用的是 keys,而不是值。出于这个原因,我认为您可能想要制作像Map&lt;String, WeakReference&lt;Bitmap&gt;&gt; 这样的地图。我现在有点希望我一开始就这样做了。由于 LruCache 类与 GC 没有任何隐式联系,因此它几乎没有用处。所以你可能走在正确的轨道上。只是测试一下它的废话,如果它的行为可以接受,就宣布胜利并收工。
    • 这就是我查找它的原因。我不记得以前使用过它,所以我去 JavaDocs 检查我的假设。这种事情发生在我身上很多次,检查文档是我本能的第一反应。
    • WeakHashMap 在值的生命周期取决于键的生命周期的情况下会很好用。但是,在这种情况下,情况并非如此。我们不想保留对键的外部引用,因此映射需要对它们的强引用。我认为Map&lt;String, &lt;WeakReference&lt;Bitmap&gt;&gt;&gt; 是要走的路。我现在正在认真考虑重写我的应用程序的这一部分并放弃LruCache
    • 我想我可以解决这个问题。我将从 LinkedHashMap 开始,它已经具有一些 LRU 功能 chriswu.me/blog/a-lru-cache-in-10-lines-of-java 现在我只需要弄清楚如何准确地知道 GC 何时清除弱引用,以便我可以更新当前缓存大小。我将在我的应用程序中尝试这个。如果我得到一些好的工作代码,我会更新答案,这样你就可以看到我想出了什么。
    • 因为它没有与 GC 集成。我的问题是我使用Application.onTrimMemory() 告诉我何时清除缓存,但是当您甚至没有足够的堆来分配位图时,onTrimMemory 直到 after 才会被调用OutOfMemoryError 被抛出(并被捕获)。在日志中,我可以看到 GC 正在运行,试图在 OOM 之前为位图清除一些内存,所以我知道我的缓存是否与 GC 一起工作并且不等待Application.onTrimMemory(),它会做正确的事情。比捕捉OutOfMemoryError 干净得多。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-17
    • 2021-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-15
    • 2012-11-08
    相关资源
    最近更新 更多