一个应用程序可以使用的最大RAM量是多少?

166

我对 Android 操作系统的内存管理问题很感兴趣,因此希望能得到关于该主题的详细回答。

我想知道:

  • 一个 Android 应用程序(不是系统应用程序)可以使用的内存最大量是多少(以兆字节或总 RAM 的 百分比表示)?
  • Android 版本之间有什么区别吗?
  • 设备的制造商是否会影响内存使用情况?

更重要的是:

  • 在运行时,系统如何确定应用程序可以使用多少 RAM(假设每个应用程序的最大内存不是静态数字)?

迄今为止我听到的信息(截至2013年):

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

让我非常好奇的是:

这两个限制都很低。

我最近下载了Android 任务管理器来查看我的设备 RAM。我注意到有些应用程序使用了约 40-50 兆字节的内存,这显然超过了最大内存使用量,例如32 MB的限制。那么Android如何确定应用程序可以使用多少内存?应用程序如何超过该限制?

此外,我发现我的一些应用程序使用约30-40兆字节时会崩溃(被系统杀掉?)而出现OutOfMemoryException。另一方面,我的一些应用程序在一段时间后使用100MB及更多的内存(可能由于内存泄漏),但不会崩溃或被杀掉。

所以,当涉及到确定可以节省多少RAM时,这显然也取决于应用本身。这是如何实现的? (我使用带有768 MB RAM的HTC One S进行测试)
免责声明:我与Android任务管理器应用程序没有任何关联。
6个回答

119
以下是翻译:

Android应用程序(不是系统应用程序)可以使用的最大内存量(以兆字节/总RAM百分比表示)是多少?

这取决于设备。ActivityManager上使用getMemoryClass()将为您提供代码运行设备的值。

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

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

设备制造商是否会影响内存大小?

是的,因为制造商制造设备,设备的大小因设备而异。

确定应用程序可使用多少RAM时考虑了哪些“附加因素”?

我不知道“附加因素”是什么意思。

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

没错。屏幕分辨率是一个重要的决定因素,因为更高的分辨率意味着更大的位图,所以平板电脑和高分辨率手机通常会有更高的值。例如,你会看到一些设备具有48MB的堆,我不会感到惊讶如果有比这更高的值。

应用程序如何超出这个限制?

你假设该应用程序的作者知道他正在做什么。考虑到应用程序的内存使用对于核心Android工程师来说很难确定,我不会认为涉及的应用程序一定提供特别准确的结果。

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

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

请记住,Android垃圾回收器不是紧凑型垃圾回收器。异常应该真正是CouldNotFindSufficientlyLargeBlockOfMemoryException,但这可能被认为过于冗长。 OutOfMemoryException意味着您无法分配所请求的块,而不是完全耗尽堆。

我无法理解关于表格的内容,我有一部分辨率约为1080 x 1920的Xperia X手机,这是一个很高的分辨率,另外一台设备是三星Tab 4,分辨率为800 x 1280,但它占用的内存相同,请指导我,因为手机配备了3GB的RAM,而平板电脑则配备了1.5GB的RAM,所以平板电脑是否占用更多的内存是因为它有更大的屏幕? - Rahul Mandaliya
@RahulMandaliya:非常抱歉,我不明白您的问题或者它与这个问题有什么关系。您可以开一个单独的Stack Overflow问题,在那里详细解释您的问题。 - CommonsWare

19

现在已经是2018年末,情况有所改变。

首先:运行你的应用,并在Android Studio中打开Android Profiler选项卡。 你将会看到它消耗了多少内存,你会惊讶地发现它可以分配很多RAM。

此外,在官方文档中 这里有一篇很棒的文章 ,详细介绍了如何使用Memory Profiler来深入了解你的内存管理。

但在大多数情况下,普通的Android Profiler就足够了。

输入图片描述

通常情况下,一个应用程序开始时会分配50Mb的RAM,但在你开始加载一些照片到内存时,它会立即跳到90Mb。当你打开一个ViewPager并预加载带有相片(每个3.5Mb)的Activity时,你可以轻易地在几秒钟内得到190Mb。

但这并不意味着你存在内存管理问题。

我能给出的最好的建议就是遵循指南和最佳实践,使用顶级图像加载库(Glide、Picasso),你就没问题了。


但如果你需要调整一些东西,并且你真的需要知道你可以手动分配多少内存,你可以获取总空闲内存并计算出其中预先确定的部分(以%)。 在我的情况下,我需要将解密后的照片缓存在内存中,这样每当用户滑动列表时就不需要再解密它们了。

为此,你可以使用现成的LruCache类。这是一个缓存类,它会自动跟踪对象分配的内存量(或实例数量),并删除最老的对象以保持最近的使用历史。 这里有一个很棒的教程,介绍如何使用它。

在我的情况下,我创建了两个缓存实例:用于缩略图和附件。 用单例访问方法使它们静态化,这样它们就可以在整个应用程序中全局可用。

缓存类:

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);
}

以下是我在Adapter中使用它来获取缓存图像的方法:

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

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

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

我的应用程序针对API 19+,因此设备不算老旧,可用的RAM缓存量在我这种情况下足够(为1%和3%)。

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


P.S. 我正在使用静态类字段来保存缓存,但根据最新的Android指南,建议使用ViewModel architecture component来完成此目的。


有趣的事实:Android没有任何API或其他黑客来获取分配给您的应用程序的内存量。这是错误的。当然它有,但在应用程序的JNI / Linux层中。 Android(Java)本身只是一个过度膨胀的GUI库。 - Lothar

12

13
驱动器的URL正在请求访问权限。 - silent_grave

4

目前,我正在开发一个应用程序,我使用的三星Ultra S21手机(即使有16GB可用的内存...)即使在清单文件中设置了android:largeHeap =“true”,也无法为此应用程序分配超过500MB的内存。


1

现在没有限制了,至少如果您使用NDK的话。为什么会有限制呢?请记住,Android最初来自嵌入式设备领域,每个应用程序都有明确简单的任务要完成,通常根本不允许动态内存使用。

它并不被认为是一个通用系统。我们仍然可以看到很多这种古老时代的设计遗物,这些设计在“真正的计算机”上从未有意义,这也是为什么iOS有比Android更好的应用程序的原因,因为它的设计更好,更容易为用户驱动的任务编程。

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


0
对于我的情况来说,同一个应用在不同的设备上运行,我监控其内存使用情况,发现它们的数值是不同的,甚至相差好几倍。我的策略是在性能较低的设备上监控我的应用,并对其进行优化。

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