3

I'm reading the Project Valhalla proposal to add value types to Java. It's arguing for explicit value types because the compiler can't automatically move allocations from the heap to the stack very well even if the objects are immutable (all fields are final) because it can't know if some code won't suddenly check the object for reference equality or try to lock on it. Object identity must be preserved, and with it heap allocation, garbage collection, and a boatload of overhead for small objects that are allocated in tight loops.

My question is why can't the object's identity be preserved on the stack as well? I mean, issue each object a unique id, and use it as its identity instead of its address. Immutable objects can then be always passed by value and never garbage collected.

It seems like a much simpler and more palatable solution than value types, even if some objects grow by another 8-12 bytes. I've used C#, and explicit value types are a pain. They add a lot of conceptual bloat to a language.

  • "issue each object a unique id" - and how would you do this in a multithreaded environment where the number of threads is not bounded? – Ordous Apr 6 '16 at 20:23
  • I think the better question is why should we care if we can easily identify which types are stack and heap allocated? I don't disagree that's it's occasionally useful to know, but there are no guarantees provided by the language that that is the case--it's all an implementation detail. – mgw854 Apr 6 '16 at 20:29
  • @Ordous Exactly how you issue objects unique addresses. Each thread gets ids in blocks/slabs. – Aleksandr Dubinsky Apr 6 '16 at 20:53
  • Can you explain more about the pain? I skimmed through this and it sees to me the only difference to the user of the language would be a qualifier on the class. This doesn't see too bad. Is your concern coming from another perspective or perhaps there is more to this that I'm missing? – JimmyJames Apr 6 '16 at 21:32
  • 1
    @JimmyJames Part of the pain is that it conceptually breaks classes into two categories with different rules - it makes the language more complex. Also, if the JVM can automatically figure out what can be allocated on the stack, it can optimize on a per method basis as opposed to the coarser per class basis and can possibly do a better job than a human can at identifying potential sites to optimize. – Solomonoff's Secret Apr 6 '16 at 21:37
4

The stack allocation is not why this proposal is being made. You're fixating on one minor benefit among a lengthy list of more substantial ones. Your alternative solution has a lot of overhead, especially when some of the actual main goals of the proposal are eliminating the space overhead of an object header (8 to 16 bytes) and a reference (4 to 8 bytes), and the time overhead of the pointer dereference, when they are not needed statically.

Regarding being able to determine the stack vs heap allocation, you're probably not as good at predicting it for reference types as you think you are, and that's a good thing. No one cares where their memory is allocated except C or C++ programmers, and they wish they didn't have to care.

Also keep in mind the JVM is used by other languages, like Scala, that already have value types and use immutable types extensively and could really benefit from the optimizations JVM support would enable.

  • Thank you, value types are becoming clear to me. I've got to say, though, that even though I write algorithms and deal with the type of code value types are meant to address, I've been pretty comfortable using "structs-of-arrays" and never felt a huge need for "arrays-of-structs." They don't have top performance anyway. I have, however, been bitten by passing around transient utility objects (eg, views into datastructures) that did not get optimized onto the stack. Having to pass those as individual primitives is awful. So basically, I disagree that stack allocation is the minor benefit. – Aleksandr Dubinsky Apr 7 '16 at 7:26
1

Preserving identity on the stack can work.

However, the resulting JIT optimization will not be as widely-applicable as true value types. For example, value types can be stored (by inlining) as fields inside objects, in arrays, etc. Objects on the stack, meanwhile, can be stored only inside local variables of functions.

Nevertheless, if we limit the scope to optimizing transient small objects (which do occur in Java a lot), preserving identity does work. However, things get a tad hairy if we find ourselves passing the object to a context that expects a reference. (Eg, try to store it in a field.) We can allocate a heap "double" and use its address. We'll add this address to a map. Next time, we look up the object's ID in the map to get the address of the same heap double. At GC time, we'll need to check over our stack-allocated objects to know which heap doubles may be freed. What mitigates this cost is the fact that the number of local variables isn't usually large, and if we find ourselves creating many heap doubles, we can change the methods back to using heap-allocated objects.

On balance, this appears a reasonable optimization compared to relying solely on escape analysis. It would be interesting to hear why, on balance, the JIT team decided against implementing it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.