This one is "tricky" in a fun way.
>From a language semantics point of view, the compiler is probably allowed
to perform the optimization suggested (removing the null assignment to a
non-volatile field, knowing that it will soon be overwritten), which does
raise an interesting point about the language semantics and OOM conditions.
However, from a practical implementation point of view, it is "hard" [my
shorthand for "it may be impossible, but I can't prove it, or don't want to
bother trying"] to perform this optimization in a way that would eliminate
the null assignment from a GC perspective, because there is an object
allocation between the two assignments. The difficulty comes from the fact
that even in optimized code (in current JVM implementations), all
allocation sites retain the ability to take a safepoint. Here is the logic:
- The allocation attempt *may* need to wait for a GC to complete (e.g. if a
GC is needed in order to produce the empty memory that the allocation will
use).
- A GC can't be guaranteed to complete (in all current practical JVM
implementations) without transitioning all threads (at the very least
temporarily and individually, if not simultaneously and globally) to a
safepoint.
- Since the thread must be able to "come to a safepoint" at the allocation
site (which sits between the first and second assignments), and since
safepoints can end up being used for things other than GC (such as
deoptimization, breakpoints, etc.), the JVM state at the safepoint must be
completely reconstructible.
- If a safepoint is taken at the allocation site. the state of the JVM at
that safepoint would include the memory outcome of the first assignment and
*not* include the outcome second assignment.
- Therefore, *IF* the safepoint is taken, the null assignment must occur
before it is taken.
Technically, this can either defeat the optimization altogether (which is
what it will do for most JITs). However, it is possible to keep the
optimization in the fast path (allocation doesn't take a safepoint) if a
JIT was able to push code into the path between the poll to determine if a
safepoint is needed and actually reaching the safepoint. If the JIT has
that ability, the null assignment code could be moved around such that it
occurs only if a safepoint is actually taken, and is skipped if a safepoint
is not taken.
Either way (regardless of whether the optimization is defeated, or the null
is moved to happen only in the safepoint-taking path), the null assignment
would occur before GC is ever forced at the allocation site. This will
explain why you won't see an OOM on that allocation on current JVMs, even
with JIT-optimized code, even if the heap is only large enough to
accommodate one copy of the byte[].
On Tuesday, November 13, 2018 at 9:28:18 AM UTC-8, Shevek wrote:
>
> Given the following code:
>
> byte[] data = ...;
>
> {
> data = null;
> data = new byte[N];
> }
>
> Is the compiler allowed to discard the assignment of null, because it's
> "dead" in language terms? My argument is that it isn't dead because it
> allows the garbage collector to respond to pressure within the 'new' and
> reuse the space, but in language terms, this presumably isn't defined,
> and it would seem to be legal for the first assignment to be removed.
>
> Thank you.
>
> S.
>
--
You received this message because you are subscribed to the Google Groups
"mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.