AW: Frozen objects?
Markus Karg
markus at headcrashing.eu
Wed Dec 20 10:31:33 UTC 2023
C++ ("const") does not freeze the memory region at all, and it does not need to (and hence is quite fast at runtime as it does not even need to check the access). The compiler simply rejects to compile the attempt to write via read-only references. That would be sufficient for most cases. Freezing objects is a different idea, and only needed in side cases. So I would plea for introducing compile-time read-only references first, as it is the lower hanging fruit.
-Markus
Von: Holo The Sage Wolf [mailto:holo3146 at gmail.com]
Gesendet: Dienstag, 19. Dezember 2023 14:18
An: Markus Karg
Cc: Dan Heidinga; Archie Cobbs; amber-dev
Betreff: Re: Frozen objects?
How do you freeze a memory region without talking about freezing objects?
Unless your data is flat (so only value classes, primitives and arrays, 2 of which won't benefit from freezing) the only way to have freezing something that is enforced at compile time you must talk about objects.
On Tue, 19 Dec 2023, 10:04 Markus Karg, <markus at headcrashing.eu> wrote:
I wonder why we discuss about freezing *objects* (which needs time) but not simply freezing *references* (like `const` does in C++)?
-Markus
Von: amber-dev [mailto:amber-dev-retn at openjdk.org] Im Auftrag von Dan Heidinga
Gesendet: Montag, 18. Dezember 2023 16:04
An: Archie Cobbs; amber-dev
Betreff: Re: Frozen objects?
Let me throw out one other concern: races. The invariant frozen objects want is that the application and runtime can trust they will never be mutated again. Unfortunately, if the object is published across threads before it is frozen, then that invariant is very difficult and expensive to maintain.
If two threads, A & B, both have references to the object and thread A freezes it, B may still be publishing writes to it that A only observes later. To ensure the right JMM happens-before relationship for fields of Freezable objects, both reads and writes would need to be more expensive (volatile semantics?) until a thread could validate the object it was operating on was frozen.
Freezing is not just a free set of unexplored optimizations. There’re also new costs associated with it across the runtime (field read/write, profiling, etc).
--Dan
From: amber-dev <amber-dev-retn at openjdk.org> on behalf of Archie Cobbs <archie.cobbs at gmail.com>
Date: Saturday, December 16, 2023 at 12:33 PM
To: amber-dev <amber-dev at openjdk.org>
Subject: Frozen objects?
Caveat: I'm just trying to educate myself on what's been discussed in the past, not actually suggest a new language feature. I'm sure this kind of idea has been discussed before so feel free to point me at some previous thread, etc.
In C we have 'const' which essentially means "the memory allocated to this thing is immutable". The nice thing about 'const' is that it can apply to an individual variable or field in a structure, or it can apply to an entire C structure or C array. In effect it applies to any contiguous memory region that can be named/identified at the language level.
On the other hand, it's just a language fiction, i.e., it can always be defeated at runtime by casting (except for static constants).
In Java we have 'final' which (in part) is like 'const' for fields and variables, but unlike C 'final' can't be applied to larger memory regions like entire objects or entire arrays.
In C, 'const' can be applied "dynamically" in the sense I can cast foo to const foo. Of course, this is only enforced at the language level.
Summary of differences between C 'const' and Java 'final':
· Granularity:
o C: Any contiguous memory region that has a language name/identification
o Java: At most 64 bits at a time (*) and arrays are not included
o Advantage: C
· Enforcement:
o C: Enforced only by the compiler (mostly)
o Java: Enforced by the compiler and at runtime
o Advantage: Java
· Dynamic Application:
o C: Yes
o Java: No
o Advantage: C
(*) With records and value objects we are gradually moving towards the ability for larger things than an individual field to be 'const'. More generally, Java has slowly been glomming on some of the goodness from functional programming, including making it easier to declare and work with immutable data.
This all begs the question: why not take this idea to its logical conclusion? And while we're at it, make the capability fully dynamic, instead of limiting when you can 'freeze' something construction time?
In other words, add the ability to "freeze" an object or array. If 'x' is frozen, whatever 'x' directly references becomes no longer mutable.
A rough sketch...
Add new Freezable interface:
public interface Freezable {
boolean isFrozen();
static boolean freeze(Freezable obj); // returns false if already frozen
}
Arrays automatically implement Freezable (just like they do Cloneable)
What about the memory model? Ideally it would work as if written like this:
public class Foo implements Freezable {
private volatile frozen; // set to true by Freezable.freeze()
void mutateFooContent(Runnable mutation) {
if (this.frozen)
throw new FrozenObjectException();
else
mutation.run();
}
}
But there could be a better trade-off of performance vs. semantics.
Other trade-offs...
· (-) All mutations to a Freezable would require a new 'frozen' check (* see below)
· (-) There would have to be a new bit allocated in the object header
· (+) Eliminate zillions of JDK defensive array copies (things like String.toCharArray())
· (+) JIT optimizations for constant-folding, etc.
· (+) GC optimizations
o (*) Put frozen objects into a read-only region of memory to eliminate mutation checks
o Optimize scanning of frozen references (since they never change)
I'm curious how other people think this idea would or wouldn't make sense for Java & what's been decided in the past.
Thanks,
-Archie
--
Archie L. Cobbs
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20231220/785fa372/attachment-0001.htm>
More information about the amber-dev
mailing list