TSS Asks: JVM memory management v. CPython and CRuby

Discussions

News: TSS Asks: JVM memory management v. CPython and CRuby

  1. One of the issues in moving from the native versions of Python and Ruby to the Java equivalents (Jython and JRuby, for example) is that the memory models change. This probably won't affect most programs, but it's something to consider, and is probably actually a huge win for JRuby and Jython. For example, Python uses a simple (well, potentially simple!) memory manager based on malloc() and free(), with different object-specific allocators on the same heap (see "Memory Overview" from the Python docs). Ruby documentation on memory management was hard to find; googling for Ruby and GC yielded the best results, but it looks like Ruby also manages all memory itself, providing garbage collection through ptmalloc (one post from 2007 suggested fixing some heap fragmentation issues by linking to ptmalloc3 instead of ptmalloc2.) Compared to Java, Ruby and Python memory management seems fairly cryptic (albeit simple). Java virtual machines have different memory management approaches and garbage collection algorithms available, but even the varieties are well-known and documented. The question, then, revolves around TSS readers: have you noticed any issues caused by the JVM memory managers compared to the native Python and Ruby memory managers? (Groovy and Scala don't suffer from these issues because they run on the JVM in the first place.) Have you noticed any performance improvements related to memory on the JVM? Even if you haven't, it's probably something to consider when writing Ruby or Python code for the JVM: things that might be necessary to compensate for GC issues in CRuby or CPython (to wit: patches for Rails that change allocation strategies) might not be necessary at all for JVM-based code, and things that leverage the JVM's ability to garbage-collect efficiently (or at least with tuning parameters) might run extremely poorly on CRuby or CPython.
  2. Memory management in CRuby sucks. It uses, essentially, a conservative GC for the stack and CPU registers. I.e. it treats any integer on the stack or in CPU register as a potential pointer. It allows 'transparent' garbage collection in C code, but disallows a LOT of optimizations (such as heap defragmentation). Also, conservative GC in Ruby is SLOW - garbage collector in Sun JVM is definitely faster. CPython is a little bit more tricky. It uses reference counting with cycle detection (essentially, a form of GC). Refcounting in CPython is also quite effective because it doesn't use synchronization - CPython interpreter is effectively single-threaded (it uses Global Interpreter Lock for almost all operations).
  3. All commercial JVMs (and MSFT's .NET as well) have been using precise (aka exact) GC algorithms for quite some time now. The major benefit for new languages (dynamic, scripting, DSLs) and frameworks in targeting the JVM or .NET CLR is that they can then leverage a modern, JIT'ed and garbage collected environment that can run robustly and fast in a production environment. Having solved the precise-GC problem about a decade ago, these runtimes tend to have JIT compiler optimization capabilities that turn into very real code performance benefits when compared to "last century" environments. The key technical thing to get over here is that for GC to be robust, the VM absolutely has to know *exactly* where all the pointers are at all times (and where they are not). Otherwise, the GC mechanism will not be able to safely move objects in memory, and to compact memory to survive fragmentation (which *always* happens). Without this capability "interesting" things happen over time, and your program becomes "sensitive" (read: you need to bounce it every once in a while). The majority of the work in the VM needed to achieve this (other than extreme discipline needed in coding the runtime to treat all heap pointers as such, and not miss any), is that all precise-GC environment JIT compilers must provide tracking information for all reference state, at every possible point where GC may need to know where the pointers are - this means that compilers for precise GC environments have to produce a significant amount of additional information, precisely describing the parts of the stack and registers that hold references at every interesting code location (usually termed safe points). Without the compiler doing this, you either can't JIT, or can't do compaction... (so you either can't run fast or can't run for long). -- Gil. Azul System