Web browser with java virtual machine installed. The essence of the Java virtual machine


The design of the JVM was developed based on many years of programming experience in languages ​​such as C and C++. This allowed us to create a JVM structure that made the programmer's life much easier:

  • application code runs in a container,
  • there is a protected program execution environment,
  • the programmer's ability to manage memory has been reduced to a minimum,
  • the execution environment was made cross-platform,
  • runtime (during program execution) information for self-management began to be used.

This last aspect allows the JVM to accept more optimal solutions when executing a program, based on how often some of its blocks are called. The virtual machine itself interprets the byte code of the compiled Java program, but in the JVM it is possible to compile frequently called program blocks into machine code at runtime. This technology is called Jast-in-time (JIT). This does not mean that the machine code will be saved in the program file, it will exist only during its execution in random access memory. Thus, the performance of a Java program, after several cycles of operation, can become higher than that of similar programs in compiled languages ​​C and C++.

What is bytecode?
When developers are first introduced to the JVM, they sometimes think of it as a computer within a computer. Therefore, it is easy to represent the bytecode as machine code for the processor internal computer or machine code for a fictional processor.

In fact, the bytecode is not very similar to the machine code that would run on a real processor. Experts call bytecode an intermediate representation, an adaptation between source code and machine code.

The ultimate goal of bytecode is to have a format for representing data and control information that can be efficiently executed by the JVM.

Why was it called bytecode?
The instruction code (opcode opcode) is only one byte (some operations have parameters that follow the operation byte as a stream of bytes), so there are only 256 instruction variants. In practice, some are not used, leaving about 200 in use, but some of them were not used in latest version javac.

Is the compiler javac?
Typically, the compiler produces machine code, and javac produces bytecode that is not machine code-like. However, class files are a bit like object files (like Windows *.dll or Unix *.so) and are unreadable.

In terms computer science javac is most similar to a front-end compiler that creates an intermediate representation program code, from which machine code can then be generated.

Thus, in its pure form, javac is not a compiler, but in most books and articles you can see such phrases - source code compiler or javac compiler. And the actual compilation is done by JIT, when it creates machine code to optimize program execution.

Is the bytecode optimized?
Early versions of javac produced highly optimized bytecode. This turned out to be wrong. With the advent of JIT compilation, getting machine code quickly became more important. Therefore, it turned out that it was necessary to create bytecode that would be easily JIT compiled. Therefore, now there is a compromise between the optimality of the bytecode and the speed of its JIT compilation. In turn, some part of the bytecode continues to be interpreted.

Is bytecode truly machine independent? What about byte order?
The byte code format is always the same, no matter what machine it was created on, it is always big-endian (from most significant to least significant). For example, an integer that occupies 4 bytes in memory is stored there byte by byte from high to low.

Is Java an interpreted language?
Essentially the JVM is an interpreter (with JIT compilation, which gives a performance boost). Actually source Java is not sent to the interpreter for execution, it is compiled into bytecode, and the bytecode is interpreted by the JVM.

Can other languages ​​run on the JVM?
Anything that compiles to bytecode can be executed on the JVM. Examples of such languages ​​are Scala, Clojure, Kotlin, etc.
In addition, it is possible to implement an interpreter for a certain language in Java. How this is done, for example, for the JRuby language.

To see the byte code, just open the .class file in a simple text editor. It will be clearly unreadable. However, we have the ability to disassemble it into bytecode mnemonics, that is, into elementary instructions (that can be read) and data.

Let's consider a simple program adding two numbers.

Public class Main ( /** * Method that adds two numbers. * @param a the first term * @param b the second term * @return the result of adding two numbers. */ private static int adding(int a, int b) ( return a + b; ) /** * Entry point to the program * @param args unused command line parameters */ public static void main(String args) ( // first term int x = 43; // second term int y = 56; // adding the values ​​of two variables and placing // the result into a variable. int result = adding(x, y); // displaying the value of the variable result. System.out.println(result); ) )

For convenience, you can create an empty project in Idea, and place this class in the src folder.
Run it for execution. Idea will compile it, naturally using javac for this, create a Main.class file and launch it for execution. Let's check that everything worked out, the sum of numbers should appear in the console.

Then go to the terminal in the folder of your project, and then to the folder where it should be created class file. This should be in the out/production folder.

To disassemble, run next command, and pass the compiled class as a parameter:

Javap -c -p Main.class

C - disassembly,
-p - display information about all members of the class.

In the terminal we should get the following:

Public class org.dart.Main ( public org.dart.Main(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object." ":()V 4: return private static int adding(int, int); Code: 0: iload_0 1: iload_1 2: iadd 3: ireturn public static void main(java.lang.String); Code: 0: bipush 43 // number literal assigned to the first variable 2: istore_1 3: bipush 56 // number literal assigned to the second variable 5: istore_2 6: iload_1 7: iload_2 8: invokestatic #2 // Method adding:(II)I 11: istore_3 12: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 15: iload_3 16: invokevirtual #4 // Method java/io/PrintStream.println:(I)V 19: return )

Here you can see a lot of interesting things, even code that we obviously didn’t write ourselves. For example method:

Public org.dart.Main(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object." ":()V 4: return

This is not an ordinary method, it is a constructor. It was added automatically during compilation. Why it is added is a separate story.

Please note that it seems that the lines of code within the methods are numbered out of order. This is not entirely true. It takes into account the length of the parameters on which some operations depend.

Let's consider a method that adds two numbers, using comments to explain the meaning of the operations:

Private static int adding(int, int); Code: 0: iload_0 // load the first integer from the parameter onto the stack 1: iload_1 // load the second integer from the parameter onto the stack 2: iadd // add two numbers from the stack and push the result onto the stack 3: ireturn // return an integer numbers from the method

IN in this case all operations occupy exactly one byte.
The list of operations can be found at this link:
Java bytecode instruction list

Any program compiled by javac consists of a set of such operations and their parameters. In turn, the JVM, when the program starts, begins to interpret commands, turning some program blocks into machine code using JIT technology.

As an experiment, you can see what code you get for different operations with different types data. You can also check how many bytes will be given for data for numbers exceeding one byte, that is, greater than 255.

Can be compiled into Java bytecode, which can then be executed by the JVM.

The JVM is a key component of the Java platform. Since Java virtual machines are available for many hardware and software platforms, Java can also be considered as a glue software, and as an independent platform, hence the principle “write once, run anywhere”. Using one bytecode for many platforms allows Java to be described as “compile once, run anywhere”.

JVM Specification

Confrontation between Sun and IBM

In 2001, with the goal of developing a standard for cross-platform Desktop applications, Eclipse.

IBM VisualAge. IBM managed to balance the interests of the free community and the interests of business (its interests) in the Eclipse Public License, recognized by the FSF.

The project is developing successfully, is recognized by the industry, and has largely separated from IBM into the independent Eclipse Foundation.


Wikimedia Foundation. 2010.

See what "Java Virtual Machine" is in other dictionaries:

    Java virtual machine- The main part of the performing Java systems(Java Runtime Environment; JRE). The Java Virtual Machine interprets and executes Java byte code previously generated from source text Java programs s Java compiler. JVM can be used for... - developed by JavaSoft. Web applications programs created using it can execute naturally within the operating system, or Web browser, or inside an emulating environment known as the Java Virtual Machine... Dictionary e-business

    - (JPF) free tool for testing multi-threaded Java programs. At its core, it is a Java Virtual Machine on the basis of which model checking methods are implemented. This means that... ... Wikipedia

    Java virtual machine- Byte interpreter Java code programs. A virtual machine designed to execute Java applets. The JVM is built into most web browsers. This allows you to execute Java applets on the client side, the calls of which are provided in ... ... Technical Translator's Guide

    Another name for this concept is “Java”; see also other meanings. Not to be confused with JavaScript. Java Class language... Wikipedia

    It is necessary to check the quality of the translation and bring the article into compliance with the stylistic rules of Wikipedia. You can help improve this article by correcting errors. Original n... Wikipedia

Many good and correct answers have already been given here, but I would like to clarify that this metaphor is:

The Java virtual machine is also essentially an interpreter

can lead you down a very wrong path!

The words in the names have a fairly precise meaning, and the JVM is called exactly by car, and not an interpreter, and not a compiler, is not at all accidental. There is a compiler in Java (javac), and it is needed not for executing the program, but specifically for compiling it (into bytecode). That is why it is not included in the JRE (runtime environment), but is contained in the JDK (development environment). The JVM itself has another one, a JIT compiler, which compiles bytecode into processor instructions during program execution, but that’s another story, and it also can’t be called an interpreter.

Essentially, the JVM is a processor, only a virtual one. And like any processor (hardware, x86 type, or virtual, type CLR in .NET), it has its own set of op codes called a byte code. Just as the x86 can run code generated by a compiler with C++, or Pascal, or Go, the JVM can run bytecode compiled from Java, or Scala, or Kotlin (or even written by hand), and .class -file is essentially the same .exe (more precisely .so), compiled for the “JVM processor”. This is what cross-platform is all about. Just as code compiled for x86 will run on a processor from Intel or AMD, so will JVM bytecode run on JVMs from Oracle, IBM, OpenJDK, etc. And even the presence of JIT, compiling bytecode into the opcode of a specific hardware processor during execution still does not give reason to call an honest stack (SUN) or register (Dalvik) VM an interpreter, even if only in essence :)

The fact is that this classification itself (interpreted/compiled LANGUAGE) recent years 25 is practically meaningless. Languages ​​that were initially oriented towards implementation in the form of an interpreter (with simply parsed vocabulary, so that the interpreter was smaller and could leave enough space for the program itself in a limited memory) such as APL or BASIC, now (except, of course, for very highly specialized applications) have a place of honor only that in the old textbooks, of which this very classification, with tenacity worthy of better use, continues to be copied word for word into the new ones. At the same time, for some reason, they forget to clarify that these two concepts are no longer about the languages ​​themselves, but only about some methods of their implementation, and that since then, in addition to these methods, many other good and different concepts on this topic have appeared ( such as VM, JIT, garbage collectors, and at least the same OOP, different types typifications and a million other things), which were simply not yet in those textbooks due to their year of publication. And that today, even for languages ​​that are fundamentally designed for compilation for a register architecture, such as C, there are a dime a dozen interpreters (one, two, three) ... which, again, no one names virtual machines, because that's the whole point different concepts. In short, it’s like trying to understand where fire, water, earth and air are in quantum mechanics, as Plato and Aristotle understood them :)

P.S. To understand when this classification was still relevant, I recommend this. There, the creators of APL, one of the first true interpreted languages, discuss the pressing problems of language development of the time. If English is a problem, at least look at the introduction... those pieces of hardware had less memory and computing power than in a modern SIM card :)

JVM (Java Virtual Machine) - the basis of the language Java programming. Java Environment consists of five elements:
Java language
■Bytecode definition
■ Java/Sun class libraries
■ Java Virtual Machine
■ Structure of the .class file

Of all these five elements, the elements that led to the success of Java
■ Bytecode definition,
■ file structure.class,
■ and Java Virtual Machine.

Thus, "write once and run anywhere" has actually been made possible by the portability of the .class file, which facilitates execution on any computer or chipset using the Java Virtual Machine.

1.3.1 What is the Java Virtual Machine?

A virtual machine is software based on the concepts and idea of ​​an imaginary computer that has a logical set of instructions, and commands that define the operations of that computer. This is, one might say, small operating system. It forms the necessary level of abstraction, where independence from the platform and equipment used is achieved.

The compiler converts source text into code that is based on the computer's imaginary instruction set and is independent of the specific processor. An interpreter is an application that understands these command streams and translates these commands for the hardware being used, to which the interpreter belongs. The JVM creates a runtime system internally that helps code execute when
■ loading .class files,
■ memory management
■ performing exception handling.

Due to the inconsistency of hardware platforms, the virtual machine uses the concept of a stack, which contains the following information:
■ Method state descriptors
■ Operands to bytecodes
■Method parameters
■ Local variables

When code is executed using the JVM, there is one special register that is used as a counter, indicating the currently executing commands. If necessary, commands modify the program, changing the flow of execution, otherwise the flow is sequential and moves from one command to another.

Another concept that is becoming popular is the use of a Just In Time (JIT) compiler. Browsers like Netscape Navigator 4.0 and Internet Explorer 4.0 includes JIT compilers that increase the execution speed of Java codes. The main goal of JIT is to convert the bytecode instruction set to machine code instructions targeted to a specific microprocessor. These commands are stored and used whenever a request is made to that specific method.

1.3.2 Java runtime

JRE (Java Runtime Environment) The JVM interacts with the hardware on one side and the program on the other. The JRE runs code compiled for the JVM:
Loading .class files
Done using the "Class Loader"
The class loader does a security check if the files are used on the network.
Bytecode verification
Performed by a "bytecode verifier"
A bytecode verifier checks code format, object type conversions, and checks for access violations.
Executing Code
Executed "by the interpreter at runtime"
The interpreter executes the bytecodes and makes requests for the hardware being used.


Figure 1.3: Java Runtime Environment

1.3.3 Exception handling and memory management

In C, C++ or Pascal, programmers used primitive methods for allocating and freeing memory blocks - dynamic memory. Dynamic memory is a large piece of memory, which is designated in the volume of the entire memory.

Dynamic memory is used:
Free Blocklist
Distributed Block List

The free list checks a block of memory whenever a request is made. The allocation mechanism used is the "first fit" method, whereby the first smallest block of memory is allocated depending on the request. This procedure allocates and frees small amounts of memory various sizes from dynamic memory, while dynamic memory fragmentation is minimized.

There is a stage whereby a memory request is made to obtain a larger block of memory than is available. In such cases, the heap manager must create more memory. This technique is called compaction. This is the process by which all free available memory blocks are combined together by moving free memory one end of the dynamic memory, thus creating one large block of memory.

The Java Virtual Machine uses two separate heaps for static and dynamic memory allocation.

Dynamic Memory - Does not do dynamic memory exception handling, which retains all class properties, persistent pool and method tables.

The second dynamic memory is again divided into two sections, which can be expanded in opposite directions when needed. One partition is used to store instances of objects, and the other partition is used to store handles to those instances. A descriptor is a structure that consists of two pointers. Point to a table of object methods and other items to an example of that object. This arrangement essentially eliminates the need to maintain paths pointing to an object when modifying pointers after compaction. All we have to do is update the handle pointer value.

The exception handling algorithm is applied to objects placed in dynamic memory. As a request for a block of memory is received, the heap manager first checks the free list and if the heap manager cannot find free memory blocks, exception handling is invoked as soon as the system has been idle for a sufficient period of time. In cases where applications are highly interactive and system downtime is kept to a minimum, exception handling should be called explicitly by the application.

The exception collector calls the termination method before a sample object is collected using exception handling. The shutdown method is used to clean up external resources like files and streams that are open and not taken care of in standard exception handling. Even if we explicitly call exception handling on the (System.gc()) method, it will not work quickly. It's just meant to work. This also means that exception handling cannot be called. This is because exception-handling threads run at very low priority and may be interrupted frequently. This can happen when our object has never been located in memory before.







2024 gtavrl.ru.