158

我对这个关于Android操作系统的内存管理的问题很好奇,所以我希望能得到一个关于这个话题的非常详细的答案。

我想知道的:

  • Android 应用程序(非系统应用程序)可以使用的最大内存量(以MB为单位/占总 RAM 的 百分比)是多少?
  • Android版本之间有什么区别吗?
  • 设备制造商是否存在差异?

最重要的是:

  • 在系统确定应用程序在运行时可以使用多少 RAM(假设每个应用程序的最大内存不是静态数字)时,考虑了什么/取决于什么?

到目前为止我所听到的(直到 2013 年):

  • 早期 Android 设备的每个应用程序上限为 16MB
  • 后来这个上限增加到 24MB 或 32MB

是什么让我很好奇:

这两个限制都非常低。

我最近刚刚下载了Android 任务管理器来检查我的设备 RAM。我注意到有些应用程序使用大约 40-50 MB 的 RAM,这显然比提到的最大 RAM 使用量(比如 32 MB)要多。那么,Android 是如何确定应用程序可以使用多少 RAM 的呢?应用程序怎么可能超过这个限制?

此外,我注意到我的一些应用程序在使用大约 30-40 兆字节时崩溃(被系统杀死?)并出现OutOfMemoryException 。另一方面,一段时间后(可能由于内存泄漏),我的手机上运行的应用程序使用100 MB 或更多内存,这些应用程序不会崩溃或被杀死。因此,在确定可以节省多少 RAM 时,显然还取决于应用程序本身。这怎么可能?(我使用 768 MB RAM 的 HTC One S 进行了测试)

免责声明:我不以任何方式隶属于 Android 任务管理器应用程序。

4

5 回答 5

119

Android 应用程序(不是系统应用程序)可以使用的最大内存量是多少(以 MB 为单位/占总 RAM 的百分比)?

这因设备而异。getMemoryClass()onActivityManager将为您提供运行代码的设备的值。

Android版本之间有什么区别吗?

是的,只要操作系统要求多年来不断增加,设备必须进行调整以匹配。

设备制造商是否存在差异?

是的,只要制造商制造设备,并且尺寸因设备而异。

在确定应用程序可以使用多少 RAM 时,会考虑哪些“附带因素”?

我不知道“副作用”是什么意思。

早期设备的每个应用程序上限为 16MB;后来的设备将其增加到 24MB 或 32MB

这差不多。屏幕分辨率是一个重要的决定因素,因为更大的分辨率意味着更大的位图,因此平板电脑和高分辨率手机往往会有更高的值。例如,您会看到具有 48MB 堆的设备,如果有更高的值,我不会感到惊讶。

应用程序怎么可能超过这个限制?

您假设该应用程序的作者知道他在做什么。考虑到核心 Android 工程师很难确定应用程序的内存使用情况,我不认为有问题的应用程序一定会提供特别准确的结果。

话虽如此,本机代码(NDK)不受堆限制。而且,从 Android 3.0 开始,应用程序可以请求“大堆”,通常在数百 MB 范围内,但对于大多数应用程序来说,这被认为是糟糕的形式。

此外,我注意到我的一些应用程序在使用大约 30-40 兆字节时会出现 OutOfMemoryException 崩溃。

请记住,Android 垃圾收集器不是压缩垃圾收集器。例外确实应该是CouldNotFindSufficientlyLargeBlockOfMemoryException,但这可能被认为太罗嗦了。OutOfMemoryException意味着您无法分配您请求的块,而不是您已经完全耗尽了堆。

于 2013-09-07T16:55:47.620 回答
18

现在是 2018 年底,所以情况发生了变化。

首先:运行您的应用并在 Android Studio 中打开 Android Profiler 选项卡。您会看到它消耗了多少内存,您会感到惊讶,但它可以分配大量 RAM。

此外,官方文档中还有一篇很棒的文章,其中包含有关如何使用 Memory Profiler 的详细说明,它可以让您深入了解内存管理。

但在大多数情况下,您的常规 Android Profiler 对您来说就足够了。

在此处输入图像描述

通常,一个应用程序从 50Mb 的 RAM 分配开始,但当您开始在内存中加载一些照片时,它会立即跃升至 90Mb。当您使用带有预加载照片(每张 3.5Mb)的 ViewPager 打开 Activity 时,您可以在几秒钟内轻松获得 190Mb。

但这并不意味着您在内存管理方面存在问题。

我能给出的最佳建议是遵循指南和最佳实践,使用顶级库进行图像加载(Glide、Picasso),你会没事的。


但是,如果您需要定制某些东西并且您确实需要知道可以手动分配多少内存,您可以获得总可用内存并从中计算出预定的部分(以 % 为单位)。就我而言,我需要将解密的照片缓存在内存中,这样我就不需要在每次用户滑过列表时都解密它们。

为此,您可以使用现成的LruCache 类。它是一个缓存类,它自动跟踪您的对象分配了多少内存(或实例数),并根据它们的使用历史删除最旧的以保留最新的。 这是一个关于如何使用它的很棒的教程。

就我而言,我创建了 2 个缓存实例:用于拇指和附件。通过单例访问使它们成为静态的,以便它们在整个应用程序中全局可用。

缓存类:

public class BitmapLruCache extends LruCache<Uri, byte[]> {

    private static final float CACHE_PART_FOR_THUMBS_PRC = 0.01f; // 1% (Nexus 5X - 5Mb)
    private static final float CACHE_PART_FOR_ATTACHMENTS_PRC = 0.03f;// 3% (Nexus 5X - 16Mb)
    private static BitmapLruCache thumbCacheInstance;
    private static BitmapLruCache attachmentCacheInstance;

public static synchronized BitmapLruCache getDecryptedThumbCacheInstance() {
    if (thumbCacheInstance == null) {

        int cacheSize = getCacheSize(CACHE_PART_FOR_THUMBS_PRC);
    //L.log("creating BitmapLruCache for Thumb with size: " + cacheSize + " bytes");
        thumbCacheInstance = new BitmapLruCache(cacheSize);
        return thumbCacheInstance;
    } else {
        return thumbCacheInstance;
    }
}

public static synchronized BitmapLruCache getDecryptedAttachmentCacheInstance() {
    if (attachmentCacheInstance == null) {

        int cacheSize = getCacheSize(CACHE_PART_FOR_ATTACHMENTS_PRC);
    //            L.log("creating BitmapLruCache for Attachment with size: " + cacheSize + " bytes");
        attachmentCacheInstance = new BitmapLruCache(cacheSize);
        return attachmentCacheInstance;
    } else {
        return attachmentCacheInstance;
    }
}

private BitmapLruCache(int maxSize) {
    super(maxSize);
}

public void addBitmap(Uri uri, byte[] bitmapBytes) {
    if (get(uri) == null && bitmapBytes != null)
        put(uri, bitmapBytes);
}

public byte[] getBitmap(Uri uri) {
    return get(uri);
}


@Override
protected int sizeOf(Uri uri, byte[] bitmapBytes) {
    // The cache size will be measured in bytes rather than number of items.
    return bitmapBytes.length;
}
}

这就是我计算可用空闲 RAM 以及我可以从中咬出多少的方法:

private static int getCacheSize(float partOfTotalFreeMemoryToUseAsCache){
    final long maxMemory = Runtime.getRuntime().maxMemory();
    //Use ... of available memory for List Notes thumb cache
    return (int) (maxMemory * partOfTotalFreeMemoryToUseAsCache);
}

这就是我在适配器中使用它来获取缓存图像的方式:

byte[] decryptedThumbnail = BitmapLruCache.getDecryptedThumbCacheInstance().getBitmap(thumbUri);

以及我如何将其设置到后台线程中的缓存中(常规 AsyncTask):

BitmapLruCache.getDecryptedThumbCacheInstance().addBitmap(thumbUri, thumbBytes); 

我的应用程序以 API 19+ 为目标,因此设备并不旧,并且这些可用 RAM 部分在我的情况下足以用于缓存(1% 和 3%)。

有趣的事实: Android 没有任何 API 或其他 hack 来获取分配给您的应用程序的内存量,它是根据各种因素动态计算的。


PS 我正在使用静态类字段来保存缓存,但根据最新的 Android 指南,它建议为此目的使用ViewModel 架构组件

于 2018-11-20T14:39:42.820 回答
11

每个应用程序的内存限制取决于屏幕大小和 Android 版本:https ://drive.google.com/file/d/0B7Vx1OvzrLa3Y0R0X1BZbUpicGc/view?usp=sharing

来源:Android 兼容性下载http://source.android.com/compatibility/downloads.html;兼容性定义文档 (CDD),虚拟机兼容性或运行时兼容性部分

于 2015-12-29T08:10:10.150 回答
1

没有任何限制了,至少如果你去 NDK。为什么要在那里?请记住,Android 在历史上来自嵌入式设备世界,每个应用程序都有明确的简单任务要做,通常根本不允许动态内存使用。

它不被视为通用系统。我们仍然可以看到很多古代的设计作品,这些作品在“真正的计算机”上从来没有意义,这也是为什么 iOS 拥有比 android 更好的应用程序的原因,它被设计得更好,更容易为用户驱动的任务编程.

应用程序设计中的重要一点是您的活动对“onLowMemory”信号反应良好。

于 2020-11-17T02:53:29.203 回答
1

现在,我正在开发一个应用程序,而我正在使用的三星 Ultra S21(即使有 16GB 的可用内存..)即使在 android:largeHeap="true" 中也不允许我为这个应用程序使用超过 500MB 的空间清单。

于 2021-12-07T19:02:12.767 回答