Hacker Read top | best | new | newcomments | leaders | about | bookmarklet login

Escape analysis is very limited and what I found in practice, it often doesn't work in real code, where not all the things are inlined. If a method allocates an object and returns it 10 layers up, EA can't do anything.

In contrary, in e.g. C I can wrap two 32-bit fields in a struct and freely pass then anywhere with zero heap allocations.

Also, record types are not going to fix the pointer chasing problem with arrays. This is promised by Valhalla, but I'be been hearing about it for 3 years or more now.



sort by: page size:

It's mostly an issue with poor escape analysis and the tendency to use heap allocation for data that could go on the stack. This isn't a requirement of the language/runtime though (I believe) so this could be improved, but maybe some property or feature of the language is what makes escape analysis hard?

I'm not sure what benefit you're picturing from escape analysis, but EA is largely pointless because GCs have gotten so good that stack allocation of objects is really no better than heap allocation plus collection of short-lived objects.

I worked for years on escape analysis in IBM's Java JIT compiler. We struggled to find any actual programs that showed any benefit at all. The real benefits of escape analysis were second-order effects like eliminating monitor operations on non-escaping objects, or breaking apart objects and putting their fields into registers (especially autoboxed primitives). The actual stack allocation wasn't really any faster than heap allocation, and a GC operation in the nursery doesn't even look at dead objects.

EA is basically a microbenchmark-killer. For real software, it's not often worth the trouble.


No mention of escape analysis. That's where I'm placing my bets.

http://en.wikipedia.org/wiki/Escape_analysis

Most short-lived objects can be stack allocated. Then the heap gets much less of a work out.

The runtime can figure out heap vs stack. Progressively better over time. Definitely better than I can do.

C# has (had?) the option of explicit stack allocated data (pseudo objects). Terrible idea. Premature optimization that prevents the runtime from doing a better job.

If I was doing embedded, realtime, or kernel dev work, I'd want to fiddle the bits myself. But I don't so I don't.


This does happen partially with escape analysis within a function. Objects/primitives could be allocated on the stack instead. I totally agree that this should be an area that could be explored more in depth.

> Are you referring to heap vs. stack? That is not as simple as it seems, because of escape analysis. The only thing that Go lets you do that Java doesn't is put structs inside other structs, which is nice, but it's not a game-changer.

This isn't true. I can get a contiguous slice of structs, and I can pass value types by copy without needing to reason about escape analysis at all. If I do `var foo Foo` (assuming Foo is a struct type), I know that `foo` won't escape unless I return a pointer to it (or to something in it), at which point it's subject to escape analysis. I can get quite a long way in Go without needing to reason about escape analysis at all.

> I don't see how the HotSpot JVM escape analysis can be "simpler" than that of Go. They work the same way.

I'm pretty sure they don't work the same way, but I don't have any examples off hand to share (it's been a while since I worked with Java). I think Java's escape analysis is more sophisticated.


It's not about escape analysis.

The programmer explicitly specifies when to use pointers. Whenever pointers are not used, the object is stored in-place (for example, on the stack, in the array or slice, or in the struct).


I'm of the impression that Java has done escape analysis for a while now. They just haven't had value types, which as I understand, just introduce a semantic for stack allocation.

C# doesn't have escape analysis, but it has stack-allocated value types. Java doesn't have stack-allocated value types (yet), but it has escape analysis. It seems a lot harder to me to reason about escape analysis without value types, and I have no idea what happens in C# if you take a reference to a value type (i.e., is it moved to the heap?).

I never meant to imply escape analysis was somehow perfect; only that it is (mostly) easy to reason about.


Java does do escape analysis to allocate certain things on the stack.

As opposed to what exactly? How would a C program’s memory graph look with quick bursts of memory-allocation requiring functionality, especially if it is very dynamic in nature? Yeah you can overallocate with memory pools and the like, and there are cases of course where escape analysis can’t help — that’s why Valhalla is in the works for quite some times now.

But GC-wise the JVM is far ahead the game, whatever you see is likely better than the same functionality would be under JS, Python, C#, Go, etc (though the latter two do have value types/structs already so they can at some place manually do the “escape-analysis”. But not every problem requires/can use value types either)


.net already has in-place allocated types (e.g. 'structs') which are dropped on the stack or inside the allocation of their owner (in the case of members fields), which explicitly covers most cases that escape analysis tries to handle implicitly. However, they still need to be heap-alloc'd when boxed (e.g. when used as a IEnumerator or something), which would definitely benefit from hotspot-style allocation-inlining (though the C# compiler already tries to do this prejit for common cases like iterators, too, for practicality's sake).

Not sure if this is what the OP is referring to, but escape analysis determines whether a variable can be on the stack of a single function, or if it must be the heap (because it "escapes" the function's lifetime).

So if you can inline more functions, then you can allocate more objects on the stack rather than the heap.


I _think_ the author was just trying to convey that it's no longer stack allocated in this case. Escape analysis is really nice, but when you need performance, it can get in the way. I've talked to people who ended up having to leave a ton of comments with the equivalent of "dont do this, it messes up escape analysis and performance goes south" all over their codebase.

Yes, escape analysis does tend to eliminate some unnecessary heap allocations but they're specifically isolated to a single subroutine (it's better than not having it). Whereas with Rust, you can safely pass around references to stack allocated memory to other subroutines, given proper lifetimes.

Java does escape analysis, but if I remember correctly, it is no longer used for stack allocation (it is used for other advanced stuff, like lock elision, and replacing objects with scalars) because the JVM GCs have gotten so good that stack allocation provided no significant benefit. The reason is that stack allocation is relevant only for short-lived objects anyway, and for generational GCs, short-lived objects are (almost) a non-issue. GCs struggle much more with long-lived objects, which eventually require compaction, whose cost rises linearly with the size of the live data set. This is the cause of the problems with large heaps mentioned in the comments below.

Managed languages almost never offer comparable escape hatches. Eg, it's not possible to do anything interesting at all with the JVM. Nor with JS. Nor with Python. Etc..

None of them let you do either placement allocations nor arena allocations.

.NET at least lets you do a linear array of structs, but that's nearly it.


Correction: JVM implementations perform escape analysis, in particular, because Java does not have structs. .NET does not perform escape analysis for objects and all attempts to experiment with it so far has shown greater impact on compilation speed with little to no profit in allocation rate. However, you will find average allocation traffic much lower in .NET because C# already puts way more data on stack through structs, non-boxing operations (where Java boxes), stack allocated buffers, non-copying slicing with spans and object pooling (which is common in Java too).

False. The JVM already does quite a good job with escape analysis, and record types just add extra semantic information to potentially further improve the situation.

Stack allocation that depends on escape analysis is not very predictable at all, unless the programmer can force an error if escape analysis fails.

Explicit stack allocation via pass/return-by-value structs (as in C#) works well if predictability is a goal.

next

Legal | privacy