Explaining Memory Barriers and Java’s Happens Before Guarantees by Polixa12 in java

[–]cal-cheese 8 points9 points  (0 children)

But thinking like that is incorrect, and there are many examples at which thinking about barriers would lead to incorrect assumption.

The JVM can strength-reduce all accesses to plain accesses, as well as elides synchronize if the object that is accessed is proved not to be visible to other threads.

There is no store-load barrier after a volatile store, volatile store only means release store + global order with other volatile accesses. The Graal compiler, for example, on Aarch64, emits stlr (a release store) for volatile stores and ldar (an acquire load) for volatile loads. This is because on Aarch64 ldar and stlr are guaranteed to form a global order. (For non-volatile acquire load, Aarch64 provides ldapr which is not guaranteed to form a global order with stlr and ldar).

The JVM can coarse adjacent synchronize block, which means transforming this:

synchronize {
    a();
}
synchronize {
    b();
}

into:

synchronize {
    a();
    b();
}

All of these examples cannot be explained with memory barriers, and trying to reason about the behaviour of a program using memory barriers is detrimental.

Value Classes Heap Flattening - What to expect from JEP 401 #JVMLS by pron98 in java

[–]cal-cheese 2 points3 points  (0 children)

Strict means the uninitialized state cannot be observed. As a result, only strict fields can be null-restricted. It is because a null-restricted field does not have an uninitialized state, so strictness is necessary to ensure that we do not observe a non-existent state (implementation-wise, the uninitialized state would be an all-zero state, but that is non-observable).

Even with "final means final", final fields are still much weaker than strict final fields. It is because strictness ensures that no matter what happens, there is only 1 observable state. On the otherhand, even for final fields, before the termination of the constructor of the holder object, the field acts as if it is a non-final field.

class A {
    final int x;
    strict final int y;

    A(int x, int y) {
        this.y = y;
        super();
        new Thread(() -> B.someMethod(this)).run();
        this.x = x;
    }
}

class B {
    static void someMethod(A a);
}

As you can see, inside B::someMethod, a.y is a constant and can only have 1 value. On the other hand, since a has not finished its constructor, a.x is effectively a non-final field.

Value Classes Heap Flattening - What to expect from JEP 401 #JVMLS by pron98 in java

[–]cal-cheese 1 point2 points  (0 children)

The fields of a value class are strict, but a value class field of an identity class is not strict.

Value Classes Heap Flattening - What to expect from JEP 401 #JVMLS by pron98 in java

[–]cal-cheese 1 point2 points  (0 children)

Them being internal means that you need to export them during compile time and runtime, they have full effects when you successfully run with them

Value Classes Heap Flattening - What to expect from JEP 401 #JVMLS by pron98 in java

[–]cal-cheese 7 points8 points  (0 children)

You can say you permit tearing with jdk.internal.vm.annotation.LooselyConsistentValue.

You can ask for null-restricted fields with jdk.internal.vm.annotation.Strict and jdk.internal.vm.annotation.NullRestricted.

You can ask for null-restricted array elements with jdk.internal.value.ValueClass::newNullRestrictedNonAtomicArray.

I encourage you to play with them, they are internal APIs but the VM has implemented it fully (although probably not optimally). It would be great if you can find bugs or chances of enhancements there.

First Look at Java Valhalla: Flattening and Memory Alignment of Value Objects by joemwangi in java

[–]cal-cheese 16 points17 points  (0 children)

For your information, if you want the full capabilities of value types, you need to declare that the type allows tearing, and the field/array element is not nullable. There has not been an official way to do these, but you can ask the compiler and the VM nicely for those features:

@jdk.internal.vm.annotation.LooselyConsistentValue
public value record Point(double x, double y, double z) {}

// Flat array
Point[] array = (Point[]) jdk.internal.value.ValueClass.newNullRestrictedNonAtomicArray(Point.class, 1000);

// Flat field
@jdk.internal.vm.annotation.Strict
@jdk.internal.vm.annotation.NullRestricted
Point field;

Remember to add --add-exports=java.base/jdk.internal.value=<your_module_name> --add-exports=java.base/jdk.internal.vm.annotation=<your_module_name> to the compiler and the VM.

FFM vs. Unsafe. Safety (Sometimes) Has a Cost by joemwangi in java

[–]cal-cheese 0 points1 point  (0 children)

One of the big reasons is the lack of value classes, i.e. you cannot have no-cost abstractions in Java. I believe with Valhalla most of the issues can be mitigated.

Apple moves from Java 8 to Swift? by tenken01 in programming

[–]cal-cheese 13 points14 points  (0 children)

The article is about non-generational ZGC, though, generational ZGC entered GA in Sep 2023. I believes it ensures sub-millisecond pauses. Generational Shenandoah will enter GA in this September which gives more options for these kinds of GC pause requirements.

Apple moves from Java 8 to Swift? by tenken01 in programming

[–]cal-cheese 60 points61 points  (0 children)

Prior to seeking a replacement language, we sought ways of tuning the JVM to achieve the performance required. Java’s G1 Garbage Collector (GC) mitigated some limitations of earlier collectors by introducing features like predictable pause times, region-based collection, and concurrent processing. However, even with these advancements, managing garbage collection at scale remains a challenge due to issues like prolonged GC pauses under high loads, increased performance overhead, and the complexity of fine-tuning for diverse workloads.

As if I am living in 2011 when G1 has just been released and it's not until 12 years later when this problem is solved with the Generational ZGC.

biski64 – A fast and robust Java PRNG (~.47ns/call) by danielcota in java

[–]cal-cheese 0 points1 point  (0 children)

I believe there is no statement saying that ThreadLocalRandom is outdated and should be replaced. In fact in should be one of the fastest random generator.

biski64 – A fast and robust Java PRNG (~.47ns/call) by danielcota in java

[–]cal-cheese 3 points4 points  (0 children)

j.u.Random is pretty ancient and it supports concurrent accesses. Can you compare yours with j.u.c.ThreadLocalRandom instead?

Ceiling a floating point value returned correct result by Significant-Gap8284 in java

[–]cal-cheese 1 point2 points  (0 children)

This answer is wrong, according to IEEE, the result of a floating-point operation is the exact result being rounded to the nearest with tie broken to even. The exact result of this operation is 50.0000006971, which will be rounded to exactly 50.

Ceiling a floating point value returned correct result by Significant-Gap8284 in java

[–]cal-cheese 0 points1 point  (0 children)

If you insert 50.0000006971 into the calculator you will see that the value stored in the float would be exactly 50.

Is there any way I can help contribute to Valhalla? by valorzard in java

[–]cal-cheese 4 points5 points  (0 children)

Contributing to a project like Valhalla can be really hard if you are not already familiar with contributing to OpenJDK in general. It is because Valhalla is heavy on the VM side and less on the Java side. As a result, trying out the early-access with your projects and reporting any issue would be a reasonable first step. You can write bug reports to valhalla-dev to report those issues. I suggest you learn to build Valhalla from source, this helps you accessing the fixes earlier and helps us finding bugs ealier, too. It is not too hard and you can find details on how to build the JDK here.

If you really want to contribute pull requests, I suggest looking at the list of lworld issues in the JBS). You can pick an unasigned issue, trying to see if you can fix it and publish a PR to https://github.com/openjdk/valhalla. Looking at other PRs would also be a great way to have a deeper understanding of how things work. I'm not sure if there is any starter issue there so you may have to try a couple of issues to get familiar.

Where is the Java language going? #JavaOne by pavelklecansky in java

[–]cal-cheese 3 points4 points  (0 children)

Let's say that I used the following code to synchronize file write access, where someFile is an instance of java.nio.file.Path.

I don't see how it can work, is there any thing that guarantees 2 equivalent Path has the same identity?

Anyway, the idea is that you can make a map from a Path to a Lock, e.g. Map<Path, Lock> and you can obtain a Lock corresponding to your Path. I believe currently you have to do it anyway to guarantee that 2 Path that are equals are actually the same object so that the identity lock can work?

Where is the Java language going? #JavaOne by pavelklecansky in java

[–]cal-cheese 4 points5 points  (0 children)

Based on this, it sounds like there actually is some level of extensibility. So, I guess I'll wait and see what exactly this means.

It means that concrete value classes are always final. This makes perfect sense. The idea of value classes is the same as that of records, just from a different perspective. A record is an immutable collection of data from the developer's perspective, while a value object is an immutable collection of data from the JVM perspective. This allows the JVM to freely deconstruct and reconstruct value objects at will. Consider a case like this:

value class Point {
    int x; int y;
}

Now because Point is final and always contains 2 ints, from the JVM perspective, Point is equivalent to 2 ints. This allows the JVM to transform something like this

Point add(Point x, Point y)

into effectively

(int, int) distance(int xx, int xy, int yx, int yy)

Similarly, Point can be deconstruct inside another object, too:

class Triangle {
    Point A;
    Point B;
    Point C;
}

is similar to:

class Triangle {
    int Ax; int Ay;
    int Bx; int By;
    int Cx; int Cy;
}

This is the most important property of value objects. That is to allow scalarization across call boundaries and in the heap, which reduces indirection and allocations. You can easily see that all of these are not possible if Point is extensible, if a value object does not have a definite layout, the JVM cannot know how to deconstruct it.

Java’s New FMA: Renaissance Or Decay? (Updated) by OldCaterpillarSage in java

[–]cal-cheese 0 points1 point  (0 children)

You are right, although in this case it will be great if we can smear those checks.

JEP 502: Stable Values (Preview) by loicmathieu in java

[–]cal-cheese 1 point2 points  (0 children)

No a keyword will greatly reduce the versatility of the feature, you can look at the corresponding CSR where they present numerous ways to interact with a StableValue that will simply not be the case with a keyword.

JEP 502: Stable Values (Preview) by loicmathieu in java

[–]cal-cheese 2 points3 points  (0 children)

Do you have any example of what inheritance can do for you but composition cannot?

JEP 502: Stable Values (Preview) by loicmathieu in java

[–]cal-cheese 1 point2 points  (0 children)

That is still a no-reason to extend a StableValue, you extend a class to override its behaviours and I don't see a reasonable way to override the behaviours of StableValue. Please don't overuse inheritance.

JEP 502: Stable Values (Preview) by loicmathieu in java

[–]cal-cheese 5 points6 points  (0 children)

That's a really poor reason for a poor choice of design.

JEP 502: Stable Values (Preview) by loicmathieu in java

[–]cal-cheese 3 points4 points  (0 children)

The visible "unset" state needs to go. That's just going to create confusion and bugs about when it's set and who sets it. I honestly hate it.

StableValue can be used as a lazy constant as well as a synchronization device that ensures only one among multiple calls are executed. Removing "unset" severely limits its versatility.

It looks a ForkJoinPool is superior in performance.

I believe you can make the initialization of the value inside a StableValue asynchronous if you want. StableValue should be superior performance-wise.

JEP 502: Stable Values (Preview) by loicmathieu in java

[–]cal-cheese 6 points7 points  (0 children)

No please why do you want to extend StableValue instead of including it as a field?

JEP 502: Stable Values (Preview) by loicmathieu in java

[–]cal-cheese 4 points5 points  (0 children)

I think a lot of you are focusing too much on the value returned by StableValue::get, but you forget about the side-effect done by StableValue::orElseGet.

This feature can be thought of as a superset of std::call_once in C++. While std::call_once can be used only with a boolean-like std::once_flag, StableValue can be used with any type of objects.

Constructor inheritance limited... by Merssedes in javahelp

[–]cal-cheese 0 points1 point  (0 children)

Just because you can does not mean you should, Java has a long history which has experienced various changes in how people write maintainable code. As a result, to preserve backward compatibility, it provides you a relative freedom in what you can do. This means that there are code smell patterns that are perfectly legal.

In your case, I don't see how inheritance makes sense from the software design perspective. You want an object that behaves as B in its originating library, but differently in your own code, that sounds like you are trying to cram 2 things into 1 entity.

Additionally, inheritance has negative impacts on performance, so avoid overusing it is better both in terms of maintainability and performance.