As I had promised, albeit very late, I am back with
the Code Samples of my earlier article Java SE 9... What's New? You
may refer the earlier article to understand the newly introduced features at a
High Level. This article provides the Concluding Part of Code Samples for each of the Features. You may refer to the First Part of this Article at Java SE 9... What's New? [Code Samples - 01/02]
You may Download the Code Samples for the following New Features Here. (Import as Eclipse Project, Set Compiler/Environment as Java 9. Run the Main Class 'Java9Application' to see the Output/Outcome of the Code Samples.)
You may Download the Code Samples for the following New Features Here. (Import as Eclipse Project, Set Compiler/Environment as Java 9. Run the Main Class 'Java9Application' to see the Output/Outcome of the Code Samples.)
09. Stack-Walking API
Prior to Java 9, The way to access Stack Trace was very limited and provided the entire dump or stack information at once. This was inefficient and did not allow any direct way of filtering data. With Java 9, a Lazy StackWalker API has been introduced. This will allow to fetch data based on filtering conditions and is more efficient.
Although this has no external ramification to a developer in terms of syntax or semantics change - It may impact the way we design for memory and performance. The current UTF-16 representation uses 2 Bytes for Storage. Most of the string contain characters that are only Latin-1 in nature. The Latin-1 characters require only 1 Byte for Storage. With Java 9, String storage has been modified to start with an Additional Encoding Flag. This flag indicates whether it contains ISO-8859-1/Latin-1 characters or the UTF-16 characters. As per the official word, It has lead to an Improved Usage of Memory and Efficient GC, but with Some Loss in Performance at Peak Loads.
The Compact Strings are always enabled in Java 9, but it can be disabled by passing in the VM Argument +XX:-CompactStrings
It has to be noted that in Java 9, the implementation of java.lang.String decides at runtime, whether the storage size is to be 2 Bytes or 1 Byte, as per the actual String size (UTF-16 or Latin-1 Character).
11. Spin-Wait Hints
For multi-threading applications, this brings in some performance improvements under Busy-Waiting or Spin-Waiting conditions. Usually, Busy-Waiting is done for synchronization of some state of the object between two or more invokers - Waiting for a condition to occur before processing starts or continues. Thread.onSpinWait() has been introduced as a static method in the Thread class and can be optionally called in Busy-Waiting loops - This will allow the JVM to issue processor instructions on some system architectures to improve Reaction Time in such Spin-Wait Loops and also Reduce the Power Consumed by the Core Thread or Hardware thread. This benefits the Overall Power Consumption of a Program, and possibly Allowing other Cores or Hardware Threads to execute at Faster Speeds within the Same Power Consumption Envelope.
Prior to Java 9, The way to access Stack Trace was very limited and provided the entire dump or stack information at once. This was inefficient and did not allow any direct way of filtering data. With Java 9, a Lazy StackWalker API has been introduced. This will allow to fetch data based on filtering conditions and is more efficient.
package com.techilashots.java9.features;
import java.lang.StackWalker.Option;
import java.util.List;
import java.util.stream.Collectors;
/**
* Stack Walker API is a new feature of Java 9, aimed at Improving Performance of the predecessor Stack Track Element,
* as also for providing a way to filter the Stack Elements, in case of Exception or to Understand Application Behavior.
* Although, there have been multiple changes, I am covering only Stack Frame Attributes and also the walk() method
* for Walking the Stack Frame.
*/
public class StackWalkingService {
private int databaseService() {
int x = 3;
// Usage 01: Walking All Stack Frames
System.out.println("Java 9 Stack Walker API - Showing All Stack Frames");
StackWalker stackWalker = StackWalker.getInstance();
stackWalker.forEach(System.out::println);
System.out.println("");
// Usage 02 : Filtering or Walking Stack Frames
System.out.println("Java 9 Stack Walker API - Walking / Filtering Stack Frames");
List<StackWalker.StackFrame> stackFrames;
stackFrames = stackWalker.walk(frames -> frames.limit(2).collect(Collectors.toList()));
stackFrames.forEach(System.out::println);
System.out.println("");
// Usage 03 : Show All Attributes of a Stack Frame
System.out.println("Java 9 Stack Walker API - Show All Attributes in Stack Frame");
StackWalker newWalker = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);
stackFrames = newWalker.walk(frames -> frames.limit(1).collect(Collectors.toList()));
stackFrames.forEach(sfRecord->
{
System.out.printf("[Bytecode Index] %d%n", sfRecord.getByteCodeIndex());
System.out.printf("[Class Name] %s%n", sfRecord.getClassName());
System.out.printf("[Declaring Class] %s%n", sfRecord.getDeclaringClass());
System.out.printf("[File Name] %s%n", sfRecord.getFileName());
System.out.printf("[Method Name] %s%n", sfRecord.getFileName());
System.out.printf("[Is Native] %b%n", sfRecord.isNativeMethod());
System.out.printf("[Line Number] %d%n", sfRecord.getLineNumber());
});
return x;
}
private float persistenceService() {
float x = databaseService();
return x;
}
private double businessService() {
double x = persistenceService();
return x;
}
private double presentationService() {
long x = (long) businessService();
return x;
}
public void uiDisplay() {
System.out.println("Java 9 Stack Walker API for Debugging and Application Behavior");
double x = presentationService();
System.out.println("\n[Method to Display On User Interface]");
System.out.println("Old MacDonald had a Farm. In that Farm, He had " + x + " Cows!");
}
/**
* @param args
*/
public static void main(String[] args) {
StackWalkingService stackWalkingService = new StackWalkingService();
stackWalkingService.uiDisplay();
}
}
10. Compact Strings
The Compact Strings are always enabled in Java 9, but it can be disabled by passing in the VM Argument +XX:-CompactStrings
It has to be noted that in Java 9, the implementation of java.lang.String decides at runtime, whether the storage size is to be 2 Bytes or 1 Byte, as per the actual String size (UTF-16 or Latin-1 Character).
11. Spin-Wait Hints
For multi-threading applications, this brings in some performance improvements under Busy-Waiting or Spin-Waiting conditions. Usually, Busy-Waiting is done for synchronization of some state of the object between two or more invokers - Waiting for a condition to occur before processing starts or continues. Thread.onSpinWait() has been introduced as a static method in the Thread class and can be optionally called in Busy-Waiting loops - This will allow the JVM to issue processor instructions on some system architectures to improve Reaction Time in such Spin-Wait Loops and also Reduce the Power Consumed by the Core Thread or Hardware thread. This benefits the Overall Power Consumption of a Program, and possibly Allowing other Cores or Hardware Threads to execute at Faster Speeds within the Same Power Consumption Envelope.
package com.techilashots.java9.features;
import java.util.List;
import java.util.Vector;
/**
* For a Single Line Demonstration, I wrote the Non-Pure Threaded form of Producer-Consumer. Run the Code Multiple Times
* On a Machine, where you can Understand how the Temperature Changes and Extra Cooling Fan Kicks Off. Even though I did
* not do it myself, Take it up as an Experiment, by removing the Thread.onSpinWait() [Compare Before/After and also try
* with Data Set of 1 Million to 10 Million]
*
* Thread.onSpinWait() will Optimize Latency and Reduce/Optimize Power Consumption
*/
public class SpinWaitHints {
public static void main(String[] args) {
List<String> itemQueue = new Vector<String>();
Producer producer = new Producer(itemQueue);
Consumer consumer = new Consumer(itemQueue);
producer.start();
consumer.start();
}
}
class Producer extends Thread {
List<String> itemQueue;
Producer(List<String> itemQueue) {
this.itemQueue = itemQueue;
}
public void run() {
try {
produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void produce() throws InterruptedException {
while (true) {
while (itemQueue.size() < 100000) // produce 1 lac items
{
String item = "Sumith Puri " + (itemQueue.size());
itemQueue.add(item);
System.out.println("Item Produced: " + item);
}
while (itemQueue.size() > 0) {
// spin waiting - x86 architectures will now optimize
Thread.onSpinWait();
}
}
}
}
class Consumer extends Thread {
List<String> itemQueue;
public Consumer(List<String> itemQueue) {
this.itemQueue = itemQueue;
}
public void consume() throws InterruptedException {
while (true) {
while (itemQueue.size() < 100000) {
// spin waiting - x86 architectures will now optimize
Thread.onSpinWait();
}
int x = itemQueue.size();
while (x >= 1) {
x = x - 1;
if (x >= 0) {
String item = itemQueue.remove(x);
System.out.println("Item Consumed: " + item);
}
}
if (itemQueue.size() > 0)
itemQueue.remove(0);
}
}
public void run() {
try {
consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
12. New Version-String Scheme
From Java 9, $MAJOR.$MINOR.$SECURITY.$PATCH is the naming scheme for releases in Java. These details are also contained in the Runtime.Version Class.
package com.techilashots.java9.features;
/**
* $MAJOR.$MINOR.$SECURITY+$BUILD is the Naming Scheme for Version String in Java.
*/
public class JavaVersionStringChanges {
public void printVersionInformation() {
Runtime.Version versionInfo = Runtime.version();
System.out.println("Version String Changes in Java 9");
System.out.println("Major Version: " + versionInfo.major());
System.out.println("Minor Version: " + versionInfo.minor());
System.out.println("Security Version: " + versionInfo.security());
System.out.println("Build Version: " + versionInfo.build());
System.out.println("\nIn Java 9, Version Naming is Major.Minor.Security.Build");
System.out.println("Currently Running in Java Version: " + versionInfo.toString());
}
public static void main(String[] args) {
new JavaVersionStringChanges().printVersionInformation();
}
}
13. Enhanced Method Handles
A method handle is a typed, directly executable reference to an underlying method, constructor, field, or similar low-level operation, with optional transformations of arguments or return values. These transformations are quite general, and include such patterns as conversion, insertion, deletion, and substitution. In Java 9, Method Handles have been Enhanced to include static methods for creating different kind of Method Handles.
package com.techilashots.java9.features;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
/**
* MethodHandles were introduced first in Java 7. You have to think them as an alternative for Java Reflection API, but
* with an advantage of better performance as they are specified at creation time. Enhanced Method Handles has primarily
* added new static methods to better/widen the functionality provided by Method Handles.
*
* Note that Method Handles are Enhanced in Java 9, to introduce very many changes and methods. I will be covering the
* ones that are the most important, only to introduce this topic.
*
* arrayLength, arrayConstructor, zero, empty,
* loop, countedloop, iteratedloop, dowhileloop, whileloop, try/finally
*/
public class EnhancedMethodHandles {
public void enhancedMethodHandleDemo() {
try {
// arrayLenth
MethodHandle methodHandleLength = MethodHandles.arrayLength(int[].class);
int[] ageArray = new int[] { 21, 28, 36 };
int arrayLength;
arrayLength = (int) methodHandleLength.invoke(ageArray);
System.out.println("Length of Array using Method Handle is " + arrayLength);
// arrayConstructor
MethodHandle methodHandleConstructor = MethodHandles.arrayConstructor(int[].class);
int[] newAgeArray = (int[]) methodHandleConstructor.invoke(3);
System.out.println("Array Constructed using Method Handle of Size " + newAgeArray.length);
// zero
int x = (int) MethodHandles.zero(int.class).invoke();
System.out.println("Default Value of Primitive Integer using Method Handles is " + x);
String y = (String) MethodHandles.zero(String.class).invoke();
System.out.println("Default Value of String using Method Handles is " + y);
System.out.println();
System.out.println("Reader/Developer - Left as an Exercise for You :-)");
System.out.println("Refer Loop, CountedLoop, DoWhileLoop, WhileLoop, IteratedLoop, TryFinally");
} catch (Throwable e) {
System.out.println("Was Hungry as Ever - Gulped Everything I Got!");
}
// refer to, https://goo.gl/JCyo7N (official javadoc)
// also use, https://goo.gl/i8wNJ8 (individual blog)
}
public static void main(String[] args) {
new EnhancedMethodHandles().enhancedMethodHandleDemo();
}
}
14. Variable Handles
Java's Concurrent Package (java.util.concurrent.atomic) provide all Atomic Types for performing atomic operations. Apart from this, Unsafe Operations (sun.misc.unsafe) such as creating objects without calling the constructor used in Java Low-Level Programming require to be hidden from the outside world as per JEP 260: Encapsulate Most Internal APIs This has led to creation of a new abstract class type named VarHandle - This will allow a developer to assign different types to the same reference (dynamically typed reference). It can also take care of performing atomic operations on the held variable, including compare and swap (set or exchange) operations. It also provides memory fencing operations, to order the in-memory representation of the object, by providing finer grain control.
Java's Concurrent Package (java.util.concurrent.atomic) provide all Atomic Types for performing atomic operations. Apart from this, Unsafe Operations (sun.misc.unsafe) such as creating objects without calling the constructor used in Java Low-Level Programming require to be hidden from the outside world as per JEP 260: Encapsulate Most Internal APIs This has led to creation of a new abstract class type named VarHandle - This will allow a developer to assign different types to the same reference (dynamically typed reference). It can also take care of performing atomic operations on the held variable, including compare and swap (set or exchange) operations. It also provides memory fencing operations, to order the in-memory representation of the object, by providing finer grain control.
Firstly, You need to understand Memory Ordering Effects, as VarHandle are entirely based on the Understanding of Plain, Volatile, Opaque, Acquire/Release Memory Ordering Modes. You may refer to them at : https://www.baeldung.com/java-variable-handles (Under Memory Ordering Effects). You have to try and visualize this correctly, and then proceed to the Code Samples.
package com.techilashots.java9.features;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
/**
* VarHandle allows developers to assign different types to the same reference (dynamically typed reference).It can also
* take care of performing atomic operations on the held variable, including compare and swap (set/exchange) operations.
* It also provides memory fencing operations, to order the in-memory representation of the object, by providing finer
* grain control.
*
* I am providing an Example of the Read Operations using VarHandle. Please refer to the link provided below for further
* info on VarHandle on Public Variable, VarHandle for Private Variables, VarHandle for Array Types
*
* https://www.baeldung.com/java-variable-handles
*/
class VarHandleStore {
public int varIntHandle01 = 5;
public int varIntHandle02 = 9;
public byte varByteHandle03 = 21;
}
public class VariableHandles {
public void useVariableHandle() {
System.out.println("Java 9 Introduces.... Variable Handles!");
try {
VarHandleStore varHandleStore = new VarHandleStore();
VarHandle varHandle = MethodHandles.lookup().in(VarHandleStore.class).findVarHandle(VarHandleStore.class,
"varIntHandle01", int.class);
// value using get() in varhandle
int plainValue = (int) varHandle.get(varHandleStore);
System.out.println("Value using get() in VarHandle: " + plainValue);
// value is written to using set() - plain access
// you can also use set(), setOpaque(), setVolatile(), setRelease()
varHandle.set(varHandleStore, 21);
plainValue = (int) varHandle.get(varHandleStore);
System.out.println("Set Value using set(), then get() in VarHandle: " + plainValue);
// value is written to using getandadd()
int oldValue = (int) varHandle.getAndAdd(varHandleStore, 51);
plainValue = (int) varHandle.get(varHandleStore);
System.out.println("Using getAndAdd() in VarHandle, Old Value: " + oldValue + ", New Value: " + plainValue);
varHandle = MethodHandles.lookup().in(VarHandleStore.class).findVarHandle(VarHandleStore.class,
"varIntHandle02", int.class);
// please do try out the compareandset() - atomic updates
// have left this due to time constraints
// value is written to using getandbitwiseor()
varHandle = MethodHandles.lookup().in(VarHandleStore.class).findVarHandle(VarHandleStore.class,
"varByteHandle03", byte.class);
byte before = (byte) varHandle.getAndBitwiseOr(varHandleStore, (byte) 127);
byte after = (byte) varHandle.get(varHandleStore);
System.out.println("Get Byte Value, Then Or, using getAndBitwiseOr()");
System.out.println("Old Byte Value: " + before + "; New Byte Value: " + after);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new VariableHandles().useVariableHandle();
}
}
15. Filter Incoming Serialization Data
This feature is related to the addition of filters at the serialization incoming streams to improve security and robustness. The core mechanism is a filter interface implemented by serialization clients and set on an ObjectInputStream
The core mechanism is a Filter Interface implemented by serialization clients and set on an ObjectInputStream. The filter interface methods are called during the Deserialization Process to Validate the classes being Deserialized, The Sizes of Arrays being Created, The Stream Length, Graph Depth and Number of References as the Stream is being Decoded. A filter determines whether the arguments are ALLOWED or REJECTED and should return the appropriate status. If the filter cannot determine the status it should return UNDECIDED. Filters are designed for the specific use case and expected types. A filter designed for a particular use may be passed a class that is outside of the scope of the filter. If, for example, the purpose of the filter is to Black-List Classes then it can reject a candidate class that matches and report UNDECIDED for others.
This feature is related to the addition of filters at the serialization incoming streams to improve security and robustness. The core mechanism is a filter interface implemented by serialization clients and set on an ObjectInputStream
. The filter interface methods are called during the de-serialization process to validate the classes being de-serialized, the sizes of arrays being created, and metrics describing stream length, stream depth, and number of references as the stream is being decoded. The filter returns a status to accept, reject, or leave the status undecided. Some Helpful Information On this Feature can be Found at: https://goo.gl/bRezWt You have to remember the words Security, Vulnerabilities and Robustness as the main reasons behind creation of this Feature in Java. This will thwart possible Security Attacks like Denial Of Service.The core mechanism is a Filter Interface implemented by serialization clients and set on an ObjectInputStream. The filter interface methods are called during the Deserialization Process to Validate the classes being Deserialized, The Sizes of Arrays being Created, The Stream Length, Graph Depth and Number of References as the Stream is being Decoded. A filter determines whether the arguments are ALLOWED or REJECTED and should return the appropriate status. If the filter cannot determine the status it should return UNDECIDED. Filters are designed for the specific use case and expected types. A filter designed for a particular use may be passed a class that is outside of the scope of the filter. If, for example, the purpose of the filter is to Black-List Classes then it can reject a candidate class that matches and report UNDECIDED for others.
package com.techilashots.java9.features;
import java.io.ObjectInputFilter;
/**
* Demonstrates Java 9 Serialization/De-Serialization Filters for Incoming Data. Do Refer https://goo.gl/bRezWt for more
* on Filters, with more Details and Examples.
*/
class ItemCatalogFilter implements ObjectInputFilter {
private long maxStreamBytes = 400; // Maximum allowed bytes in the stream.
private long maxDepth = 2; // Maximum depth of the graph allowed.
private long maxReferences = 5; // Maximum number of references in a graph.
@Override
public Status checkInput(FilterInfo filterInfo) {
if (filterInfo.references() < 0 || filterInfo.depth() < 0 || filterInfo.streamBytes() < 0
|| filterInfo.references() > maxReferences || filterInfo.depth() > maxDepth
|| filterInfo.streamBytes() > maxStreamBytes) {
// reject this, as it seems malicious, incorrect or tampered with
return Status.REJECTED;
}
Class<?> clazz = filterInfo.serialClass();
if (clazz != null) {
if (CatalogCustomer.class == filterInfo.serialClass()) {
// we are expecting a customer of our product catalog
System.out.println("Incoming Serialization Data Verified for Structure and Vulnerabilities");
return Status.ALLOWED;
} else {
// seems like some tampered, unexpected or malicious data here
return Status.REJECTED;
}
}
// the status as undecided, when we cannot infer - or + for sure
// left for the developer to decide as per business/security process
return Status.UNDECIDED;
}
}
Check out the Eclipse Console Output provided Below from Running the 'Java9Application' (Refer Attached Code Samples). Please Refer to the Individual Classes, provided for Each of the Features to Understand the Features Better. Go Ahead - Add, Modify, Delete to Experiment with all of the New Java 9 Features. The Output for the Features Marked [01-08] is Shown in the Part 1 of the Article at
================================
09. Stack Walking API
---------------------
Java 9 Stack Walker API for Debugging and Application Behavior
Java 9 Stack Walker API - Showing All Stack Frames
com.techilashots.java9.features.StackWalkingService.databaseService(StackWalkingService.java:23)
com.techilashots.java9.features.StackWalkingService.persistenceService(StackWalkingService.java:57)
com.techilashots.java9.features.StackWalkingService.businessService(StackWalkingService.java:63)
com.techilashots.java9.features.StackWalkingService.presentationService(StackWalkingService.java:69)
com.techilashots.java9.features.StackWalkingService.uiDisplay(StackWalkingService.java:77)
com.techilashots.java9.main.Java9Application.stackWalkingAPI(Java9Application.java:321)
com.techilashots.java9.main.Java9Application.java9CoreLibraryChanges(Java9Application.java:106)
com.techilashots.java9.main.Java9Application.main(Java9Application.java:48)
Java 9 Stack Walker API - Walking / Filtering Stack Frames
com.techilashots.java9.features.StackWalkingService.databaseService(StackWalkingService.java:30)
com.techilashots.java9.features.StackWalkingService.persistenceService(StackWalkingService.java:57)
Java 9 Stack Walker API - Show All Attributes in Stack Frame
[Bytecode Index] 112
[Class Name] com.techilashots.java9.features.StackWalkingService
[Declaring Class] class com.techilashots.java9.features.StackWalkingService
[File Name] StackWalkingService.java
[Method Name] StackWalkingService.java
[Is Native] false
[Line Number] 38
[Method to Display On User Interface]
Old MacDonald had a Farm. In that Farm, He had 3.0 Cows!
================================
11. Spin Wait Hints
-------------------
Spin Wait Hints Makes Power Consumption Efficient
[Uncomment Method Call in Code, To Run (Non-Terminating) Demo]
================================
12. Java Version Naming
----------------------
Java 9 changes the way Java Version String Format
Version String Changes in Java 9
Major Version: 9
Minor Version: 0
Security Version: 4
Build Version: Optional[11]
In Java 9, Version Naming is Major.Minor.Security.Build
Currently Running in Java Version: 9.0.4+11
================================
13. Enhanced Method Handles
---------------------------
Java 9 has Enhanced Method Handles for Multitude of Operations
Length of Array using Method Handle is 3
Array Constructed using Method Handle of Size 3
Default Value of Primitive Integer using Method Handles is 0
Default Value of String using Method Handles is null
Reader/Developer - Left as an Exercise for You :-)
Refer Loop, CountedLoop, DoWhileLoop, WhileLoop, IteratedLoop, TryFinally
================================
14. Variable Handles
--------------------
Java 9 Introduces Variable Handles, Different Types for Same Reference
Java 9 Introduces.... Variable Handles!
Value using get() in VarHandle: 5
Set Value using set(), then get() in VarHandle: 21
Using getAndAdd() in VarHandle, Old Value: 21, New Value: 72
Get Byte Value, Then Or, using getAndBitwiseOr()
Old Byte Value: 21; New Byte Value: 127
================================
15. Filter Incoming Serialization Data
-------------------------------------
Java 9 Allows Filtering of Serialization Data, through Object Input Filters
Incoming Serialization Data Verified for Structure and Vulnerabilities
Customer Details String Follows (Filtered then De-Serialized)
skp:skp:4240123488889001:1.243223256E7
================================
_______________________________________________________________________________
16. More Concurrency Updates
[Will be Covered In a Separate Article, As there are Many Changes to Cover and Merits an Article on Its Own]
_________________________________________________________________________________
JEP 200: The Modular JDK