CtrlF CtrlF - 25 days ago 7
Android Question

Android NDK: Why is this malloc() having no observable effect?

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?

Answer

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.