I'm running into out of memory exceptions in device log generated by GPU when trying to load heavy graphics scene using VBO on Android.
20:53:48.640 app W/Adreno-GSL: <sharedmem_gpumem_alloc_id:2255>: sharedmem_gpumem_alloc: mmap failed errno 12 Out of memory
20:53:48.642 app E/Adreno-GSL: <gsl_memory_alloc_pure:1971>: GSL MEM ERROR: kgsl_sharedmem_alloc ioctl failed.
Raw binary data size i'm trying to supply is less than half of available amount of RAM. After doing some research, i found that after each glBufferData(..) call the amount of free memory decreases 2 times the size of data supplied (tried on different devices, same result). Here is my setup:
logMem("before buff creation");
ByteBuffer dummyBuff = ByteBuffer.allocateDirect(1024 * 1024 * 16).order(ByteOrder.nativeOrder());
byte[] some = new byte[1024];
for (int i = 0; i < dummyBuff.capacity(); i+= some.length) {
dummyBuff.put(some);
}
dummyBuff.rewind();
logMem("buff data created");
int[] bufferHandles = new int[1];
GLES20.glGenBuffers(1, bufferHandles, 0);
int bufferHandle = bufferHandles[0];
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferHandle);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, dummyBuff.capacity(), dummyBuff, GLES20.GL_STATIC_DRAW);
logMem("buff data supplied");
Amount of memory left is logged with
manager = (ActivityManager) getSystemService( Activity.ACTIVITY_SERVICE );
info = new ActivityManager.MemoryInfo();
...
manager.getMemoryInfo(info);
Log.v("mem", tag + ", mem " + info.availMem/1024/1024);
Here is what i am getting in the log
20:13:34.243 V/mem: before buff creation, mem 1381
20:13:34.466 V/mem: buff data created, mem 1366
20:13:34.500 V/mem: buff data supplied, mem 1334
I also tried a combination of
GLES20.glBufferData(.., dummyBuf.capacity(), null, ..);
GLES20.glBufferSubData(.., 0, dummyBuf.capacity(), dummyBuf);
In this case after first line execution i was getting a loss of 1x buffer size, as expected, but with the second, another 1x buffer size of memory was gone.
I tried it on 2 different devices utilizing different GPUs (Adreno, Mali) and was getting same behavior. So, my question is: am i missing something, or is this an expected behavior? Is there any way to decrease this RAM impact when supplying data to VBO?
Are you remembering to free the copy of the buffer you allocated?
In OpenGL ES all GPU server-side resources (textures, buffers, programs, etc) are copied into memory owned by the driver stack. Drivers can't just keep pointers to the buffers allocated by the application.
Once you have uploaded the data you can safely delete the application-side copy; it's not needed any more.
Related
I have a flutter app which uses Dart ffi to connect to my custom C++ audio backend. There I allocate around 10MB of total memory for my audio buffers. Each buffer has 10MB / 84 of memory. I use 84 audio players. Here is the ffi flow:
C++ bridge:
extern "C" __attribute__((visibility("default"))) __attribute__((used))
void *
loadMedia(char *filePath, int8_t *mediaLoadPointer, int64_t *currentPositionPtr, int8_t *mediaID) {
LOGD("loadMedia %s", filePath);
if (soundEngine == nullptr) {
soundEngine = new SoundEngine();
}
return soundEngine->loadMedia(filePath, mediaLoadPointer, currentPositionPtr, mediaID);
}
In my sound engine I launch a C++ thread:
void loadMedia(){
std::thread{startDecoderWorker,
buffer,
}.detach();
}
void startDecoderWorker(float*buffer){
buffer = new float[30000]; // 30000 might be wrong here, I entered a huge value to just showcase the problem, the calculation of 10MB / 84 code is redundant to the code
}
So here is the problem, I dont know why but when I allocate memory with new keyword even inside a C++ thread, flutters raster thread janks and I can see that my flutter UI janks lots of frames. This is also present in performance overlay as it goes all red for 3 to 5 frames with each of it taking around 30 40ms. Tested on profile mode.
Here is how I came to this conclusion:
If I instantly return from my startDecoderWorker without running new memory allocation code, when I do this there is 0 jank. Everything is smooth 60fps, performance overlay doesnt show me red bars.
Here are some screenshots from Profile mode:
The actual cause, after discussions (in the comments of the question), is not because the memory allocation is too slow, but lie somewhere else - the calculations which will be heavy if the allocation is big.
For details, please refer to the comments and discussions of the question ;)
EDIT:
I do not see this OOM error anymore. This was possibly related to some memory leak somewhere else in the app: I found one possible Activity leak: this could be the culprit and JavaMail has nothing to do with this activity leak... my mistake.
My simplified mail app is working fine for more than 1 year, but lately, I started to receive some bug report about OutOfMemoryError occurring on Samsung SM-T530 matissewifi 5.0.2 device (i.e. Galaxy Tab 4 (10.1'',Wi-Fi) ).
Code excerpt:
try
{
FlagTerm ft = new FlagTerm(new Flags(Flags.Flag.SEEN), false);
Message[] messages = folder.search(ft);
folder.fetch(messages, createFetchProfile());
return messages;
}
catch (Throwable th)
{
// I got OutOfMemoryError here because of folder.fetch(...):
/*
java.lang.OutOfMemoryError: Failed to allocate a 1036 byte allocation with 8388608 free bytes and 387MB until OOM; failed due to fragmentation (required continguous free 131072 bytes for a new buffer where largest contiguous free 65536 bytes)
or even this (with !0! for largest contiguous free bytes value!!!)
java.lang.OutOfMemoryError: Failed to allocate a 1036 byte allocation with 8388608 free bytes and 385MB until OOM; failed due to fragmentation (required continguous free 131072 bytes for a new buffer where largest contiguous free 0 bytes)
at com.sun.mail.iap.ByteArray.grow(SourceFile:161)
at com.sun.mail.iap.ResponseInputStream.readResponse(SourceFile:125)
at com.sun.mail.iap.Response.(SourceFile:121)
at com.sun.mail.imap.protocol.IMAPResponse.(SourceFile:66)
at com.sun.mail.imap.protocol.IMAPProtocol.readResponse(SourceFile:458)
at com.sun.mail.iap.Protocol.command(SourceFile:414)
at com.sun.mail.imap.protocol.IMAPProtocol.fetch(SourceFile:2440)
at com.sun.mail.imap.protocol.IMAPProtocol.fetch(SourceFile:2422)
at com.sun.mail.imap.IMAPFolder.fetch(SourceFile:1417)
at <mycode...>
*/
}
fetchprofile is
FetchProfile fetchProfile = new FetchProfile();
fetchProfile.add(UIDFolder.FetchProfileItem.ENVELOPE);
// fetch other info to speed up process
fetchProfile.add(UIDFolder.FetchProfileItem.FLAGS);
fetchProfile.add(UIDFolder.FetchProfileItem.UID);
fetchProfile.add(UIDFolder.FetchProfileItem.CONTENT_INFO);
fetchProfile.add(UIDFolder.FetchProfileItem.SIZE); // not sure about this one
I found grow() method source: here it is (quite simple):
public void grow(int incr)
{
byte[] nbuf = new byte[bytes.length + incr];
System.arraycopy(bytes, 0, nbuf, 0, bytes.length);
bytes = nbuf;
}
Manifest:
I already have
<application android:largeHeap="true">
in AndroidManifest.xml.
Questions:
Could this be related to a mail box containing so many email that mail API crash with OOM exception? I only fetch ENVELOPPE (and a few other things) , so I suppose this is not the correct explanation.
If, as I think, it's NOT related to the amount of email, what can I do about that?
Also, How should I interpret OOM message with value 0 for largest contiguous bytes!?! (sometimes happens, but not always)
java.lang.OutOfMemoryError: Failed to allocate a 1036 byte allocation with 8388608 free bytes and 385MB until OOM; failed due to fragmentation (required continguous free 131072 bytes for a new buffer where largest contiguous free 0 bytes)
Of course you should make sure that you're closing Folders when you're done with them, and not keeping references to Folders or Messages that you're no longer using. And make sure all the other parts of your application aren't causing memory leaks.
The Folder.fetch response needs to be kept in a single byte array. You can see how the byte array grows. Compared to the actual content of messages, the fetch information is usually quite small. But if you have thousands and thousands of messages, it could become large. Since you're only looking for unseen messages, it seems unlikely that that's the case.
I don't know what's possible on Android but if you could get a heap dump when this happens and examine it to determine which objects are using all the memory that would be most helpful.
i am making an application and on the start of application i want to display a picture but i get this out of memory exception. i used a lot of shared Preferences in the code that is not yet executed. so this rises 2 questions -
is memory for shared Preferences allocated at the beginning itself ?
and is the memory for shared Preferences allocated in the apps heap memory ?
should i clear the shared preferences and use database instead . will that solve the problem ?
Android hardware must at least support 2048x2048.
For OpenGL I used:
int[] maxSize = new int[1];
gl.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxSize, 0);
For Nexus 4 it equals to 4096
For Sansung 2 it equals to 4096
You can calculate [(Height in pixels) x (length in pixels) x (bit depth)] / 8 / 1024 = image size in kilobytes (KB)
So be sure that you have proper image size.
Hope it will help,
Here's a simplified version of the code I'm using
Java:
private native void malloc(int bytes);
private native void free();
// this is called when I want to create a very large buffer in native memory
malloc(32 * 1024 * 1024);
// EDIT: after allocating, we need to initialize it before Android sees it as anythign other than a "reservation"
memset(blob, '\0', sizeof(char) * bytes);
...
// and when I'm done, I call this
free()
C:
static char* blob = NULL;
void Java_com_example_MyClass_malloc(JNIEnv * env, jobject this, jint bytes)
{
blob = (char*) malloc(sizeof(char) * bytes);
if (NULL == blob) {
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "Failed to allocate memory\n");
} else {
char m[50];
sprintf(m, "Allocated %d bytes", sizeof(char) * bytes);
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, m);
}
}
void Java_com_example_MyClass_free(JNIEnv * env, jobject this)
{
free(blob);
blob = NULL;
}
Now when I call malloc() from MyClass.java, I would expect to see 32M of memory allocated and that I would be able to observe this drop in available memory somewhere.
I haven't seen any indication of that however, either in adb shell dumpsys meminfo or adb shell cat /proc/meminfo. I am pretty new to C, but have a bunch of Java experience. I'm looking to allocate a bunch of memory outside of Dalvik's heap (so it's not managed by Android/dalvik) for testing purposes. Hackbod has led me to believe that Android currently does not place restrictions on the amount of memory allocated in Native code, so this seems to be the correct approach. Am I doing this right?
You should see an increase in "private / dirty" pages after the memset(). If you have the extra developer command-line utilities installed on the device, you can run procrank or showmap <pid> to see this easily. Requires a rooted device.
Failing that, have the process copy the contents of /proc/self/maps to a file before and after the allocation. (Easiest is to write it to external storage; you'll need the WRITE_EXTERNAL_STORAGE permission in your manifest.) By comparing the map output you should either see a new 32MB region, or an existing region expanding by 32MB. This works because 32MB is above dlmalloc's internal-heap threshold, so the memory should be allocated using a call to mmap().
There is no fixed limit on the amount of memory you can allocate from native code. However, the more you allocate, the tastier you look to the kernel's low-memory process killer.
I want to acquirećAndroid Device VRAM size.
Is there a method for acquisition from the program?
Let's do some calculation using Nexus One:
Screen resolution is 480x800. So minimum required video memory size would be:
400 * 800 * 4 bytes = 1536000 bytes
Assuming that driver may (and normally should) use several buffers, we should also expect values like:
1536000 * 2 bytes = 3072000 bytes
1536000 * 3 bytes = 4608000 bytes
etc...
It would be weird to have values that are not multiple of 1536000 (or W x H x 4 in general).
After some searches on Android internals I've found this documentation :
...Android makes two requirements of the driver: a linear address space of mappable memory that it can write to directly...accessing the driver by calling open on /dev/fb0...
So I tried and take size of /dev/graphics/fb0 file (on my device there is no /dev/fb0).
But a direct approach doesn't work:
File file = new File("/dev/graphics/fb0");
file.length(); // ==0, doesn't work, no read access
Using next trick you can get actual size of fb0:
>adb pull /dev/graphics/fb0
1659 KB/s (4608000 bytes in 2.712s)
Video memory is ~4mb (Nexus One). Let's check if this is multiple of Nexus screen size:
4608000/1536000 = 3
It looks like a right value. And we also can say that driver uses three screen buffers.
So, as a conclusion, you can detect video memory size using adb, but you can't use this approach from your android application in runtime due to file access restrictions.
You typically do not have a dedicated "VRAM" on mobile devices. At least you don't have it with PowerVR architectures (wich totally dominate the market with their MBX and SGX cores).
That is, the OpenGL driver allocates normal RAM until you run out of it, and the more you allocate the less you have left for your application.
The Android/OpenGL APIs don't offer explicit methods to read the VRAM size from a given device.
Poor man solution:
You could try to infer the VRAM size in an empiric way adding 1MB texture until you get an out of memory error from gl.glGetError().
From your "dmesg" output u can read off the VRAM, so for my Tablet:
> [ 0.000000] Machine: TDM3730 [ 0.000000] Reserving 12582912
> bytes SDRAM for VRAM
>
> 7>[ 3.929962] VRAM: checking region 9f400000 3072
> <4>[ 3.929992] Failed. Allocating 4194304 bytes for fb 0
> <7>[ 3.935333] VRAM: alloc mem type 0 size 4194304 paddr dec2bd4c
> <7>[ 3.935485] VRAM: checking region 9f400000 3072
> <7>[ 3.935485] VRAM: found 9f400000, end a0000000
> <6>[ 3.936584] android_usb gadget: high speed config #1: android
> <4>[ 3.960113] allocating 4194304 bytes for fb 1
or details at:
http://pastebin.com/jQSXQqHh
Is simple just count how many Mb ram that from usable to real capacity of the ram, example for my lenovo a369i has 512 RAM Module, but in setting app only showing 471 Mb usable so the 41Mb left is reserved for the GPU, so the conclusion is my a369i has 41Mb vram
This method is based from shared graphics memory (wiki)
I suspect that android.os.StatFs is what you're looking for:
http://developer.android.com/reference/android/os/StatFs.html