Early Android devices had a per-app cap of 16MB. Later this cap increased to 24MB or 32MB.
Checking How Much Memory Your App Can Use Android supports devices with as little as 512 MB of RAM. Make sure you remember low-end devices too!
Before you blame games or other heavy apps for draining battery and slowing down your phone, note that in most cases, it is Facebook or Instagram app that tends you hog the most battery and RAM on any Android phone.
What is the maximum amount of memory (in Megabytes / as percentage of the total RAM) that an Android application (that is not a system app) can use?
That varies by device. getMemoryClass()
on ActivityManager
will give you the value for the device your code is running upon.
Are there any differences between the Android versions?
Yes, insofar as OS requirements have increased over the years, and devices have to adjust to match.
Are there differences concerning the manufacturer of the device?
Yes, insofar as manufacturers manufacture devices, and the size varies by device.
Which "side factors" are taken into consideration when it comes to determining how much RAM an app can use?
I have no idea what "side factors" means.
Early devices had a per-app cap of 16MB; Later devices increased that to 24MB or 32MB
That's about right. Screen resolution is a significant determinant, as larger resolutions mean larger bitmaps, and so tablets and high-resolution phones will tend to have higher values yet. For example, you will see devices with 48MB heaps, and I wouldn't be surprised if there are values higher than that.
How is it possible that apps exceed that limit?
You assume that the author of that app knows what (s)he is doing. Considering that memory usage of an app is difficult for a core Android engineer to determine, I would not assume that the app in question is necessarily providing particularly accurate results.
That being said, native code (NDK) is not subject to the heap limit. And, since Android 3.0, apps can request a "large heap", usually in the hundreds of MB range, but that's considered poor form for most apps.
Furthermore, I noticed that some apps of mine crash with an OutOfMemoryException when using around 30-40 Megabytes.
Bear in mind that the Android garbage collector is not a compacting garbage collector. The exception really should be CouldNotFindSufficientlyLargeBlockOfMemoryException
, but that was probably deemed too wordy. OutOfMemoryException
means that you could not allocate your requested block, not that you have exhausted your heap entirely.
It's the end of 2018 so things have changed.
First of all: run your app and open Android Profiler tab in Android Studio. You will see how much memory it consumes, you will be surprised but it can allocate a lot of RAM.
Also here is a great article in official docs with detailed instructions on how to use Memory Profiler which can give you an in-depth look of your memory management.
But in most of the cases, your regular Android Profiler will be enough for you.
Usually, an app starts with 50Mb of RAM allocation but instantly jumps up to 90Mb when you start loading some photos in memory. When you open Activity with a ViewPager with preloaded photos (3,5Mb each) you can get 190Mb easily in seconds.
But this doesn't mean you have issues with memory management.
The best advice I can give is to follow the guidelines and best practices, use top libraries for image loading (Glide, Picasso) and you'll be OK.
But if you need to tailor something and you really need to know how much memory you can allocate manually you can get total free memory and calculate a pre-determined portion (in %) out of it. In my case, I needed to cache decrypted photos in memory so I don't need to decrypt them everytime user slides through the list.
For this purpose you can use ready to use LruCache class. It's a cache class which automatically tracks of how much memory your objects allocate (or the number of instances) and removes the oldest to keep the recent by their usage history. Here is a great tutorial on how to use it.
In my case, I created 2 instances of caches: for thumbs and attachments. Made them static with singleton access so they are available globally throughout the app.
cache class:
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;
}
}
This is how I calculate available free RAM and how much I can bite out of it:
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);
}
And this is how I use it in Adapters to get cached image:
byte[] decryptedThumbnail = BitmapLruCache.getDecryptedThumbCacheInstance().getBitmap(thumbUri);
and how I set it into cache in background thread (regular AsyncTask):
BitmapLruCache.getDecryptedThumbCacheInstance().addBitmap(thumbUri, thumbBytes);
My app targets API 19+ so devices are not old and these portions of available RAM are good enough for cache in my case (1% and 3%).
Fun fact: Android does not have any APIs or other hacks to get the amount of memory allocated to your app, it's calculated on the fly based on various factors.
P.S. I'm using a static class field to hold a cache but as per the latest Android guidelines its recommended to use ViewModel architecture component for that purpose.
Memory limits per app are here depending on screen size and Android version: https://drive.google.com/file/d/0B7Vx1OvzrLa3Y0R0X1BZbUpicGc/view?usp=sharing
Source: Android Compatibility Downloads http://source.android.com/compatibility/downloads.html; Compatibility Definition Document (CDD), Section Virtual Machine Compatibility or Runtime Compatibility
There isn't any limit anymore. Why should there? Remember that Android was historically coming from the the embedded devices world where every app had clear simple tasks to do, often disallowing dynamical memory usage at all.
It was not considered a general purpose system. We can still see a lot of design artifacts of this ancient times that never made sense on a "real computer" and is a reason why iOS has so many better apps then android, it was designed much better and easier to program for user driven task.
Important in app design is that your activity reacts well to "onLowMemory" signals.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With