我对这个关于Android操作系统的内存管理的问题非常好奇,所以我希望对这个主题有一个非常详细的答案。
我想知道的是:
最重要的是:
到目前为止我所听到的(直到2013年):
是什么让我非常好奇:
这两个限制都非常低。
我刚刚下载了Android Task Manager来检查我的设备内存。我注意到有些应用程序使用大约40-50兆字节的RAM,这比上面提到的最大RAM使用量(比如说32 MB)要多得多。那么Android如何确定应用程序可以使用多少RAM?应用程序如何超出此限制?
此外,当我使用大约30-40兆字节时,我注意到我的一些应用程序崩溃(被系统杀死了?) OutOfMemoryException 。另一方面,我使用 100 MB以及更多在我的手机上运行应用程序一段时间后(可能是由于内存泄漏)不会崩溃或被杀死。 因此,当确定可以节省多少RAM时,显然也取决于应用程序本身。这怎么可能? (我使用带有768 MB RAM的HTC One S进行了测试)
免责声明:我不以任何方式与Android任务管理器应用相关联。
答案 0 :(得分:113)
Android应用程序(非系统应用程序)可以使用的最大内存量(以兆字节/占总RAM的百分比)是多少?
因设备而异。 getMemoryClass()
on ActivityManager
将为您运行代码的设备提供价值。
Android版本之间是否有任何差异?
是的,就多年来操作系统要求的增加而言,设备必须进行调整才能匹配。
设备制造商是否存在差异?
是的,就制造商制造设备而言,尺寸因设备而异。
在确定应用可以使用多少RAM时会考虑哪些“因素”?
我不知道“边缘因素”是什么意思。
早期设备的每个应用上限为16MB;后来的设备增加到24MB或32MB
那是对的。屏幕分辨率是一个重要的决定因素,因为较大的分辨率意味着更大的位图,因此平板电脑和高分辨率手机的价值往往更高。例如,您将看到具有48MB堆的设备,如果值高于此值,我不会感到惊讶。
应用程序如何超出此限制?
您认为该应用的作者知道他在做什么。考虑到memory usage of an app is difficult for a core Android engineer to determine,我不认为有问题的应用程序必然会提供特别准确的结果。
话虽这么说,本机代码(NDK)不受堆限制的约束。而且,自从Android 3.0以来,应用程序可以请求“大堆”,通常在数百MB范围内,但对于大多数应用程序而言,这被认为是糟糕的形式。
此外,我注意到当使用大约30-40兆字节时,我的一些应用程序因OutOfMemoryException而崩溃。
请记住,Android垃圾收集器不是压缩垃圾收集器。例外情况应该是CouldNotFindSufficientlyLargeBlockOfMemoryException
,但这可能被认为过于冗长。 OutOfMemoryException
表示您无法分配您请求的阻止,而不是您完全耗尽了您的堆。
答案 1 :(得分:12)
每个应用的内存限制取决于屏幕尺寸和Android版本:https://drive.google.com/file/d/0B7Vx1OvzrLa3Y0R0X1BZbUpicGc/view?usp=sharing
来源:Android兼容性下载http://source.android.com/compatibility/downloads.html; 兼容性定义文档(CDD),部分虚拟机兼容性或运行时兼容性
答案 2 :(得分:9)
到了2018年底,情况发生了变化。
首先:运行您的应用并在Android Studio中打开“ Android Profiler”标签。 您会看到它消耗了多少内存,您会感到惊讶,但是它可以分配很多RAM。
另外here is a great article在官方文档中也提供了有关如何使用Memory Profiler的详细说明,这可以使您对内存管理有更深入的了解。
但是在大多数情况下,常规的Android Profiler就足够了。
通常,一个应用程序开始时会分配50Mb的RAM,但是当您开始在内存中加载一些照片时,应用程序会立即跳至90Mb。当您使用带有预加载照片(每张3,5Mb)的ViewPager打开“活动”时,您可以在几秒钟内轻松获得190Mb。
但这并不意味着您在内存管理方面存在问题。
我能提供的最佳建议是遵循准则和最佳实践,使用顶级库进行图像加载(Glide,Picasso),您会没事的。
但是,如果您需要调整某些内容,并且确实需要知道可以手动分配多少内存,则可以获得总的可用内存,并从中计算出预定部分(以%为单位)。 就我而言,我需要将解密后的照片缓存在内存中,因此无需在用户每次浏览列表时都对它们进行解密。
为此,您可以使用随时可用的LruCache class。它是一个缓存类,它会自动跟踪对象分配的内存量(或实例数),并根据其使用历史记录删除最旧的以保持最新状态。 Here is很棒的使用教程。
就我而言,我创建了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或其他黑客来获取分配给您的应用程序的内存量,它是根据各种因素动态计算得出的。
P.S。我正在使用静态类字段来保存缓存,但是根据最新的Android指南,建议为此目的使用ViewModel architecture component。
答案 3 :(得分:0)
没有任何限制了。为什么要在那里?请记住,Android历史上是来自嵌入式设备世界的,每个应用程序都有明确的简单任务要做,通常根本不允许使用动态内存。
它不被认为是通用系统。我们仍然可以看到许多古代的设计工件,这些工件在“真实计算机”上从来没有用过,这是iOS具有比Android更好的应用程序的原因,它设计得更好,更容易为用户驱动的任务编程
在应用程序设计中,重要的是您的活动对“ onLowMemory”信号反应良好。