JVM Essentials Guide

Source: Internet
Author: User

Brief introduction

Java Virtual Machine (JVM) is a Java application operating environment, in general, the JVM is defined by the specification of a virtual computer, is designed to explain the implementation of Java source code compiled from the bytecode. More generally, the JVM refers to a specific implementation of this specification. This implementation is based on strict instruction sets and a comprehensive memory model. In addition, the JVM is often described as an implementation of the software runtime environment. Typically the JVM implementation primarily refers to a hotspot.

The JVM specification guarantees that any implementation can interpret the execution byte code in the same way. The implementations can be diversified, including processes, standalone Java operating systems, or processor chips that execute byte codes directly. The JVM we know most is implemented as software, running on popular operating system platforms (including Windows, OS X, Linux, and Solaris).

The structure of the JVM allows for finer control over a Java application. These applications run in a sandbox environment. Make sure that you cannot access local file systems, processors, and network connections without proper permissions. When executed remotely, the code also requires certificate authentication.

In addition to interpreting Java bytecode execution, most JVM implementations also include a JIT (Just-in-time-im) compiler to generate machine code for commonly used methods. The machine code uses the CPU's native language, which is faster than the byte code.

While understanding that the JVM is not a requirement for developing or running Java programs, there is a chance to avoid many performance problems if you learn more about JVM knowledge. Understanding the JVM, in fact, these problems will become simple and clear.

System structure

The JVM specification defines a series of subsystems and their external behavior. The JVM mainly has the following subsystems:

    • Class LoaderClass loader. Used to read the Java source code and load the class into the data area.
    • Execution EngineExecution engine. Executes instructions from the data area.

The data area uses the memory that the underlying operating system allocates to the JVM.

ClassLoader (class Loader)

The JVM uses different classloader at several different levels:

    • Bootstrap class loader (boot class loader): Is the parent class of other ClassLoader, which is used to load the Java Core Library and is the only class loader written in native code.
    • Extension class loader (extension ClassLoader): is a subclass of the Bootstrap class loader loader that is used to load the extension library.
    • System class Loader: is a subclass of the extension class loader loader that is used to load the class file of an application in Classpath.
    • User-defined class Loader (user-defined class loader): is a subclass of the System class loader or other user-defined ClassLoader.

When a class loader receives a request to load a class, it first checks the cache, confirms that the class has been loaded, and then proxies the request to its parent class. If the parent does not successfully load the class, the subclass will attempt to load the class on its own. Subclasses can check the cache of the parent class loader, but the parent class cannot see the class that the child class is loading. The class loading system is designed to assume that a subclass should not repeatedly load classes that have already been loaded by the parent class.

Execution engines (execution engine)

The execution engine executes a byte code that is loaded into the data area one after the other. To ensure that bytecode instructions are readable for the machine, the execution engine uses the following two methods:

    • Explanation execution: The execution engine interprets every instruction it encounters as a machine language.
    • Instant compilation: If an instruction is used frequently, the execution engine compiles it into local code and stores it in the cache. In this way, all the code associated with this method is executed directly, thus avoiding duplication of interpretation.

Although instant compilation takes more time than interpreting execution, it only needs to be processed once for thousands of times. Running in local code can save a lot of execution time compared to interpreting execution every time.

It is not specified in the JVM specification to use instant compilation. Instant compilation is not the only way to improve the performance of the JVM. The specification only specifies the local code corresponding to each bytecode, and how the execution engine implements the corresponding process is entirely determined by the specific implementation of the JVM.

Memory model

The Java memory model is built on the concept of automatic memory management. When an object is no longer referenced by an application, the garbage collector reclaims it, freeing the appropriate memory. This is very different from many other languages that need to release memory on their own.

The JVM allocates memory from the underlying operating system and divides them into the following areas:

    • Heap space: This is a shared memory area used to store objects that can be reclaimed by the garbage collector.
    • Method Area: This area was formerly known as the "immortal generation" (permanent generation), which is used to store the loaded classes. This area was recently canceled by the JVM. The loaded class is now loaded as metadata into the local memory area of the underlying operating system.
    • The region (Native area): This area is used to store references and variables of the base type.

An effective way to manage memory is to divide the space into different generations so that the garbage collector does not have to scan the entire heap area. Most objects have a short life cycle, and objects with long lifecycles often need to be purged until the app exits.

When a Java application creates an object, the object is stored in the "Primary Pool" ( eden pool ). Once the primary pool is full, a minor GC (small-scale garbage collection) is triggered in the Cenozoic. First, the garbage collector flags those "dead objects" (which are no longer referenced by the application), while prolonging the life cycle of all reserved objects (the length of the life cycle is described by numbers, representing the number of garbage collections that have gone through the period). The garbage collector then reclaims the dead objects and moves the remaining live objects to the surviving pool ( survivor pool ), emptying the primary pool.

When an object survives a certain period, it is moved to the Laosheng generation in the heap: "Lifetime Generation" ( tenured pool ). Finally, when a lifetime generation is filled, a full GC or major GC (a complete garbage collection) is triggered to clean up the lifetime generation.

(Translator Note: In general, we combine the primary pool and the surviving pool into the new generation, the region where life generation is the Laosheng generation.) Correspondingly, the GC generated on the Cenozoic is called the minor GC, and the GC generated on the Laosheng generation is called the full GC. I hope we can understand it better when we see the corresponding terminology in other places.

When garbage collection (GC) executes, all application threads are stopped and the system generates a pause. Minor GC is very frequent, so it is optimized to quickly recover dead objects, which is the primary way of recovering the new generation of memory. The major GC is much slower to run because it scans very many living objects. There are many implementations of the garbage collector itself, and some garbage collector can perform major GC more quickly under certain circumstances.

The heap size is dynamic and is only allocated from memory when the heap needs to be expanded. When the heap is filled, the JVM will re-allocate more memory to the heap until the heap size is reached, and this redistribution will also cause the application to stop briefly.

Thread

The JVM runs in a separate process, but it can execute multiple threads concurrently, and each thread runs its own method, which is a part of the Java prerequisites. As an example of an application such as an instant messaging client, it runs at least two threads. One thread waits for the user to enter, and the other checks whether the server has a new message transmission. In the case of server-side applications, sometimes a request may involve multiple threads executing concurrently, so multiple threads are required to process the request.

In the process of the JVM, all threads share memory and other available resources. Each JVM process starts a main thread at the entry point (the Main method), and other threads start from the main thread and become an independent part of the execution process. Threads can be executed in parallel on different processors and can share a single processor, and the thread scheduler handles the case of multiple threads sharing a single processor.

Many applications, especially server-side applications, handle many tasks and need to run in parallel. Some of these tasks are very important and need to be executed in real time. Others are background tasks that can be executed when the CPU is idle. The task is run in a different thread. For example, the server may have some low-priority threads that calculate statistics based on some data. Some high-priority processes are also initiated to process incoming data in response to requests for these statistics. There may be a lot of source data, a lot of data requests from the client, and each request will cause the server to briefly stop the background compute thread in response to the request. Therefore, you must monitor the number of threads running and ensure that there is sufficient CPU time to perform the necessary calculations.

(Translator Note: This paragraph in the original text is in the section of performance optimization, the translator thinks this may be the author's carelessness, seems to be placed in the thread of the chapter more appropriate.) )

Performance optimization

The performance of the JVM depends on whether its configuration matches the functionality of the app. Although the garbage collector and memory recycling processes automatically manage memory, you must control their frequency. In general, the more memory your app can use, the fewer memory management processes that can cause app pauses to work.

If garbage collection occurs more often than you think, you can configure a larger maximum heap size value for the JVM when it is started. The longer the heap is filled, the more time it takes to reduce the frequency of garbage collection. The maximum heap size value can be set with parameters when the JVM is started -Xmx . The default maximum heap size is One-fourth of the operating system memory that is set to be available, or a minimum of 1GB.

If the problem is to reallocate memory frequently, then you can set the initialization heap size to be the same as the maximum heap size. This means that the JVM never needs to reallocate memory for the heap. However, this will lose the optimization of the dynamic heap size adaptation, and the heap size is fixed from the start. Configuration initialization for the size is initiated in the JVM, which is used -Xms to set. The default initialization heap size is set to one-sixty Fourth of the physical memory available to the operating system, or a minimum value is set. This value is determined on a different platform basis.

If you know what kind of garbage collection (minor GC or major GC) is causing performance problems, you can set the size ratio of the Cenozoic and laosheng generations without changing the entire heap size. For applications that need to produce a large number of temporary objects, it is necessary to increase the percentage of the Cenozoic (of course, the consequence is to reduce the size of the Laosheng generation). For applications with more long life cycle objects, you need to increase the proportion of laosheng (naturally need to reduce the size of the Cenozoic generation). Here are some ways to set the size of the Cenozoic and Laosheng generations:

    • When starting the JVM, parameters are used -XX:NewRatio to specify the size ratio of the Cenozoic and Laosheng generations. For example, if you want to make the Laosheng generation five times times the size of the new generation, then set the parameter to-xx:newratio=5, the default parameter is set to 2 (that is, the Laosheng takes up two-thirds of the heap space, the Cenozoic occupies One-third).
    • When the JVM is started, the -Xmn initialization and maximum Cenozoic size are set directly using parameters, then the remaining size in the heap is the size of the Laosheng generation.
    • When the JVM is started, the -XX:NewSize -XX:MaxNewSize initialization and maximum Cenozoic size are directly used and parameters are set, then the remaining size in the heap is the size of the Laosheng generation.

Each thread has a stack that holds the function calls, the return address, and so on, and the stacks have corresponding memory allocations. If there are too many threads, a outofmemory error is caused. Even if you have enough space for the heap to hold the object, your app may crash because it creates a new thread. In this case, you need to consider limiting the maximum stack size in the thread. The thread stack size can be set by parameters when the JVM is started, -Xss and the default value is set to between 320KB and 1024KB, which is related to the platform.

Performance monitoring

When developing or running a Java application, it is important to monitor the performance of the JVM. Configuring the JVM is not a one-time configuration, especially if you are dealing with Java server applications. You must continuously check the heap and non-heap memory allocations and usage, the number of threads created, and the data conditions of the classes loaded in memory. These are the core parameters.

Using the Anturis console, you can configure monitoring for the JVM running on any hardware component (for example, a Tomcat Web server running on a single computer).

JVM monitoring can use the following metrics:

    • Total memory Usage (MB): The total memory used by the JVM. If the JVM uses all available memory, this metric can measure the overall performance of the underlying operating system.
    • Heap memory Usage (MB): That is, all memory allocated by the JVM to the objects used by the running Java application. Objects that are not used are usually removed from the heap by the garbage collector. So, if the index increases, it means that your app doesn't remove unused objects or you need to better configure the garbage collector's parameters.
    • Non-heap memory usage (MB): All memory allocated for the method area and code cache. The method area is used to store references to the classes that are loaded, and if these references are not properly cleaned up, the immortalized pool will grow each time the application is redeployed, resulting in a non-heap memory leak. This indicator may also indicate a leak of thread creation.
    • Total memory in the pool (MB): The memory of all variable memory pools allocated by the JVM and (that is, all memory except code buffers). This indicator allows you to identify the total memory your application can use before the JVM is overloaded.
    • Thread: That is, the number of all valid threads. For example, in a tomcat server, each request is a separate thread to handle, so this metric can indicate how many requests are currently in effect, and whether the low-privileged threads in the background are running.
    • Class: The total number of classes that are loaded. If your app dynamically creates many classes, this could be a cause of server memory leaks.

JVM Essentials Guide

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.