Fwd: Feedback about StableValues(Preview)
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Wed Sep 3 00:03:00 UTC 2025
On 03/09/2025 00:54, david Grajales wrote:
> Hello Maurizio. Thanks for the quick response and explanation.
>
> So, in short the stable values are not mean for very dynamic scenarios
> because the API prioritize performance and efficiency at runtime and
> sacrifices flexibility in exchange; which translates as " the keys or
> at least the size of the stableValue must be know at compile time".
I think so -- e.g. to get better performance than a concurrent hash map
you need to give up some flexibility. If you don't, that's fine, but
then concurrent hashmap is likely the best implementation for your use case.
I think the main use cases for the stable lists and maps we provide is
to deal with groups of stable values. E.g. think of a situation where a
class has maybe 10 fields like Logger -- fields that are lazily
initialized. Do you declare 10 holder classes? Or 10 stable values, each
with its own update logic? With a stable map you can actually group
these stable values together -- which means you only need one lambda,
not 10.
Maurizio
>
> Best regards.
>
> El mar, 2 de sept de 2025, 6:14 p.m., Maurizio Cimadamore
> <maurizio.cimadamore at oracle.com> escribió:
>
> Hi David,
> Thanks for your feedback.
>
> The factories that we provide, like StableValue::list and
> StableValue::map cover common cases where the list/map elements
> are known upfront, but should be computed lazily.
>
> In other words you might think of these factories as lazy variants
> of List::of and Map::of. Both kinds of factories return an
> unmodifiable collection -- that is a collection whose size is
> fixed, and that rejects update operations.
>
> I understand that you would like to create a "stable" map, whose
> key/values are not known upfront -- more specifically, where the
> keys are only known dynamically.
>
> I believe in these cases the "win" for using a stable map in the
> first place is much less obvious. If the map can grow dynamically
> (e.g. because you don't know how many entries you might be adding
> to it) you are probably looking at an implementation that has some
> way to "resize" itself -- which makes using something like a
> stable construct much harder. For instance, adding new entries on
> a map might cause the underlying array of buckets to be
> reallocated, and existing entries to be rehashed in the new
> (larger) bucket array. This means that the bucket array itself
> will need to be updated several times during the lifecycle of the
> map, making it not stable (remember: stable means "updated at most
> once").
>
> If some constraints are relaxed, e.g. maybe you know how many
> entries you are going to add in your map -- that might make the
> problem easier, as now we're back in a situation where we now the
> size of the underlying storage. For instance one can have a
> specialized hash table implementation backed by a linear array (of
> an appropriate size), and then use linear probing to store entries
> in the linear array. Since the size is bounded, the size of the
> entries linear array is also bounded, and we can then make that
> linear array stable (e.g. use a stable list).
>
> Since such a "fixed size" hash map would be quite specialized, we
> did not see yet enough motivation for adding it to the JDK --
> especially given that developers should be able to define such
> constructs on top of the StableValue API (in fact _all_ the
> existing provided factories are defined in terms of the Stable
> Value API).
>
> But it's not just about whether the size is known or not -- in
> order for the JVM to be able to apply any sort of constant-folding
> optimization to the map access, you need the key to be a constant
> (e.g. either some compile-time constant, or the value of a static
> final field, or the contents of some other stable value). Only
> then we can fold the entire map access expression (if we're
> lucky). But in the example you provide, the key provided to
> Map::get is just a random class name you get from the current
> stack. So there's very little for the JIT to optimize here. If the
> input (the key) is not known, then the access expression
> (Map::get) cannot be optimized.
>
> In other words, the case of a fully dynamic list/map that you
> propose just doesn't seem a great fit for stability, in the sense
> that you likely won't get any performance improvement (over a
> concurrent hash map) by using some sort of stable map there.
>
> Maurizio
>
>
> On 02/09/2025 02:45, david Grajales wrote:
>>
>>
>> ---------- Forwarded message ---------
>> De: *david Grajales* <david.1993grajales at gmail.com>
>> Date: lun, 1 sept 2025 a la(s) 8:43 p.m.
>> Subject: Feedback about StableValues(Preview)
>> To: <core-libs-dev at openjdk.org>
>>
>>
>> Subject: Feedback and Questions on JEP 8359894 - Stable Values API
>>
>> Dear Java core-libs development team,
>>
>> Please accept my sincere gratitude and compliments for your
>> ongoing dedication to improving the Java platform. The continuous
>> innovation and thoughtful evolution of Java is truly appreciated
>> by the developer community.
>>
>> I have been experimenting with the Stable Values API (JEP
>> 8359894) in a development branch of a service at my company, and
>> I would like to share some observations and seek your guidance on
>> a particular use case.
>>
>>
>> Current Implementation
>>
>> Currently, I have a logging utility that follows a standard
>> pattern for lazy value computation:
>>
>>
>> class DbLogUtility {
>> private static final ConcurrentMap<String, Logger>
>> loggerCache = new ConcurrentHashMap<>();
>>
>> private DbLogUtility(){}
>>
>> private static Logger getLogger() {
>> var className =
>> Thread.currentThread().getStackTrace()[3].getClassName();
>> return loggerCache.computeIfAbsent(className,
>> LoggerFactory::getLogger);
>> }
>> public static void logError(){
>> //.... implementation detail
>> }
>> }
>>
>>
>> Challenge with Stable Values API
>>
>> When attempting to migrate this code to use the Stable Values
>> API, I encountered a fundamental limitation: the API requires
>> keys to be known at compile time. The current factory methods
>> (|StableValue.function(Set<K>, Function)| and
>> |StableValue.intFunction(int, IntFunction)|) expect predefined
>> key sets or bounded integer ranges.
>>
>> This design constraint makes it challenging to handle dynamic key
>> discovery scenarios, which are quite common in enterprise
>> applications for:
>>
>> * Logger caching by dynamically discovered class names
>> * Configuration caching by runtime-determined keys
>> * Resource pooling with dynamic identifiers
>> * Etc.
>>
>>
>> Questions and Feedback
>>
>> 1. *Am I missing an intended usage pattern?* Is there a
>> recommended approach within the current API design for
>> handling dynamic key discovery while maintaining the
>> performance benefits of stable values?
>> 2. Would you consider any of these potential enhancements:
>> * Integration of stable value optimizations directly into
>> existing collection APIs (similar to how some methods
>> have been added to List and Map interfaces for better
>> discoverability)
>> * A hybrid approach that provides stable value benefits for
>> dynamically discovered keys
>> 3. Do you envision the Stable Values API as primarily serving
>> compile-time-known scenarios, with dynamic use cases
>> continuing to rely on traditional concurrent collections?
>>
>> Thank you for your time and consideration. I would be grateful
>> for any guidance or clarification you might provide on these
>> questions. If there are planned enhancements or alternative
>> patterns I should consider, I would very much appreciate your
>> insights.
>>
>> Best regards, and always yours.
>>
>> David Grajales Cárdenas.
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20250903/9b0abd86/attachment-0001.htm>
More information about the amber-dev
mailing list