In most conversations about programming, speed, and efficiency in Java are reoccurring terms as it's a language native to these features.
You might ask, what is Java? You may also wonder what it means for a programming language to be interpreted or compiled. Let's explore these concepts in the following sections.
What is the Java Programming Language?
Java is a programming language that is platform-independent and adopts the Object Oriented Programming structure to build software. It is not only limited to the web but also mobile and desktop.
You might ask what it means to be platform-independent. This is about the capability of Java programs to effortlessly operate on various operating systems like Windows, Mac, and Linux.
Unlike languages like C or C++, which can be limited to a solitary Operating System, Java's adaptability eliminates the limitation of depending on a specific operating system for it to operate.
To understand Java better, let's take a look into its history and the purpose it was created for.
A Brief History Of Java
In 1991, Java was developed by James Gosling and his team at Sun Microsystems. The first version of the language, 1.0, was released in 1996.
It was designed to have similar syntax to other higher-level languages like C and C++, so that engineers at that time wouldn't have a problem understanding and developing a program with Java.
It was developed with the intent to develop television interactivity, but it was too advanced for its time. The technology infrastructure and devices of that time were not adequately prepared to leverage Java's advanced features.
In recent times, we have seen Java still in the spotlight as it is the software that most big tech companies adopt due to its versatility. It's very much in demand as a primary language for building enterprise-level applications on different platforms.
Moving forward in our exploration of Java's inner workings, there are some key concepts you need to understand. Those concepts are understanding what it means for a programming language to be interpreted or compiled.
Interpreted vs Compiled Languages
We have often heard of these terms, but to understand them better, let us picture a foreign student embarking on two journeys to different locations.
In the first scenario, they arrive at the location without knowing the local language or customs, so they rely on an interpreter or translator to be able to communicate with the natives of that area. This is much like how an interpreter in an interpreted programming language translates code on-the-fly for the computer to understand.
In the second scenario, the student had the foresight to partake in classes to learn the language they will be needing, and is familiar with the customs of the location they will be going to. On arrival, they doesn't need an interpreter in order to communicate with the natives of that area. This is similar to how a compiled language functions.
By understanding these two scenarios, you can spot the differences between an interpreted language and a compiled language.
The former translates each line of code on the go. That is a real-time translation, much like how the student in the example made use of a translator/interpreter.
On the other hand, the latter first predigests the code to ensure smooth flow. Then it 'compiles' or transforms it into machine code which the computer can understand and execute, similar to our prepared student communicating fluently.
Each of these programming terms has a similar purpose of allowing communication between computer systems and the programmers.
Now, as you inquire deeper into these programming terms, let's understand how Java works.
How does Java Work?
Java is unique in the sense that it is both compiled and interpreted.
The basic flow is that it is first compiled from source code into what we call bytecode. Then, it is interpreted to machine code using an interpreter named JVM, which stands for Java Virtual Machine. This interpretation happens at runtime, so you can refer to Java as an interpreted language and also a compiled language.
Now you might ask what is bytecode?
The common terms we hear in program compilation and interpretation are mostly source code and machine code, The bytecode is akin to machine code in the sense that is it a compiled source code, but it's not executable unless interpreted by a virtual machine, in this case, the Java Virtual Machine.
What is a Java Virtual Machine?
The JVM (Java Virtual Machine) is a platform dependent virtual machine that interprets and executes Java Programs in bytecode and any other program from other languages that compiles to Java bytecode.
Java bytecode has a special quality: it's not tied to a specific platform.
Java bytecode, which is akin to a universal dialect, can be executed by a Java Virtual Machine (JVM) on most operating systems you can envision. Presently, when the JVM starts its processing, it converts that bytecode into machine code that's an exact match for the operating system it's running on.
But here's where it gets interesting: while Java bytecode is like a chameleon that can adapt to different platforms, the JVM itself isn't quite as flexible. It's like having different versions of a tool, each custom-made for a specific operating system.
So, you've got different flavours of JVMs to match the variety of operating systems out there.
Now you might be wondering since Java undergoes two processes, compilation and interpretation, does that make it slow? The answer is no. Although it undergoes various processing, it still is fast.
To understand this, we need to discuss some fundamentals seen in the JVM such as the JIT (Just In Time Compilation), Hotspot JVM, and its memory/storage uniqueness.
How does Java Attain High Speed?
It is important to note that Java streamlined its compilation for fast execution and took the platform independence of an interpreted language.
Compiled languages are known for their speed in processing due to them converting the source code fully and not line by line, unlike their counterpart the interpreted languages. Java compiles to bytecode which is faster to interpret using the JVM due to bytecode being very compact and optimized already.
JVM has several components such as its ability to manipulate memory at run time. Due to automatic memory management, less time is spent on the allocation of memory size for data using its inbuilt garbage collector.
How to make use of the JIT Compiler to achieve faster execution
Another interesting feature in the JVM, which helps to cut down time spent on improving code processing speed, is the JIT, which is an acronym for Just In Time.
The JIT compiler is at the core of the execution engine in the JVM. It observes as bytecode is being interpreted and executed, and stores a "Hotspot". This is a repeated set of executed bytecode.
Instead of reinterpreting and executing this same bytecode again, the JIT compiler stores this frequently executed bytecode in memory as a native machine code for future use.
This cached machine code is what gets executed when you run your bytecode, which results to a faster execution time. This whole process can be called a dynamic compilation.
What is garbage collection and how it improves JVM performance?
The JVM is unique in the way that it automates memory management. This involves memory allocation for programs it executes.
Unlike C/C++, where you write a code and then manually compute the memory allocation of the program, this feature improves and enhances the productivity of the programmers. It also reduces some errors that can result due to running out of memory or memory leaks.
Garbage collection aids this by automatically reclaiming memory occupied by objects in a program that is no longer in use. Doing so, frees up that memory for future use.
You might ask how this works. It follows the steps outlined below:
- It first identifies unreachable objects. These are objects in a program that are not in use. It's strict in doing this because if it selects an object in use and reclaims it, it will crash our program because there will be a referenced object error. That is a term called dangling references.
- Next, it reclaims the memory of the selected unused objects, which frees up memory for other future objects.
- In some cases, during this collection, the garbage collector compacts the remaining objects in memory, which improves memory locality. This is optional, as it depends on the garbage collection algorithms in use.
This is a rundown of how the garbage collector process works in the Java Virtual Machine.
Depending on the set garbage collector algorithm in place, it is important to note that the collection is triggered by the JVM when it detects low memory space, and kickstarts the steps above.
It is also essential to note that sometimes a pause in execution will happen in other to memory space.
There are some extra techniques performed on the compiler which help enhance performance.
The Compiler optimizes the bytecode by taking note of similar calculations. Instead of rerunning these calculations, it just outputs the result. This is called constant folding.
It also removes codes out of scope or codes that will never be executed. Doing this, eliminates unnecessary computation.
Another technique used by the compiler is the peephole optimization, which involves the replacement of bytecode instructions with a more efficient sequence.
So many factors contribute to the performance and speed of Java Programs.
These range from it both being compiled and interpreted, to the intricacies of some components in its interpreter (the Java Virtual Machine). All these play a major role in achieving high speed.
Ultimately, to have faster programs the programmer must write cleaner and organized code using good data structures.
In this article, you learned about a brief history of Java, and the differences between compiled and interpreted languages. You also learn about and some of Java's major components, such as the Java Virtual Machine, and how it contributes to the performance of Java programs.