Q1. Object Packing
Ans. Object packing functionality has been added to minimize the wasted space between data types of different sizes. This is primarily a benefit in 64-bit environments, but offers a small advantage even in 32-bit VMs. Ex:
public class Button {
char shape; String label; int xposition; int yposition; char color; int joe; object mike; char armed; } |
This would waste space between: color and joe (three bytes to pad to an int boundary), joe and mike (four bytes on a 64-bit VM to pad to a pointer boundary). Now, the fields are reordered to look like this:
...
object mike; int joe; char color; char armed; ... |
In this example, no memory space is wasted.
Q2. JVM Architecture.
Ans. We can
basically distinguish memory areas that are available for all threads in a JVM
and those memory areas that are exclusively accessible from only one thread.
The two areas that are available from all threads are the Method Area and
the Heap.
The internal
representation of a class will have the following data areas:
1. Runtime Constant
Pool: Numeric constants of the class of types
int, long, float or double, String-constants and symbolic references to all
methods, attributes and types of this class.
2. Method Code: The implementation code of all methods of this
class including constructors etc.
3. Attributes: A list of all named attributes of this class.
4. Fields: Values of all fields of this class as references
to the Runtime Constant Pool.
Heap: Java heap manages instances of classes (objects)
and arrays at runtime. The JVM specification mandates a Garbage Collection
mechanism for reclaiming the memory of an object on the Java heap. The
heap is divided into two generations: Young Generation and Tenured
Generation. The method area is implemented as a separated part - Permanent Generation.
Method area/Perm: It can be part of the heap and will
be created at runtime. The size of the method area can be static or dynamic and
deosn't need a Garbage Collector. It is responsible for storing class information. The class loader will load the
bytecode of a class and will pass it to the JVM. The JVM will generate an
internal class representation of the bytecode and store it in the method area.
In addition to
the heap and method area (shared across all threads of JVM), every
thread has exclusive access to memory that is created for each thread:
1. PC
Register/Program Counter register: The register
points to the current JVM instruction of the method the thread is executing. If it is a native method, content of the
PC register is not defined.
2. JVM Stack: Each thread gets its own stack, on which so
called frames are pushed for each method the thread currently executed.
3. Native Methode
Stack: Native methods
get its own stack known as "C-Stack"
Q3. Oracle’s
HotSpot JVM memory architecure
Ans. JVM heap is the area of
memory used by the JVM (and specifically HotSpot) for dynamic memory
allocation.The heap is split up into "generations":
1. Young
generation: It stores
short-lived objects that are created and immediately garbage collected.
2. Old (Tenured) generation: Objects that
persist longer are stored here.
3. Permanent
generation (permgen): Ir is used for
class definitions and associated metadata. Memory for class (static) variables
declared in the class is also taken from the method area.
Originally there was no permanent
generation, and objects and classes were stored together in the same area. But
as class unloading occurs much more rarely than objects are collected, moving
class structures to a specific area allows significant performance
improvements.
Q4. HotSpot
features.
1. Handleless
Objects: In previous versions of the JVM,
such as the Classic VM, indirect handles are used to represent object
references. While this makes relocating objects easier during garbage
collection, it represents a significant performance bottleneck, because
accesses to the instance variables require
two levels of indirection.
In the Java HotSpot VM, no handles are used by Java code. Object references are implemented as direct pointers. This provides C-speed access to instance variables. When an object is relocated during memory reclamation, the garbage collector is responsible for finding and updating all references to the object in place.
In the Java HotSpot VM, no handles are used by Java code. Object references are implemented as direct pointers. This provides C-speed access to instance variables. When an object is relocated during memory reclamation, the garbage collector is responsible for finding and updating all references to the object in place.
2. Two-Word Object Headers: Java HotSpot VM uses a two machine-word object header, as opposed
to three words in the Classic VM. Since the average Java object size is small, it saves approximately
8% in heap size. The first header word
contains information such as the identity hash code and GC status information.
The second is a reference to the object's class. Only arrays have a third
header field, for the array size.
3. Reflective Data are represented as Objects: Classes, methods, and other internal reflective data
are represented directly as objects on the heap. This not only
simplifies the VM internal object model, but also allows classes to be
collected by the same garbage collector used for other Java objects.
4. Native Thread Support, Including Preemption and
Multiprocessing: Both Java methods and native methods share the same stack, allowing
fast calls between the C and Java programming languages. Fully preemptive Java threads are supported using the host OS's
thread scheduling mechanism.
A major advantage of using native OS threads and
scheduling is the ability to take advantage of native OS multiprocessing
support transparently. Because the Java HotSpot VM is designed to be
insensitive to race conditions caused by preemption and/or multiprocessing
while executing Java code, Java threads will automatically take advantage of whatever scheduling and processor
allocation policies the native OS provides.
5. Garbage
Collection: The generational nature of the Java
HotSpot VM's memory system provides the flexibility to use specific GC algorithms suited to the needs of a diverse set of applications.
Q5. HotSpot
Garbage collection techniques.
Ans. Java HotSpot
VM's memory system provides the flexibility to use specific GC algorithms to
serve different pause time and throughput requirements.
A major attraction of the Java is built-in automatic memory management, or garbage collection (GC). Java HotSpot GC is a fully accurate collector. In contrast, many other GC's are
conservative or partially accurate. While conservative garbage collection can
be attractive because it is very easy to add to a system without garbage
collection support, it has certain drawbacks. Various GC implementations are:
1. Generational Copying Collection: It takes advantage of the fact that vast majority of objects (often >95%) are very short lived and hence segregates newly created objects into an object nursery in a stack-line fashion.
1. Generational Copying Collection: It takes advantage of the fact that vast majority of objects (often >95%) are very short lived and hence segregates newly created objects into an object nursery in a stack-line fashion.
a. Due to nursery structure, GC merely involves updating a single pointer and performing a single check for nursery overflow.
b. By the time the nursery overflows, most of the
objects in the nursery are already dead, allowing GC to
simply move the few surviving objects elsewhere, and avoid doing any
reclamation work for dead objects in the nursery.
2. Parallel Young
Generation Collector: The single-threaded copying
collector could become a bottleneck to scaling in an application that is
otherwise paralleled to take advantage of multiple processors. Hence, Java
HotSpot VM offers an optional multithreaded collector for the young generation,
in which the tracing and copying of live objects is accomplished by
multiple threads working in parallel. The implementation has been
carefully tuned to balance the collection work between all available
processors. This reduces the pause times for collecting young space and
maximizes garbage collection throughput. The
parallel young generation collector is the default GC algorithm
used with the Server VM.
When moving objects, the parallel
collector tries to keep related objects together, resulting in improved memory
locality and cache utilization, and leading to improved mutator performance.
This is accomplished by copying objects in depth first order.
The parallel collector also uses
available memory more optimally. It does not need to keep a portion of the old
object space in reserve to guarantee space for copying all live objects.
Instead, it uses a novel technique to speculatively attempt to copy objects. If
old object space is scarce, this technique allows the collector to switch
smoothly to compacting the heap without the need for holding any space in
reserve.
3. Mark-Compact Old
Object Collector: Although the generational copying
collector collects most dead objects efficiently, longer-lived objects still
accumulate in the old object memory area. Occasionally, based on low-memory
conditions or programmatic requests, an old object garbage collection must be
performed. Java HotSpot VM by default uses a standard mark-compact collection algorithm,
which traverses the entire graph of live objects from its roots, then
sweeps through memory, compacting away the gaps left by dead objects. By
compacting gaps in the heap, rather than collecting them into a freelist, memory fragmentation is eliminated.
4. Mostly Concurrent Mark-Sweep Collector: It is used for applications that require large heaps. Collection pauses induced by the default old generation mark-compact collector can often cause disruptions, as application threads are paused for a period that is proportional to the size of the heap. The Java HotSpot VM has implemented an optional concurrent collector for the old object space that can take advantage of spare processor cycles (or spare processors) to collect large heaps while pausing the application threads for very short periods. This is accomplished by doing the bulk of the tracing and sweeping work while the application threads are executing. In some cases, there may be a small decline in peak application throughput as some processor cycles are devoted to concurrent collection activity.
5. Parallel Old Generation Collector: The current version of the Java HotSpot VM introduces a parallel mark-compact collector for the old generation designed to improve scalability for applications with large heaps. Where the concurrent mark-sweep collector focuses on decreasing pause times, the parallel old collector focuses on increasing throughput by using many threads simultaneously to collect the old generation during stop-the-world pauses.
6. Garbage First
Collector: G1 is the long
term replacement of the Concurrent Mark-Sweep Collector (CMS). As its name suggests,
G1 concentrates its collection and compaction activity first on the areas of
the heap that are likely to be full of reclaimable objects, thus improving its
efficiency.
4. Mostly Concurrent Mark-Sweep Collector: It is used for applications that require large heaps. Collection pauses induced by the default old generation mark-compact collector can often cause disruptions, as application threads are paused for a period that is proportional to the size of the heap. The Java HotSpot VM has implemented an optional concurrent collector for the old object space that can take advantage of spare processor cycles (or spare processors) to collect large heaps while pausing the application threads for very short periods. This is accomplished by doing the bulk of the tracing and sweeping work while the application threads are executing. In some cases, there may be a small decline in peak application throughput as some processor cycles are devoted to concurrent collection activity.
5. Parallel Old Generation Collector: The current version of the Java HotSpot VM introduces a parallel mark-compact collector for the old generation designed to improve scalability for applications with large heaps. Where the concurrent mark-sweep collector focuses on decreasing pause times, the parallel old collector focuses on increasing throughput by using many threads simultaneously to collect the old generation during stop-the-world pauses.
G1 collects at
the region level. If any region contains no live objects, it is immediately
reclaimed. The user can specify a goal for the pauses and G1 will do an
estimate of how many regions can be collected in that time based on previous
collections. G1 also provides some finer control, allowing a user to specify a fraction of
time during a period of execution to be spent on garbage collection. Ex: For every 250ms of execution, spend no more than 50ms on GC.
There are two
major differences between CMS and G1. The first is that G1 is a compacting
collector. G1 compacts sufficiently to completely avoid the use of fine-grain
free lists for allocation, which considerably simplifies parts of the collector
and mostly eliminates potential fragmentation issues. As well as compacting, G1
offers more predictable garbage collection pauses than the CMS collector and
allows users to set their desired pause targets.
Q6. Advanced features of HotSpot.
Q6. Advanced features of HotSpot.
Ans.
1. Ultra fast thread synchronization.
2. 64 bit architecture: Java HotSpot VM is now 64-bit safe, and server VM includes support for both 32-bit and 64-bit operations. Users can select either 32-bit or 64-bit operation by using commandline flags -d32 or -d64 respectively. Users of the Java Native Interface will need to recompile their code to run it on the 64-bit VM.
3. Object packing
4. HotSpot Detection: By avoiding compilation of infrequently executed code (most of the program), Java HotSpot compiler can devote more attention to the performance-critical parts of the program. This hot spot monitoring is continued dynamically as the program runs, so that it literally adapts its performance on the fly to the user's needs. A subtle but important benefit of this approach is that by delaying compilation until after the code has already been executed for a while (measured in machine time, not user time), information can be gathered on the way the code is used, and then utilized to perform more intelligent optimization. As well, the memory footprint is decreased.
5. Method Inlining: Once the Java HotSpot adaptive optimizer has gathered information during execution about program hot spots, it not only compiles the hot spot into native code, but also performs extensive method inlining on that code. It dramatically reduces the dynamic frequency of method invocations, which saves the time needed to perform those method invocations.
6. Dynamic Deoptimization: Inlining is based on a form of global analysis. Dynamic loading significantly complicates inlining, because it changes the global relationships in a program. A new class may contain new methods that need to be inlined in the appropriate places. Therefore, Java HotSpot VM must be able to dynamically deoptimize previously optimized hot spots (and then reoptimize, if necessary), even while executing code for the hot spot. Without this capability, general inlining cannot be safely performed on Java.
Both Java HotSpot Client and Server compilers fully support dynamic deoptimization. This enables both aggressive and optimistic optimization as well as other techniques such as full-speed debugging.
7. Scalability: The Java HotSpot VM has recently added automatic self-sizing and adaptation mechanisms called ergonomics. Currently, ergonomics manifests in two principal areas.
9. Reliability, Availability and Serviceability: Recent releases of the VM have set new records for reliability and availability for enterprise applications, based on execution of various large applications by Sun Microsystems, Inc.
1. Ultra fast thread synchronization.
2. 64 bit architecture: Java HotSpot VM is now 64-bit safe, and server VM includes support for both 32-bit and 64-bit operations. Users can select either 32-bit or 64-bit operation by using commandline flags -d32 or -d64 respectively. Users of the Java Native Interface will need to recompile their code to run it on the 64-bit VM.
3. Object packing
4. HotSpot Detection: By avoiding compilation of infrequently executed code (most of the program), Java HotSpot compiler can devote more attention to the performance-critical parts of the program. This hot spot monitoring is continued dynamically as the program runs, so that it literally adapts its performance on the fly to the user's needs. A subtle but important benefit of this approach is that by delaying compilation until after the code has already been executed for a while (measured in machine time, not user time), information can be gathered on the way the code is used, and then utilized to perform more intelligent optimization. As well, the memory footprint is decreased.
5. Method Inlining: Once the Java HotSpot adaptive optimizer has gathered information during execution about program hot spots, it not only compiles the hot spot into native code, but also performs extensive method inlining on that code. It dramatically reduces the dynamic frequency of method invocations, which saves the time needed to perform those method invocations.
6. Dynamic Deoptimization: Inlining is based on a form of global analysis. Dynamic loading significantly complicates inlining, because it changes the global relationships in a program. A new class may contain new methods that need to be inlined in the appropriate places. Therefore, Java HotSpot VM must be able to dynamically deoptimize previously optimized hot spots (and then reoptimize, if necessary), even while executing code for the hot spot. Without this capability, general inlining cannot be safely performed on Java.
Both Java HotSpot Client and Server compilers fully support dynamic deoptimization. This enables both aggressive and optimistic optimization as well as other techniques such as full-speed debugging.
7. Scalability: The Java HotSpot VM has recently added automatic self-sizing and adaptation mechanisms called ergonomics. Currently, ergonomics manifests in two principal areas.
● First, depending on the physical configuration of the machine (number of processors, available physical memory, etc), either the Client VM or Server VM will be automatically selected. In particular, the Server VM will be selected for machines with larger numbers of processors and larger amounts of RAM, and the size of the garbage-collected heap will be automatically selected for reasonable server-side applications on such hardware.
● Second, garbage collectors will self-tune to improve the throughput of the application and reduce pause times.
8.
Fast reflection: Java libraries now generate bytecode stubs for frequently used
reflective objects such as Methods and Constructors. This technique exposes the
reflective invocations to the HotSpot compilers, yielding much higher performance.
This provides significant speedups in reflection-intensive code, such as that
used in serialization, RMI, and CORBA code environments.9. Reliability, Availability and Serviceability: Recent releases of the VM have set new records for reliability and availability for enterprise applications, based on execution of various large applications by Sun Microsystems, Inc.
Q7. JVM Tools Interface (JVM TI)
Ans. JVM TI allows tools such as profilers, debuggers, and monitors to
observe and control the JVM. Included features are:
● Full-speed debugging: JVM utilizes dynamic
deoptimization technology to support debugging of applications at full speed.
In earlier JVM, in debugging mode, the application was run only in the
interpreter. Enabling compilers in debugging scenarios
improves performance tremendously, and in many situations it is possible to run
with debugging support always enabled for better serviceability of
applications. Additionally, the launch of a debugger can be triggered upon the
throwing of an exception.
● HotSwap support: Object-oriented architecture of
JVM enables advanced features such as on-the-fly class
redefinition, or "HotSwap". HotSwap enables a class to be updated during execution while under the control
of a debugger. It also allows profiling operations to be performed by
hotswapping in methods where profiling code has been inserted.
● JNI error checking: Command-line option, -Xcheck: jni, for performing additional JNI
checks is available. Specifically, JVM validates the parameters
passed to the JNI function as well as the runtime environment data before
processing the JNI request. Any invalid data encountered indicates a problem in
the native code, and the VM will terminate with a fatal error in such cases.
● Error reporting: If JVM detects a crash in
native code, such as JNI code written by the developer, or if JVM itself
crashes, it will print and log debug information about the crash. This error
message normally will include information such as the function name, library
name, source-file name, and line number where the error occurred. The result is
that developers can more easily and efficiently debug their applications. If an
error message indicates a problem in the JVM code itself, it allows a developer
to submit a more accurate and helpful bug report.
● Signal-chaining facility: Signal-chaining enables the Java
platform to better inter-operate with native code that installs its own signal
handlers. The facility works on both Solaris OE and Linux platforms. Prior to version 1.4, the
JVM would not allow application-installed signal handlers for
certain signals including, for example, SIGBUS, SIGSEGV, or SIGILL, since those
signal handlers could conflict with the signal handlers used internally by the
JVM.
Q8. What is full GC.
Ans.
The concurrent collector uses a single GC thread that runs simultaneously with the application threads with the goal of completing the collection of the tenured generation before it becomes full. In normal operation, the concurrent collector is able to do most of its work with the application threads still running, so only brief pauses are seen by the application threads. As a fall back, if the concurrent collector is unable to finish before the tenured generation fills up, the application is paused and the collection is completed with all the application threads stopped. Such collections with the application stopped are referred to as full collections and are a sign that some adjustments need to be made to the concurrent collection parameters.
Remedies for Full Collections:
● It means incremental collector may not be able to incrementally collect the tenured generation fast enough. Try decreasing the size of the young generation in order to increase the number of young generation collections.
● This might be because young generation guarantee cannot be met, then fragmentation may be the cause. Increase the size of the tenured generation to offset the fragmentation. The space in the larger generation may not be used but it will be available for the young generation guarantee.
Q9. Java 7 features
Ans. There are a number of features in Java 7 that will please developers. Things such as strings in switch statements, multi-catch exception handling, try-with-resource statements, the new File System API, extensions of the JVM, support for dynamically-typed languages, the fork and join framework for task parallelism, and a few others will certainly be embraced by the community.
1. Diamond Operator: You may have noted on many occasions your IDE complaining of types when working with Generics.
Ex: To declare a map of trades using Generics, we write the code as follows:
Map<String, List<Trade>> trades = new TreeMap<String, List<Trade>> ();
Here we must declare the types on both the sides, although the right-hand side seems a bit redundant. In Java 7, you use the <> symbol, which is called diamond operator. It's written like this:
Map<String, List<Trade>> trades = new TreeMap <> ();
Note: While not declaring the diamond operator is legal, as trades = new TreeMap (), it will make the compiler generate a couple of type-safety warnings.
2. Using strings in switch statements: Switch statements work either with primitive types or enumerated types. Java 7 introduced another type that we can use in Switch statements: the String type.
The switch statement uses String.equals method for comparison with case arguments; consequently, comparison of String objects in switch statements is case sensitive. (I believe it first calls hashcode() method and then equals() to check the equality, this is for performance improvement.)
3. Auto-close resources: Java 7 has introduced another cool feature to manage the resources automatically. It is simple in operation, too. All we have to do is declare the resources in the try as follows.
Ex:
try (FileOutputStream fos = new FileOutputStream("movies.txt");
DataOutputStream dos = new DataOutputStream(fos)) {
dos.writeUTF("Java 7 Block Buster");
}
catch (IOException e) {
// log the exception
}
Here we can work with multiple resources, each one separated by a semicolon (;) separator. We do not have to nullify or close the streams manually, as they are closed automatically once the control exists the try block. Behind the scenes, the resources that should be auto closed must implement java.lang.AutoCloseableinterface. To support this behaviour all closable classes will be retro-fitted to implement a Closableinterface.
Note that the close methods of resources are called in the opposite order of their creation. In a try-with-resources statement, any catch or finally block is run after the resources declared have been closed.
4. Numeric literals with underscores: Numerical literals are definitely eye strainers. I am sure you would start counting the zeroes like me if you've been given a number with, say, ten zeros. It's quite error prone and cumbersome to identify a literal if it's a million or a billion unless you count the places from right to left. Not anymore, Java 7 introduced underscores in identifying the places. For example, you can declare 1000 as shown below:
int thousand = 1_000;
int million = 1_000_000; // Declaring million
You can place underscores only between digits; you cannot place underscores in the following places:
● At the beginning or end of a number.
● Adjacent to a decimal point in a floating point literal.
● Prior to an F or L suffix.
● In positions where a string of digits is expected.
5. Binary Literals: Integral types (byte, short, int, and long) can also be expressed using the binary number system. To specify a binary literal, add the prefix 0b or 0B to the number. The following examples show binary literals:
// An 8-bit 'byte' value:
byte aByte = (byte)0b00100001;
// A 16-bit 'short' value:
short aShort = (short)0b1010000101000101;
// Some 32-bit 'int' values:
int anInt1 = 0b10100001010001011010000101000101;
int anInt2 = 0b101;
int anInt3 = 0B101; // The B can be upper or lower case.
6. Catching multiple exceptions: Two or more exceptions can be grouped in the same catch statement.
catch (FileNotFoundException | ParseException | ConfigurationException e)
{
System.err.println("Config file '" + fileName_ + "' is missing or malformed");
}
catch (IOException iox)
{
System.err.println("Error while processing file '" + fileName_ + "'");
}
Note: If a catch block handles more than one exception type, then the catch parameter is implicitly final. In this example, the catch parameter ex is final and therefore you cannot assign any values to it within the catch block.
7. Rethrowing Exceptions with more inclusive type checking: Java 7 compiler performs more precise analysis of rethrown exceptions than earlier releases of Java SE. This enables you to specify more specific exception types in the throws clause of a method declaration.
Consider the following example:
static class FirstException extends Exception { }
static class SecondException extends Exception { }
public void rethrowException(String exceptionName) throws Exception {
try {
if (exceptionName.equals("First")) {
throw new FirstException();
}
else {
throw new SecondException();
} }
catch (Exception e) {
throw e;
}
}
This examples's try block could throw either FirstException or SecondException. But when you rethrow exception in catch block, it throws Exception e. Since, exception parameter of catch clause is type "Exception" and catch block rethrows the exception parameter e, you can only specify the exception type "Exception" in the throws clause of method declaration.
However, in Java 7, you can specify the exception types FirstException and SecondException in the throws clause. Java 7 compiler can determine that the exception thrown by the statement must have come from the try block, and the only exceptions thrown by the try block can be FirstException and SecondException.
public void rethrowException(String exceptionName)
throws FirstException, SecondException {
- GC indicates that it was a minor collection (young generation).
- Full GC indicates that it was a major collection (tenured generation).
- In default GC, a generation is collected when it is full (i.e., when no further allocations can be done from that generation).
- An explicit request to GC does a full collection (both young generation and tenured generation). A full collection is always done with the application paused for the duration of the collection.
The concurrent collector uses a single GC thread that runs simultaneously with the application threads with the goal of completing the collection of the tenured generation before it becomes full. In normal operation, the concurrent collector is able to do most of its work with the application threads still running, so only brief pauses are seen by the application threads. As a fall back, if the concurrent collector is unable to finish before the tenured generation fills up, the application is paused and the collection is completed with all the application threads stopped. Such collections with the application stopped are referred to as full collections and are a sign that some adjustments need to be made to the concurrent collection parameters.
Remedies for Full Collections:
● It means incremental collector may not be able to incrementally collect the tenured generation fast enough. Try decreasing the size of the young generation in order to increase the number of young generation collections.
● This might be because young generation guarantee cannot be met, then fragmentation may be the cause. Increase the size of the tenured generation to offset the fragmentation. The space in the larger generation may not be used but it will be available for the young generation guarantee.
Q9. Java 7 features
Ans. There are a number of features in Java 7 that will please developers. Things such as strings in switch statements, multi-catch exception handling, try-with-resource statements, the new File System API, extensions of the JVM, support for dynamically-typed languages, the fork and join framework for task parallelism, and a few others will certainly be embraced by the community.
1. Diamond Operator: You may have noted on many occasions your IDE complaining of types when working with Generics.
Ex: To declare a map of trades using Generics, we write the code as follows:
Map<String, List<Trade>> trades = new TreeMap<String, List<Trade>> ();
Here we must declare the types on both the sides, although the right-hand side seems a bit redundant. In Java 7, you use the <> symbol, which is called diamond operator. It's written like this:
Map<String, List<Trade>> trades = new TreeMap <> ();
Note: While not declaring the diamond operator is legal, as trades = new TreeMap (), it will make the compiler generate a couple of type-safety warnings.
2. Using strings in switch statements: Switch statements work either with primitive types or enumerated types. Java 7 introduced another type that we can use in Switch statements: the String type.
The switch statement uses String.equals method for comparison with case arguments; consequently, comparison of String objects in switch statements is case sensitive. (I believe it first calls hashcode() method and then equals() to check the equality, this is for performance improvement.)
3. Auto-close resources: Java 7 has introduced another cool feature to manage the resources automatically. It is simple in operation, too. All we have to do is declare the resources in the try as follows.
Ex:
try (FileOutputStream fos = new FileOutputStream("movies.txt");
DataOutputStream dos = new DataOutputStream(fos)) {
dos.writeUTF("Java 7 Block Buster");
}
catch (IOException e) {
// log the exception
}
Here we can work with multiple resources, each one separated by a semicolon (;) separator. We do not have to nullify or close the streams manually, as they are closed automatically once the control exists the try block. Behind the scenes, the resources that should be auto closed must implement java.lang.AutoCloseableinterface. To support this behaviour all closable classes will be retro-fitted to implement a Closableinterface.
Note that the close methods of resources are called in the opposite order of their creation. In a try-with-resources statement, any catch or finally block is run after the resources declared have been closed.
4. Numeric literals with underscores: Numerical literals are definitely eye strainers. I am sure you would start counting the zeroes like me if you've been given a number with, say, ten zeros. It's quite error prone and cumbersome to identify a literal if it's a million or a billion unless you count the places from right to left. Not anymore, Java 7 introduced underscores in identifying the places. For example, you can declare 1000 as shown below:
int thousand = 1_000;
int million = 1_000_000; // Declaring million
You can place underscores only between digits; you cannot place underscores in the following places:
● At the beginning or end of a number.
● Adjacent to a decimal point in a floating point literal.
● Prior to an F or L suffix.
● In positions where a string of digits is expected.
5. Binary Literals: Integral types (byte, short, int, and long) can also be expressed using the binary number system. To specify a binary literal, add the prefix 0b or 0B to the number. The following examples show binary literals:
// An 8-bit 'byte' value:
byte aByte = (byte)0b00100001;
// A 16-bit 'short' value:
short aShort = (short)0b1010000101000101;
// Some 32-bit 'int' values:
int anInt1 = 0b10100001010001011010000101000101;
int anInt2 = 0b101;
int anInt3 = 0B101; // The B can be upper or lower case.
6. Catching multiple exceptions: Two or more exceptions can be grouped in the same catch statement.
catch (FileNotFoundException | ParseException | ConfigurationException e)
{
System.err.println("Config file '" + fileName_ + "' is missing or malformed");
}
catch (IOException iox)
{
System.err.println("Error while processing file '" + fileName_ + "'");
}
Note: If a catch block handles more than one exception type, then the catch parameter is implicitly final. In this example, the catch parameter ex is final and therefore you cannot assign any values to it within the catch block.
7. Rethrowing Exceptions with more inclusive type checking: Java 7 compiler performs more precise analysis of rethrown exceptions than earlier releases of Java SE. This enables you to specify more specific exception types in the throws clause of a method declaration.
Consider the following example:
static class FirstException extends Exception { }
static class SecondException extends Exception { }
public void rethrowException(String exceptionName) throws Exception {
try {
if (exceptionName.equals("First")) {
throw new FirstException();
}
else {
throw new SecondException();
} }
catch (Exception e) {
throw e;
}
}
This examples's try block could throw either FirstException or SecondException. But when you rethrow exception in catch block, it throws Exception e. Since, exception parameter of catch clause is type "Exception" and catch block rethrows the exception parameter e, you can only specify the exception type "Exception" in the throws clause of method declaration.
However, in Java 7, you can specify the exception types FirstException and SecondException in the throws clause. Java 7 compiler can determine that the exception thrown by the statement must have come from the try block, and the only exceptions thrown by the try block can be FirstException and SecondException.
public void rethrowException(String exceptionName)
throws FirstException, SecondException {
try {
// ...
}
catch (Exception e) {
throw e;
}
}
}
However, if the catch parameter is assigned to another value, you must specify the exception type Exception in throws clause of the method declaration.
8. NIO 2.0: NIO 2.0 has come forward with many enhancements. It has introduced new classes to ease the life of a developer when working with multiple file systems. A new java.nio.file package consists of classes and interfaces such as Path, Paths, FileSystem, FileSystems and others. A Path is simply a reference to a file path. It is the equivalent (and with more features) to java.io.File. The following snippet shows how to obtain a path reference to the "temp" folder:
Path path = Paths.get("c:\\Temp\\temp");
System.out.println("Number of Nodes:" + path.getNameCount());
Path path = Paths.get("c:\\Temp\\temp");
System.out.println("Number of Nodes:" + path.getNameCount());
// Output is 2
System.out.println("File Name:" + path.getFileName());
System.out.println("File Name:" + path.getFileName());
// Output is temp.txt
Deleting a file or directory is as simple as invoking a delete method on Files (note the plural) class. The Files class exposes two delete methods, one that throws NoSuchFileException i.e. method Files.delete(path) and the other that does not i.e. method Files.deleteIfExists(path). You can use other utility methods such as Files.copy(..) and Files.move(..)etc.
Deleting a file or directory is as simple as invoking a delete method on Files (note the plural) class. The Files class exposes two delete methods, one that throws NoSuchFileException i.e. method Files.delete(path) and the other that does not i.e. method Files.deleteIfExists(path). You can use other utility methods such as Files.copy(..) and Files.move(..)etc.
9. File change notifications: WatchService API lets you receive notification events upon changes to the subject (directory or file). Steps are given below:1. Creating a WatchService object:
WatchService watchService = FileSystems.getDefault().newWatchService();
2. Obtain a path reference to your watchable directory.
Path path = Paths.get("C:\\Temp\\temp\\");
3. The next step is to register the directory with the WatchService for all types of events. These are java.nio.file.StandardWatchEventKinds event types
path.register(
Path path = Paths.get("C:\\Temp\\temp\\");
3. The next step is to register the directory with the WatchService for all types of events. These are java.nio.file.StandardWatchEventKinds event types
path.register(
watchService, ENTRY_CREATE, ENTRY_MODIFY,ENTRY_DELETE);
4. Initiate the infinite loop and start taking the events:
5. Run through the events on the key:
4. Initiate the infinite loop and start taking the events:
5. Run through the events on the key:
while(true) {
WatchKey key = watchService.take(); // this would return you keys
WatchKey key = watchService.take(); // this would return you keys
for (WatchEvent<?> event : key.pollEvents()) {
Kind<?> kind = event.kind();
System.out.println(
"Event on " + event.context().toString() + " is " + kind);
}
}
For ex, if you modify or delete the temp directory, you would see below statement:
Event on temp is ENTRY_MODIFY
Event on temp is ENTRY_DELETE
}
For ex, if you modify or delete the temp directory, you would see below statement:
Event on temp is ENTRY_MODIFY
Event on temp is ENTRY_DELETE
10. Fork and Join: The core classes supporting the Fork-Join mechanism are ForkJoinPool and ForkJoinTask. TheForkJoinPool is basically a specialized implementation of ExecutorService implementing the work-stealing algorithm (idle threads taking task from busy threads). We create an instance of ForkJoinPool by providing the target parallelism level (numberOfProcessors here). Now, provide this ForkJoinTask to the Executor by calling invoke method on the ForkJoinPool.
numberOfProcessors = Runtime.getRunTime().availableProcessors();
ForkJoinPool pool = new ForkJoinPool(numberOfProcessors);
pool.invoke(task); // task is ForkJoinTask
The problem that needs to be solved is coded in a ForkJoinTask. However, there are two implementations of this class: RecursiveAction and RecursiveTask. The only difference is that the former does not return a value while the latter returns an object of specified type.
public class MyBigProblemTask extends RecursiveAction {
The problem that needs to be solved is coded in a ForkJoinTask. However, there are two implementations of this class: RecursiveAction and RecursiveTask. The only difference is that the former does not return a value while the latter returns an object of specified type.
public class MyBigProblemTask extends RecursiveAction {
@Override
protected void compute() {
. . . // your problem invocation goes here
}
}
You have to override the compute method where in you need to provide the computing functionality.
11. Dynamism/JVM support for non-Java languages: Java is statically typed language - type checking of the variables, methods and return values is performed at compile time. JVM executes this strongly-typed bytecode at runtime without having to worry about finding the type information. With this, there were a lot of problems with using dynamic languages like Ruby in Java. Hence, there is an increasing pressure on Java folks to improvise running the dynamic languages efficiently. Although it is possible to run these languages on a JVM (using Reflection), it's not without constraints and restrictions. In Java 7, a new feature called invokedynamic was introduced. This makes VM changes to incorporate non-Java language requirements. A new package, java.lang.invoke, consisting of classes such as MethodHandle, CallSite and others has been created to extend the support of dynamic languages.
Q10. Features POSTPONED from Java 7
Ans.
1. Null Safe Method Invocation: Checking for nulls each and every time is very difficult and cumbersome task.
public String getPostcode (Person person) {
if (person != null) {
Address add = person.getAddress();
if (add != null) {
return add.getPostcode();
}
}
}
Above code can now be replaced as:
person?.getAddress()?.getPostcode();
2. Bracket Notation for Collections: This feature refers to the ability to reference a particular item in a Collection with square brackets similar to how Arrays are accessed.
Collection<String> c = new ArrayList<String>();
c.add(“one”);
c.add(“two”);
c.add(“three”);
Above code can now be replaced as:
Collection<String> c = new ArrayList { “one”, “two”, “three”};
3. Indexing syntax for lists and maps: This should allow us to use list[0]/map[key] instead of list.get(0)/map.get(key).
4. Improved JDBC: JDBC 4.1 introduces the following features:
● The ability to use a try-with-resources statement to automatically close resources of typeConnection, ResultSet, and Statement
● RowSet 1.1: The introduction of the RowSetFactoryinterface and theRowSetProvider class, which enable you to create all types of row sets supported by your JDBC driver.
Q11. Heap Pollution.
Q10. Features POSTPONED from Java 7
Ans.
1. Null Safe Method Invocation: Checking for nulls each and every time is very difficult and cumbersome task.
public String getPostcode (Person person) {
if (person != null) {
Address add = person.getAddress();
if (add != null) {
return add.getPostcode();
}
}
}
Above code can now be replaced as:
person?.getAddress()?.getPostcode();
2. Bracket Notation for Collections: This feature refers to the ability to reference a particular item in a Collection with square brackets similar to how Arrays are accessed.
Collection<String> c = new ArrayList<String>();
c.add(“one”);
c.add(“two”);
c.add(“three”);
Above code can now be replaced as:
Collection<String> c = new ArrayList { “one”, “two”, “three”};
3. Indexing syntax for lists and maps: This should allow us to use list[0]/map[key] instead of list.get(0)/map.get(key).
4. Improved JDBC: JDBC 4.1 introduces the following features:
● The ability to use a try-with-resources statement to automatically close resources of typeConnection, ResultSet, and Statement
● RowSet 1.1: The introduction of the RowSetFactoryinterface and theRowSetProvider class, which enable you to create all types of row sets supported by your JDBC driver.
Q11. Heap Pollution.
Ans. Most parameterized types, such as ArrayList<Number> and List<String>, are non-reifiable types. A non-reifiable type is a type that is not completely available at runtime. At compile time, non-reifiable types undergo a process called type erasure during which compiler removes information related to type parameters and type arguments. This ensures binary compatibility with Java libraries and applications that were created before generics.
Heap pollution occurs when a variable of a parameterized type refers to an object that is not of that parameterized type. This situation can only occur if program performed some operation that would give rise to an unchecked warning at compile-time. An unchecked warning is generated if, either at compile-time or at runtime, the correctness of an operation involving a parameterized type can’t be verified. Consider the following example:
List l = new ArrayList<Number>();
List<String> ls = l; // unchecked warning
l.add(0, new Integer(42)); // another unchecked warning
String s = ls.get(0); // ClassCastException is thrown
During type erasure, ArrayList<Number> and List<String> become ArrayList and List, respectively. The variable ls has the parameterized type List<String>. When the List referenced by l is assigned to ls, the compiler generates an unchecked warning. Compiler is unable to determine at compile time and knows that the JVM will not be able to determine at runtime, if "l" refers to a List<String> type or not. Since, it doesn't; heap pollution occurs.
As a result, at compile time, compiler generates another unchecked warning at the add statement. The compiler is unable to determine if the variable l refers to a List<String> type or a List<Integer> type (and another heap pollution situation occurs). However, the compiler does not generate a warning or error at the get statement. This statement is valid; it is calling the List<String>.get method to retrieve a String object. Instead, at runtime, the get statement throws a ClassCastException.
In detail, a heap pollution situation occurs when the List object l, whose static type is List<Number>, is assigned to another List object, ls, that has a different static type, List<String>. However, the compiler still allows this assignment. It must allow this assignment to preserve backwards compatibility with previous versions that do not support generics. Because of type erasure, List<Number> and List<String> both become List. Consequently, the compiler allows the assignment of the object l, which has a raw type of List, to the object ls.
Variable Arguments Methods and Non-Reifiable Formal Parameters
Consider the method ArrayBuilder.addToList in the following example. It is a variable arguments (also known as varargs) method that adds the objects of type T contained in the elements varargs formal parameter to the List listArg:
Heap pollution occurs when a variable of a parameterized type refers to an object that is not of that parameterized type. This situation can only occur if program performed some operation that would give rise to an unchecked warning at compile-time. An unchecked warning is generated if, either at compile-time or at runtime, the correctness of an operation involving a parameterized type can’t be verified. Consider the following example:
List l = new ArrayList<Number>();
List<String> ls = l; // unchecked warning
l.add(0, new Integer(42)); // another unchecked warning
String s = ls.get(0); // ClassCastException is thrown
During type erasure, ArrayList<Number> and List<String> become ArrayList and List, respectively. The variable ls has the parameterized type List<String>. When the List referenced by l is assigned to ls, the compiler generates an unchecked warning. Compiler is unable to determine at compile time and knows that the JVM will not be able to determine at runtime, if "l" refers to a List<String> type or not. Since, it doesn't; heap pollution occurs.
As a result, at compile time, compiler generates another unchecked warning at the add statement. The compiler is unable to determine if the variable l refers to a List<String> type or a List<Integer> type (and another heap pollution situation occurs). However, the compiler does not generate a warning or error at the get statement. This statement is valid; it is calling the List<String>.get method to retrieve a String object. Instead, at runtime, the get statement throws a ClassCastException.
In detail, a heap pollution situation occurs when the List object l, whose static type is List<Number>, is assigned to another List object, ls, that has a different static type, List<String>. However, the compiler still allows this assignment. It must allow this assignment to preserve backwards compatibility with previous versions that do not support generics. Because of type erasure, List<Number> and List<String> both become List. Consequently, the compiler allows the assignment of the object l, which has a raw type of List, to the object ls.
Variable Arguments Methods and Non-Reifiable Formal Parameters
Consider the method ArrayBuilder.addToList in the following example. It is a variable arguments (also known as varargs) method that adds the objects of type T contained in the elements varargs formal parameter to the List listArg:
public static <T> void addToList (List<T> listArg, T... elements) {
for (T x : elements) {
listArg.add(x);
}
}
ArrayBuilder.addToList(stringListA, "Seven", "Eight", "Nine");
ArrayBuilder.addToList(stringListA, "Ten", "Eleven", "Twelve");
List<List<String>> listOfStringLists = new ArrayList<List<String>>();
ArrayBuilder.addToList(listOfStringLists, stringListA, stringListB);
Java 7 compiler generates the following warning for definition of the method:
warning: [varargs] Possible heap pollution from parameterized vararg type T
When the compiler encounters a varargs method, it translates the varargs formal parameter into an array. However, the Java doesn't permit the creation of arrays of parameterized types. In the method ArrayBuilder.addToList, the compiler translates the varargs formal parameter T... elements to the formal parameter T[] elements, an array. However, because of type erasure, the compiler converts the varargs formal parameter to Object[] elements. Consequently, there is a possibility of heap pollution.
Note: The Java 5 and 6 compilers generate this warning when the ArrayBuilder.addToList is called. However, the Java 7 generates the warning at both the declaration site and the call site (unless the warnings are preempted with annotations).
Suppressing Warnings from Varargs Methods with Non-Reifiable Formal Parameters
If you declare a varargs method that has parameterized parameters, and you ensure that the body of the method does not throw a ClassCastException or other similar exception due to improper handling of the varargs formal parameter, you can suppress the warning by using one of the following options:
1. @SafeVarargs: Add @SafeVarargs annotation to static and non-constructor method declarations. It is a documented part of the method's contract; this annotation asserts that the implementation of the method will not improperly handle the varargs formal parameter.
@SafeVarargs
public static <T> void addToList2 (List<T> listArg, T... elements) {
for (T x : elements) {
listArg.add(x);
}
}
2. @SuppressWarnings: Add the following annotation to the method declaration:
@SuppressWarnings({"unchecked", "varargs"})
public static <T> void addToList3 (List<T> listArg, T... elements) {
for (T x : elements) {
listArg.add(x);
}
}
Unlike the @SafeVarargs annotation, the @SuppressWarnings("varargs") does not suppress warnings generated from the method's call site. You need to use the compiler option -Xlint:varargs.
public class HeapPollutionExample {
public static void main(String[] args) {
ArrayBuilder.addToList(listOfStringLists, stringListA, stringListB);
ArrayBuilder.addToList2(listOfStringLists, stringListA, stringListB);
ArrayBuilder.addToList3(listOfStringLists, stringListA, stringListB);
}
}
Java compiler generates the following warnings for this example:
addToList:
1. At method's declaration: [unchecked] Possible heap pollution from parameterized vararg type T
2. At method's call: [unchecked] unchecked generic array creation for varargs parameter of type List<String>[]
addToList2: No warnings are generated either at the method's declaration or when it is called.
addToList3:
Only at method call (no warning is generated at the method's declaration): [unchecked] unchecked generic array creation for varargs parameter of type List<String>[]
Note: In Java 5 and 6, it is the responsibility of the programmer who calls a varargs method that has a non-reifiable varargs formal parameter to determine whether heap pollution would occur. However, if this programmer did not write such a method, he or she cannot easily determine this. In Java 7, it is the responsibility of the programmer who writes these kinds of varargs methods to ensure that they properly handle the varargs formal parameter and ensure heap pollution does not occur.
Q12. Multithreaded Custom Class Loaders in Java7
Ans. Java7 release contains an important enhancement for multithreaded custom class loaders. In previous releases, certain types of custom class loaders were prone to deadlock. The Java 7 release modifies the locking mechanism to avoid deadlock.
Background
The function of a ClassLoader is to locate the bytecode for a particular class, then transform that bytecode into a usable class in the runtime system. The runtime system provides class loaders that can locate bootstrap classes, extension classes, and user classes. The CLASSPATH environment variable is one way to indicate to the runtime system where the bytecode is located.
Custom class loaders will not run into deadlocks if they adhere to an acyclic class loader delegation model. Acyclic delegation is envision of architects ClassLoader. In this model, every class loader has a parent (delegate). When a class is requested, the class loader first checks if the class was loaded previously. If the class is not found, the class loader asks its parent to locate the class. If the parent cannot find the class, the class loader attempts to locate the class itself.
Deadlock Scenario
In earlier releases of the Java platform, multithreaded custom class loaders could deadlock when they did not have an acyclic delegation model. Here is one example:
class A extends B
class C extends D
ClassLoader Delegation Hierarchy:
Custom Classloader CL1:
directly loads class A
delegates to custom ClassLoader CL2 for class B
Custom Classloader CL2:
directly loads class C
delegates to custom ClassLoader CL1 for class D
Thread 1:
Use CL1 to load class A (locks CL1)
defineClass A triggers
loadClass B (try to lock CL2)
Thread 2: Use CL2 to load class C (locks CL2)
defineClass C triggers
loadClass D (try to lock CL1)
Synchronization in the ClassLoader class was previously not sufficiently granular. A request to load a class synchronized on the entire ClassLoader object makes it prone to deadlock.
Class Loader Synchronization in the Java 7 Release
Java 7 release includes the concept of a parallel capable class loader. Loading a class by a parallel capable class loader now synchronizes on the pair consisting of the class loader and the class name. In the previous scenario, using the Java7 release, the threads are no longer deadlocked, and all classes are loaded successfully:
Thread 1:
Use CL1 to load class A (locks CL1+A)
defineClass A triggers
loadClass B (locks CL2+B)
Thread 2:
Use CL2 to load class C (locks CL2+C)
defineClass C triggers
loadClass D (locks CL1+D)
Custom class loaders that do not have a history of deadlocks do not require any changes. In particular, you do not need to change custom class loaders that follow the recommended acyclic hierarchical delegation model, that is, delegating first to their parent class. For backward compatibility, the Java7 release continues to lock a class loader object unless it registers as parallel capable.
To create new custom class loaders, the process is similar in Java 7 as in previous releases. Create a subclass of ClassLoader, then override the findClass() method and possibly loadClass(). Overriding loadClass() makes your life more difficult, but it is the only way to use a different delegation model.
If you have a custom class loader with a risk of deadlocking, with the Java 7 release, you can avoid deadlocks by following these rules:
1. Ensure that your custom class loader is multithread safe for concurrent class loading.
2. In your custom class loader's static initializer, invoke ClassLoader's static method registerAsParallelCapable(). This registration indicates that all instances of your custom class loader are multithread safe.
3. Check that all class loader classes that this custom class loader extends also invoke the registerAsParallelCapable() method in their class initializers. Ensure that they are multithread safe for concurrent class loading.
If your custom class loader overrides only findClass(String), you do not need further changes. This is the recommended mechanism to create a custom class loader. If your custom class loader overrides either the protected loadClass(String, boolean) method or the public loadClass(String) method, you must also ensure that the protected defineClass() method is called only once for each class loader and class name pair.
1. @SafeVarargs: Add @SafeVarargs annotation to static and non-constructor method declarations. It is a documented part of the method's contract; this annotation asserts that the implementation of the method will not improperly handle the varargs formal parameter.
@SafeVarargs
public static <T> void addToList2 (List<T> listArg, T... elements) {
for (T x : elements) {
listArg.add(x);
}
}
2. @SuppressWarnings: Add the following annotation to the method declaration:
@SuppressWarnings({"unchecked", "varargs"})
public static <T> void addToList3 (List<T> listArg, T... elements) {
for (T x : elements) {
listArg.add(x);
}
}
Unlike the @SafeVarargs annotation, the @SuppressWarnings("varargs") does not suppress warnings generated from the method's call site. You need to use the compiler option -Xlint:varargs.
public class HeapPollutionExample {
public static void main(String[] args) {
ArrayBuilder.addToList(listOfStringLists, stringListA, stringListB);
ArrayBuilder.addToList2(listOfStringLists, stringListA, stringListB);
ArrayBuilder.addToList3(listOfStringLists, stringListA, stringListB);
}
}
Java compiler generates the following warnings for this example:
addToList:
1. At method's declaration: [unchecked] Possible heap pollution from parameterized vararg type T
2. At method's call: [unchecked] unchecked generic array creation for varargs parameter of type List<String>[]
addToList3:
Only at method call (no warning is generated at the method's declaration): [unchecked] unchecked generic array creation for varargs parameter of type List<String>[]
Note: In Java 5 and 6, it is the responsibility of the programmer who calls a varargs method that has a non-reifiable varargs formal parameter to determine whether heap pollution would occur. However, if this programmer did not write such a method, he or she cannot easily determine this. In Java 7, it is the responsibility of the programmer who writes these kinds of varargs methods to ensure that they properly handle the varargs formal parameter and ensure heap pollution does not occur.
Q12. Multithreaded Custom Class Loaders in Java7
Ans. Java7 release contains an important enhancement for multithreaded custom class loaders. In previous releases, certain types of custom class loaders were prone to deadlock. The Java 7 release modifies the locking mechanism to avoid deadlock.
Background
The function of a ClassLoader is to locate the bytecode for a particular class, then transform that bytecode into a usable class in the runtime system. The runtime system provides class loaders that can locate bootstrap classes, extension classes, and user classes. The CLASSPATH environment variable is one way to indicate to the runtime system where the bytecode is located.
Custom class loaders will not run into deadlocks if they adhere to an acyclic class loader delegation model. Acyclic delegation is envision of architects ClassLoader. In this model, every class loader has a parent (delegate). When a class is requested, the class loader first checks if the class was loaded previously. If the class is not found, the class loader asks its parent to locate the class. If the parent cannot find the class, the class loader attempts to locate the class itself.
Deadlock Scenario
In earlier releases of the Java platform, multithreaded custom class loaders could deadlock when they did not have an acyclic delegation model. Here is one example:
class A extends B
class C extends D
ClassLoader Delegation Hierarchy:
Custom Classloader CL1:
directly loads class A
delegates to custom ClassLoader CL2 for class B
Custom Classloader CL2:
directly loads class C
delegates to custom ClassLoader CL1 for class D
Thread 1:
Use CL1 to load class A (locks CL1)
defineClass A triggers
loadClass B (try to lock CL2)
Thread 2: Use CL2 to load class C (locks CL2)
defineClass C triggers
loadClass D (try to lock CL1)
Synchronization in the ClassLoader class was previously not sufficiently granular. A request to load a class synchronized on the entire ClassLoader object makes it prone to deadlock.
Class Loader Synchronization in the Java 7 Release
Java 7 release includes the concept of a parallel capable class loader. Loading a class by a parallel capable class loader now synchronizes on the pair consisting of the class loader and the class name. In the previous scenario, using the Java7 release, the threads are no longer deadlocked, and all classes are loaded successfully:
Thread 1:
Use CL1 to load class A (locks CL1+A)
defineClass A triggers
loadClass B (locks CL2+B)
Thread 2:
Use CL2 to load class C (locks CL2+C)
defineClass C triggers
loadClass D (locks CL1+D)
Custom class loaders that do not have a history of deadlocks do not require any changes. In particular, you do not need to change custom class loaders that follow the recommended acyclic hierarchical delegation model, that is, delegating first to their parent class. For backward compatibility, the Java7 release continues to lock a class loader object unless it registers as parallel capable.
To create new custom class loaders, the process is similar in Java 7 as in previous releases. Create a subclass of ClassLoader, then override the findClass() method and possibly loadClass(). Overriding loadClass() makes your life more difficult, but it is the only way to use a different delegation model.
If you have a custom class loader with a risk of deadlocking, with the Java 7 release, you can avoid deadlocks by following these rules:
1. Ensure that your custom class loader is multithread safe for concurrent class loading.
- Decide upon an internal locking scheme. Ex: ClassLoader uses a locking scheme based on the requested class name.
- Remove all synchronization on the class loader object lock alone.
- Ensure that critical sections are safe for multiple threads loading different classes.
3. Check that all class loader classes that this custom class loader extends also invoke the registerAsParallelCapable() method in their class initializers. Ensure that they are multithread safe for concurrent class loading.
If your custom class loader overrides only findClass(String), you do not need further changes. This is the recommended mechanism to create a custom class loader. If your custom class loader overrides either the protected loadClass(String, boolean) method or the public loadClass(String) method, you must also ensure that the protected defineClass() method is called only once for each class loader and class name pair.