Σάββατο 4 Φεβρουαρίου 2023

Project Valhalla Value objects and user-defined primitives



PROJECT VALHALLA: VALUE OBJECTS AND USER-DEFINED PRIMITIVES




THE PROBLEM

In a project there are classes that don't have any use for identity and their field values can be permanently set once on instantiation, and thus their instances don't need any synchronization and other OOP related optimizations.

However, by default they act like identity components and at runtime, the support for identity can be expensive. Particular memory location is required for object's data and extra metadata to support the object's functionalities.


PROJECT VALHALLA  

An alternative approach is to represent program data using primitive data types. As primitive values do not have a unique identity, they can be easily copied and efficiently stored as compact bit sequences. At the same time, we want to retain object-oriented semantics.

That said, the purpose here is to "augment the Java object model with value objects and user-defined primitives, combining the abstractions of OOP with the performance characteristics of primitives".



VALUE CLASSES


Value classes will be the bridge between the OOP benefits and primitives' performance.

With Valhalla, the JVM will try inlining value objects where possible. These value objects will be encoded directly   with its primitive field values, avoiding any overhead from object headers, indirections, or heap allocation.

As a non-goal of the related JEP, the Value classes will be reference types, stored in memory and can  be pointed to by multiple variables.


Structure:

   value class Shape {

     int x;

     int y;

   }

  ,where "value" dictates that the class and its fields would be Final, just like the regular VO objects.Value classes contain only primitives.


Being Final, they are immutable, and thus, lack identity:

In Java, final classes are unable to be altered after they have been created, making them immutable. This means that the values stored within their fields cannot be modified, and any attempts to do so will result in an error. This is why final classes lack identity, as their values cannot be changed, and thus their identity cannot be changed.

Subsequently we can compare Value class instances only by comparing all their field values. 


To summarize:

Value classes provide programmers with a mechanism to opt out of object identity, and in return get many of the performance benefits of primitive types, without giving up the other features of Java classes.

They cannot mutate, synchronize, and lack identity.

Value objects significantly improve object performance in many contexts, providing a good fusion of the better abstractions of objects with the better performance of primitives.


Value Classes drawbacks:

Value classes being the middle ground between objects and primitives, they present some drawbacks when used as fields or in arrays:

  • A variable of a Value type could be null, so we require additional bits to encode null.
  • As reference types, Value objects' variables must be modified atomically, so it not practical to inline Value objects.
Specifically, in an array with Value objects, there's still indirections and references to all elements that need memory usage:



This can be improved using Primitive classes, where possible:



PRIMITIVE CLASSES


A primitive class is a special kind of value class that introduces a new composite primitive type. Using class features, we can create a new primitive type containing other primitives. Primitive objects will be stored in stack as a unit, thus supporting a null-free flattened storage, and improved performance.

Contrary to Value classes, Primitive ones are more performant for arrays, as they have no references.They are stored directly in stack memory.

Structure:


Note the "primitive" keyword.

Primitive classes also have class methods to perform any calculations inside the object, ensuring encapsulation like in DDD design.

Primitive classes will come handy in the case of e.g. arrays, where their advantage over Value classes is the lack of indirections. In addition no null encoding needs exist, and atomic modification in also not needed as primitives stored in stack:

A memory layout of a Primitive array:



Primitive members are flattened memory-wise, and no extra pointers are needed.



CLASSES FOR THE BASIC PRIMITIVES


Project Valhalla is an initiative which seeks to migrate the Wrapper classes (java.lang.Integer, java.lang.Double, etc.) to become primitive classes. This is done in order to reduce the overhead of modeling primitive values with classes and offer the capabilities of class types for the basic primitives, with many details delegated to the standard library. Starting from Java 5, primitive values can be stored in the Wrapper classes and represented as objects, although there are drawbacks such as runtime costs and the potential for boxing identical values leading to two objects being unequal. JEP 401 outlines the improvements that will be made with the implementation of Project Valhalla.


ENHANCED GENERICS


To maintain the performance benefits of using primitives, existing frameworks and libraries have resorted to creating specializations such as IntStream<T> or ToIntFunction<T>. Currently, to use language primitives generically, we use boxed types, like Integer for int or Float for float, which introduces an extra layer of indirection and negates the purpose of using primitives. Enhanced generics is an effort to eliminate this workaround and allow generic types to be applied to object references, primitives, value types, and possibly even void.


SOURCES:


Official doc:

https://openjdk.org/projects/valhalla/

https://en.wikipedia.org/wiki/Project_Valhalla_(Java_language)

https://openjdk.org/jeps/8277163

https://openjdk.org/jeps/401