Unit-1 Java Notes New
Unit-1 Java Notes New
JVM Architecture
JVM architecture in Java contains classloader, memory area, execution engine etc.
(i) Loading – loads the classes dynamically into the JVM memory during runtime.
(ii) Linking- After a class is loaded into the memory, it undergoes the linking process. Linking a
class or interface involves combining the different elements and dependencies of the program
together. The linking process involves following 3 steps:
(a) Verification - JVM verifies the bytecode for type mismatches and buffer overflows. For
example, for the code built using Java 11 and run on Java 8, verification fails.
(b) Preparation – Java allocates memory for the static fields of a class or interface and
initializes them with default values.
For example: a variable is declared in your class-
public static final boolean enabled=true;
so, during preparation phase, JVM allocates memory for the variable enabled &sets its
value to the default value for a Boolean, which is false.
(c) Resolution – in this phase, symbolic references are replaced with direct references
present in the runtime constant pool. For example-if you have references to other classes
or constant variables present in other classes, they are replaced with their actual
references.
(iii) Initialization – It includes calling the class’s constructor, executing the static block,
assigning values to all the static variables. For example- variable enabled is now assigned its
actual value i.e. true.
2) Method Area
JVM Method Area stores all the class level data such as the runtime constant pool, fields and method
data, and the code for methods and constructors.
public class Employee{
private String name;
private int age;
public Employee(String name, int age)
{
this.name=name;
this.age=age;
}
}
So the field level data such as name and age, and constructor details are loaded into the method area. This
method area is created on the virtual machine startup and there is only one method area per JVM.
3) Heap
All the Objects, their related instance variables, and arrays are stored in the heap. It is mainly 2-3 GB to
minimize garbage collection pauses. This is the runtime area from which memory for all class instances
and arrays are allocated. This memory is common and shared across multiple threads.
For example-
Employee employee = new Employee;
Here, an instance of Employee is created and loaded into the heap area.
Method area and heap area share the same memory for multiple threads.
4) JVM language Stack Area
Java language Stacks store local variables, and it’s partial results. Each thread has its own JVM stack,
created simultaneously as the thread is created. A new frame is created whenever a method is invoked,
and it is deleted when method invocation process is complete. So the stack area is short lived.
For every method call, one entry is made in the stack memory, which is called stack frame. When the
method call is complete, the stack frame is destroyed. The stack frame contains local variables, operand
stack and frame data.
5) PC Registers
PC register store the address of the Java virtual machine instruction which is currently executing. In Java,
each thread has its separate PC register.
6) Native Method Stacks
Native method stacks hold the instruction of native code depends on the native library. It is written in
another language instead of Java.
7) Execution Engine
It is a type of software used to test hardware, software, or complete systems. The test execution engine
never carries any information about the tested product.
Execution engine executes the “.class” (bytecode). It reads the byte-code line by line, uses data and
information present in various memory area and executes instructions. It can be classified into three
parts:
Interpreter: It interprets the bytecode line by line and then executes. The disadvantage here is that
when one method is called multiple times, every time interpretation is required.
Just-In-Time Compiler(JIT) : It is used to increase the efficiency of an interpreter. It compiles the
entire bytecode and changes it to native code so whenever the interpreter sees repeated method
calls, JIT provides direct native code for that part so re-interpretation is not required, thus
efficiency is improved.
Garbage Collector: It destroys un-referenced objects. Garbage collection runs on the heap memory
to free the memory used by objects that don’t have any reference.
The Java VM or Java Virtual Machine resides on the RAM. During execution, using the class loader the
class files are brought on the RAM. The bytecode is verified for any security breaches.
Next, the execution engine will convert the Bytecode into Native machine code. This is just in time
compiling. It is one of the main reason why Java is comparatively slow.
NOTE: JIT or Just-in-time compiler is the part of the Java Virtual Machine (JVM). It interprets part of
the Byte Code that has similar functionality at the same time.
A compiler is a program which converts a program from one level of language to another. Example
conversion of C++ program into machine code.
The java compiler converts high-level java code into bytecode (which is also a type of machine code).
An interpreter is a program which converts a program at one level to another programming language at
the same level. Example conversion of Java program into C++
In Java, the Just In Time Code generator converts the bytecode into the native machine code which are at
the same programming levels.
1. Dynamic Linking: Unlike C, linking is done at run-time, every time the program is run in Java.
2. Run-time Interpreter: The conversion of byte code into native machine code is done at run-time
in Java which furthers slows down the speed
Summary:
Full form of JVM is Java Virtual Machine. JVM in Java is the engine that drives the Java Code. It
converts Java bytecode into machines language.
JVM architecture in Java contains classloader, memory area, execution engine etc.
In JVM, Java code is compiled to bytecode. This bytecode gets interpreted on different machines
JIT stands for Just-in-time compiler. JIT is the part of the Java Virtual Machine (JVM). It is used
to speed up the execution time
In comparison to other compiler machines, JVM in Java may be slow in execution.
IDG
Notice that the class search begins with the bootstrap class loader. If the class is not found, the class
search returns to the extension class loader for another search. If the class is still not found, the process
tries again using the application class loader. If none of the searches finds the class, then the program
throws a ClassNotFoundExecption.
Bootstrap class loader
Also known as the primordial class loader, this is the class loader where the search starts. The bootstrap
class loader is responsible for loading core Java classes such as java.lang.Object and java.lang.String. It is
implemented in native code and classes are located in the $JAVA_HOME/lib directory.
There were some important changes to class loaders between Java 8 and Java 9. For example, in Java 8,
the bootstrap class loader was located in the Java Runtime Environment's rt.jar file. In Java 9 and
subsequently, the rt.jar file was removed.
Moreover, Java 9 introduced the Java module system, which changed how classes are loaded. In the
module system, each module defines its own class loader, and the bootstrap class loader is responsible for
loading the module system itself and the initial set of modules. When the JVM starts up, the bootstrap
class loader loads the java.base module, which contains the core Java classes and any other modules that
are required to launch the JVM.
The java.base module also exports packages to other modules, such as java.lang, which contains core
classes like Object and String. These packages are then loaded by the bootstrap class loader.
We'll print out the name of the class loader using the toString() method. When we run the code, we should
see output similar to what's shown in Listing 1.
Output:
null
The output is null because the bootstrap class loader has no parent class loader.
In Java 9 and later versions, the extension class loader was removed from the JVM. It was used in earlier
versions of Java to load classes from the extension directory, which was typically located in
the JRE/lib/ext directory.
Instead of the extension class loader, Java 9 and later versions use the java.lang.ModuleLayer class to
load modules from the extension directory. The extension directory is now treated as a separate layer in
the module system, and modules in the extension directory are loaded by the extension layer's class
loader.
Note that in Java 9 and later versions, it is recommended to use modules instead of the extension
mechanism to share code between applications.
The Application class loader (also called the system class loader) loads classes from the application's
classpath. The classpath is a list of directories and JAR files that the JVM searches to find a class.
The application class loader is a standard Java class that loads classes from the directories and JAR files
listed in the CLASSPATH environment variable or the -classpath command-line option. It loads the first
class it finds if there are multiple versions.
The application class loader is the last class loader to search for a class. If it can't find it, the JVM throws
a ClassNotFoundException. This class loader can also delegate class loading to its parent class loader, the
extension class loader.
Aside from loading classes from the classpath, the application class loader also loads classes generated at
runtime, like those created by the Java Reflection API or third-party libraries that use bytecode
generation.
The application class loader is important because it lets developers easily use third-party libraries and
modules in their applications.
The code example in Listing 3 demonstrates how to get the application class loader using
the ClassLoader.getSystemClassLoader() method.
Delegation
The delegation model in Java allows for loading classes flexibly and dynamically at runtime. This is
useful in environments where the class loading requirements are unknown at compile-time.
For instance, in an application server, different applications may need different versions of the same class.
The class loader delegation model makes it possible to meet these requirements without causing conflicts.
Visibility
Class loaders in Java can have varying levels of visibility, which determines their ability to find and load
classes from other class loaders. There are three levels of visibility:
Parent-first visibility: The parent class loader is used first to load a class. If it cannot find the class, the
child class loader is consulted. This is the default visibility model.
Child-first visibility: The child class loader is used first to load a class. If it cannot find the class, the
parent class loader is consulted. This model is useful when a different version of a class is needed.
Hierarchical visibility: Each class loader has its own classpath, and classes loaded by a child class loader
are not visible to parent class loaders. This model is useful to isolate different parts of an application from
each other.
The level of visibility depends on the class loader hierarchy and the classpath, and it can have significant
implications for application behavior. It's important to consider the visibility model used in an application
to ensure that classes are loaded correctly and that classloading conflicts are avoided.
Uniqueness
Java class loaders keep different versions of the same class in separate namespaces, which allows for
creating multiple instances of a class with different versions. This is useful for web applications that need
to load shared libraries without conflicts.
However, this feature can cause issues if not used carefully. If a class is loaded by two different class
loaders, the JVM will treat them as separate classes, and objects created from them will not be
interchangeable. This can lead to unexpected behavior if these objects are passed between methods
expecting objects created by different class loaders.
To avoid these issues, it is recommended to use a single class loader to load classes whenever possible.
When multiple class loaders are used, take extra care to ensure that objects are not passed between classes
with different namespaces.
loadClass(String name): Loads a class with the specified name. It first checks if the class has already been
loaded, and if not, it delegates the loading of the class to the parent class loader.
findClass(String name): Finds the class with the name you've specified. It is called by
the loadClass() method if the parent class loader cannot find the class.
getParent(): Returns a class loader's parent class loader.
getResource(String name): Finds the resource with the name you have specified. It searches the classpath
for the resource and returns a URL object that can be used to access the resource.
setDefaultAssertionStatus(boolean enabled): Enables or disables assertions for this class loader and all
classes loaded by it.
Conclusion
Here are the key points to remember when working with Java class loaders:
Java class loaders are responsible for loading classes into the JVM at runtime.
The three main types of class loaders are the bootstrap class loader, the extension class loader, and the
application class loader (also known as the system class loader).
The bootstrap class loader is responsible for loading core Java classes that are part of the JRE.
The extension class loader is responsible for loading classes that are part of the Java extension
mechanism.
The application class loader is responsible for loading classes that are part of the application's classpath.
You can also define custom class loaders to load classes from non-standard locations or to modify the
behavior of the class-loading process.
A class file consists of a stream of 8-bit bytes. All 16-bit, 32-bit, and 64-bit quantities are
constructed by reading in two, four, and eight consecutive 8-bit bytes, respectively. Multibyte
data items are always stored in big-endian order, where the high bytes come first. In the Java
SE platform, this format is supported by
interfaces java.io.DataInput and java.io.DataOutput and classes such
as java.io.DataInputStream and java.io.DataOutputStream.
This chapter defines its own set of data types representing class file data: The types u1, u2,
and u4 represent an unsigned one-, two-, or four-byte quantity, respectively. In the Java SE
platform, these types may be read by methods such as readUnsignedByte, readUnsignedShort,
and readInt of the interface java.io.DataInput.
ClassFile
{
magic_number;
minor_version;
major_version;
constant_pool_count;
constant_pool[];
access_flags;
this_class;
super_class;
interfaces_count;
interfaces[];
fields_count;
fields[];
methods_count;
methods[];
attributes_count;
attributes[];
}
Elements of class file are as follows:
1. magic_number: The first 4 bytes of class file are termed as magic_number. This is a predefined
value which the JVM use to identify whether the .class file is generated by valid compiler or not.
The predefined value will be in hexadecimal form i.e. 0xCAFEBABE. Now let’s see what happen
when JVM will not find valid magic number. Suppose we have a .java file named
as Sample.java as follows and follow step by step process on your system.
// class Declaration
class Sample
{
public static void main(String[] args)
{
System.out.println("Magic Number");
}
}
Step 3: Now erase at least single symbol from this Sample.class file from starting of file and save
it. Step 4: Now try to run this using java Sample command and see the magic i.e. you will get run time
exception (See the highlighted text in below image):
Note: This can vary depending on how much you remove the .class file data.
2. minor_version & major_version: These both together represents .class file version. JVM will use
these versions to identify which version of the compiler generates the current .class file. We
denotes the version of class file as M.m where M stands for major_version and m stands for
minor_version
Note: Lower version compiler generated .class file can be executed by high version JVM but higher
version compiler generated .class file cannot be executed by lower version JVM. If we will try to
execute we will get run time exception.
This demonstration is for Windows OS as follows:
Step 1: Open a command prompt window and try to check java compiler version and JVM version
using following commands respectively (Highlighted text in image are the commands) Output for 1.8
version will be:
Step 2: Now check with another version which may be higher or lower than already
installed.thisDownload link. And install this to your PC or laptops and note the installation address.
Step 3: Open a second command prompt window and set the path of bin folder of installed jdk installed
during 2nd step. And check for Java compiler version ad JVM version.
Step 4: Now on 1st command prompt compile the any valid .java file. For example: See
above Sample.java file. Compile it as:
Step 5: Now on 2nd command prompt window try to run the above compiled code class file and see
what happen. There is a run time exception which I have highlighted in below image.
Note: Internally jdk 1.5 version means 49.0 and 1.6 means 50.0 and 1.7 means 51.0 etc. class file
version where the digits before the decimal point represent the major_version and digits after decimal
point represents the minor_version.
1. constant_pool_count: It represents the number of the constants present in the constant pool (When
a Java file is compiled, all references to variables and methods are stored in the class’s constant
pool as a symbolic reference).
2. constant_pool[]: It represents the information about constants present in constant pool file.
3. access_flags: It provide the information about the modifiers which are declared to the class file.
4. this_class: It represents fully qualified name of the class file.
5. super_class: It represents fully qualified name of the immediate super class of current class.
Consider above Sample.java file. When we will compile it, then we can say this_class will
be Sample class and super_class will be Object class.
6. interface_count: It returns the number of interfaces implemented by current class file.
7. interface[]: It returns interfaces information implemented by current class file.
8. fields_count: It represents the number of fields (static variable) present in current class file.
9. fields[]: It represent fields (static variable) information present in current class file.
10. method_count: It represents number of methods present in current class file.
11. method[]: It returns information about all methods present in current class file.
12. attributes_count: It returns the number of attributes (instance variables) present in current class
file.
13. attributes[]: It provides information about all attributes present in current class file.