Reference favoring primitive class heap allocations

Stig Rohde Døssing stigdoessing at gmail.com
Fri Sep 3 18:01:51 UTC 2021


When Optional becomes a reference-favoring primitive class, my
understanding is that calling methods like
static Optional<T> of(T item)
will cause heap allocations unless the call gets inlined/optimized, right?
So even completely new code that uses Optional.val consistently, e.g.
Optional.val<String> opt = Optional.of("hello")
can't be trusted not to heap allocate, since it is hard to know up front
whether the "of" call will be optimized at runtime. I am concerned that
this would make it hard to trust Optional not to end up in the heap when
used in allocation-sensitive code, which seems unfortunate.

I'm wondering if you are intending to switch the return types of existing
APIs that can't return null, so methods like "of" return Optional.val
instead, while still having Optional be reference-favoring? If "of" were
declared as
static Optional.val<T> of(T item)
then developers writing new code can be reasonably sure that Optional
instances will be flattened when calling "of" (and other such methods), as
long as they remember to use Optional.val and not Optional in their own
code.

Den tor. 2. sep. 2021 kl. 11.25 skrev Stig Rohde Døssing <
stigdoessing at gmail.com>:

> Thank you, that makes sense.
>
> Den tor. 2. sep. 2021 kl. 10.52 skrev <forax at univ-mlv.fr>:
>
>>
>>
>> ------------------------------
>>
>> *From: *"Stig Rohde Døssing" <stigdoessing at gmail.com>
>> *To: *"Remi Forax" <forax at univ-mlv.fr>
>> *Cc: *"valhalla-dev" <valhalla-dev at openjdk.java.net>
>> *Sent: *Jeudi 2 Septembre 2021 00:23:29
>> *Subject: *Re: Reference favoring primitive class heap allocations
>>
>> Thanks Rémi, I think it mostly does. I'll admit that the bit about
>> optimizing local T.ref goes a bit over my head.
>>
>>
>> My point was that T.ref declared as a local variable can be optimized by
>> the JIT avoiding heap allocation.
>>
>> Just today Tobias submit a PR that shows how c2 try to avoid T.ref
>> allocation on heap for local variables even when there is no inlining
>> https://github.com/openjdk/valhalla/pull/543
>>
>>
>> What about method return values? If I declare a method as
>>
>> T.val someMethod()
>> vs
>> T.ref someMethod()
>>
>> and do
>>
>> T.val someLocal = someMethod()
>>
>> would the two methods have different behavior for how the value is
>> returned from the call (Return the value in registers/put it on the
>> caller's stack vs.put the object on heap and return a pointer to that), or
>> is this determined by whether "someLocal" is a val or a ref and doesn't
>> have anything to do with the return type of "someMethod"?
>>
>>
>> The two methods have almost the same behavior, apart from the fact that
>> T.ref allows null while T.val does not.
>> But they do not have the same "performance model". If you return T.val
>> you ask to flatten T so it works even without inlining while if you return
>> T.ref, the performance depend on the JIT being able to inline/optimize (so
>> you may get the same performance or not).
>>
>> Rémi
>>
>>
>>
>> Den ons. 1. sep. 2021 kl. 23.06 skrev Remi Forax <forax at univ-mlv.fr>:
>>
>>> ----- Original Message -----
>>> > From: "Stig Rohde Døssing" <stigdoessing at gmail.com>
>>> > To: "valhalla-dev" <valhalla-dev at openjdk.java.net>
>>> > Sent: Mercredi 1 Septembre 2021 12:43:46
>>> > Subject: Reference favoring primitive class heap allocations
>>>
>>> > Hi,
>>> >
>>>
>>> Hi,
>>>
>>> > JEP 401 describes that current value-based classes will be made
>>> reference
>>> > favoring to avoid breaking existing code. Optional is one of these. I'm
>>> > trying to understand the impact this will have on existing APIs with
>>> > respect to heap allocation.
>>> >
>>> > Please assume for the following code snippets that ordinary scalar
>>> > replacement does not apply.
>>> >
>>> > As I understand it, code like
>>> >
>>> > Optional.val of(T item) { new Optional.val(item) }
>>> > Optional.val<String> s = of("Hello")
>>> >
>>> > would tell the JVM that the Optional does not necessarily have to go
>>> on the
>>> > heap, but could maybe go on the stack or in registers instead.
>>> >
>>> > When "of" is instead defined with the reference type, as in
>>> >
>>> > Optional of(T item) { new Optional(item) }
>>> > Optional.val<String> s = of("Hello")
>>> >
>>> > will the Optional always go on the heap?
>>> >
>>> > Also is the plan to be able to avoid the heap for return values, or
>>> will
>>> > the return value of
>>> >
>>> > Optional.val of(T item)
>>> >
>>> > always go on the heap unless "of" happens to be inlined?
>>> >
>>> > Thanks for reading.
>>>
>>> Nope, there is only one way to create an object which is a primitive
>>> class.
>>>
>>> Let's use an analogy, an ArrayList<String> at runtime can be typed as
>>> List<?> or as List<String>, but it's the same ArrayList at runtime.
>>>
>>> With primitive classes, it works the same way, a primitive class is
>>> always a primitive class at runtime,
>>> there is no two different ways to create a primitive class, there is
>>> only one way (to call the factory method named <new> of Optional).
>>> What is different, is that a primitive class can be typed in two
>>> different ways, as an Optional.val or as an Optional.ref,
>>> those two types mean different things in term of storage for a field, an
>>> array element or a local variable.
>>>
>>> T.val:
>>>   For fields and array elements, all the fields (if there are not too
>>> many) of T.val are stored in the container of the T.val (the class or the
>>> array containing the T.val)
>>>   For a local variable, T.val means that the VM will use registers (or
>>> spill the values on stack due to the register allocator) independently of
>>> the inlining algorithm.
>>>
>>> T.ref:
>>>   For fields and array elements, a T.ref is stored as a reference (on
>>> heap).
>>>   For a local variable, T.ref means that inside the inlining horizon,
>>> the VM can optimize the code using escape inlining with late
>>> rematerialization, so better than a classical reference because a primitive
>>> class has no identity (in practice it's a little more complicated because
>>> you don't want to inflate/deflate the same object inside the inlining
>>> horizon).
>>>
>>> I hope it answers to your questions.
>>>
>>> regards,
>>> Rémi
>>>
>>
>>


More information about the valhalla-dev mailing list