2014-07-29

Diabling JIT for saving RAM on low RAM Android device

System-wide JIT memory usage is dependent on the number of applications running and the code footprint of those applications. The JIT establishes a maximum translated code cache size and touches the pages within it as needed. JIT costs somewhere between 3M and 6M across a typical running system.

The large apps tend to max out the code cache fairly quickly (which by default has been 1M). On average, JIT cache usage runs somewhere between 100K and 200K bytes per app. Reducing the max size of the cache can help somewhat with memory usage, but if set too low will send the JIT into a thrashing mode. For the really low-memory devices, we recommend the JIT be disabled entirely.

For how much memory JIT code cache size consumed, I checked Normandy dalvik source and have below comments:

1. The default code cache size is 1.5M.

File: dalvik/vm/Globals.h
#define DEFAULT_CODE_CACHE_SIZE 0xffffffff
 
File: dalvik/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.cpp
/* Architecture-specific initializations and checks go here /
bool dvmCompilerArchVariantInit(void)
{
   ...
   gDvmJit.threshold = 40;
   if (gDvmJit.codeCacheSize = DEFAULT_CODE_CACHE_SIZE) {
        gDvmJit.codeCacheSize = 1500 * 1024;
   } else if ((gDvmJit.codeCacheSize = 0) && (gDvm.executionMode == kExecutionModeJit)) {
        gDvm.executionMode = kExecutionModeInterpFast;
   }


2. Dalvik create Anonymous Shared Memory with that size

File: dalvik/vm/compiler/Compiler.cpp
 
bool dvmCompilerSetupCodeCache(void)
{
    int fd;
    / Allocate the code cache /
    fd = ashmem_create_region("dalvik-jit-code-cache", *gDvmJit.codeCacheSize);
    ...
    gDvmJit.codeCache = mmap(NULL, gDvmJit.codeCacheSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE , fd, 0);


That means each VM will consume 1.5M memory for holding code cache size. Moreover, Dalvik system will consume 2M for system wide usage. Meaning that 38 Java process->38 VM->38*1.5+2=59M.

Since disabling JIT will drop Java performance a lot, at least from benchmark result, I prefer reducing code cache size instead of disable it totally. I'm still testing 512K per VM. I will test 256K next week or maybe this weekend. For 512K case, I didn't see any significant performance drop when launching/playing Java app,like game. But I need more test.

JIT can be fisabled by adding the following line to the product makefile(e.g. build/target/product/core.mk):
PRODUCT_PROPERTY_OVERRIDES += dalvik.vm.jit.codecachesize=0

But to disable JIT, only set 'dalvik.vm.jit.codecachesize=0' indeed doesn't work. The reason is because 4.1's AndroidRuntime?::startVm method even not ready that property at all.

I checked 4.4 Dalvik, basically below 3 commits introduced JIT disabling feature.
- "Process new system property for max JIT cache size" (SHA-1 b63de6de026b8ebe0b7d7b7f188afc30fff42411)
To allow low-memory devices to reduce (or eliminate entirely) the RAM used by the JIT, dalvikvm has a new command-line option to set the max size of the JIT's translation cache. In this CL, we pass that new option based on a system property.

- "JIT tuning; set cache size on command line" (SHA-1 bbbe552a31f7229708bfc748480ce538218ae076)
The tuning knobs for triggering trace compilation for the JIT had not been revisited for several years. In that time, the working set of some applications have significantly increased, leading to frequent cache overlows & flushes. This CL adds the ability to set the maximum size of the JIT's cache on the command line, and we expect to use different settings depending on device configuration (rule of thumb: 1K for each 1M for system RAM, with 2M limit). Additionally, the trace compilation trigger has been tightened to limit the compilation of cold traces.

- "Suppress warning if JIT disabled" (SHA-1 b6ffb72838cc4a8f60028c21ed740c5f48c89c80)

Set "dalvik.vm.execution-mode=int:fast" seems only impact execution mode of VM. I doubt that execution mode will not impact memory used by JIT.

File: frameworks/base/core/jni/AndroidRuntime.cpp
property_get("dalvik.vm.execution-mode", propBuf, "");
if (strcmp(propBuf, "int:portable") = 0)  {
    executionMode = kEMIntPortable;
} else if (strcmp(propBuf, "int:fast") 0)  {
    executionMode = kEMIntFast;
#if defined(WITH_JIT)
  } else if (strcmp(propBuf, "int:jit") = 0) {
    executionMode = kEMJitCompiler;

No comments: