Understanding dynamic memory (heap) in Java applications

Let's start with a simple question. Does every Java developer really understand how memory works in Java? One of the responsibilities of any Java developer is to ensure that by fine-tuning a Java application, it can extract as much performance as possible. It takes time to learn how to manage memory in Java and understand the process, this applies to everyone who deals with Java. In this article I will try to explain how to master these skills.

The point is to properly organize in the Java language the allocation of new objects and the removal of unused ones; the latter process is more often called “garbage collection”. By default, Java and automatically handle memory management quite well. The fact is that the garbage collector constantly works in the background and cleans up unused objects, as well as those that are not worth references, in order to free up some memory.

A developer simply cannot do without sufficient experience and knowledge of how memory is used in Java, how exactly the Java virtual machine (JVM) and the garbage collector work. Without knowing this, it is impossible to find and eliminate bottlenecks, in search of which we execute the code and test its performance.

All my experience in the theory and practice of programming, studying the structure of this language, the desire to understand technical problems and many years of experience hosting numerous Minecraft servers gradually led me to understand how the Java virtual machine works in various environments. When this adventure first began, I wasn't even sure how or where exactly a Java object was created. I also didn't fully understand how the various garbage collectors clean up various objects that are not worth reference, as they do in various areas of the Java heap.

When I first tried to optimize several Minecraft servers (increase their performance), I encountered several memory errors, in particular java.lang.OutOfMemoryError. It was then that I began to understand much better what roles the stack and heap play when optimizing memory in Java.

Here's an important thing to keep in mind when thinking about memory in Java: you should always start the Java Virtual Machine first, and then tune it for the best possible performance. Creating any class, method, object or variable – it's working with memory. Thus, all information is stored in the Java dynamic memory (heap).

Java Dynamic Memory

I think most of you have seen ten to twenty examples of diagrams explaining how dynamic memory works in Java. Any Java developer needs to understand what is the difference between

PermGen

And

Metaspace

in the latest releases of the Java Development Kit (JDK). We will turn to these diagrams below.

image

image
image

Dynamic memory is divided into two generations. The first one is called

young

and the second

old

. The first large part is called space

eden

and the second – space

survivor

. Space

survivor

consists of subspaces

survivor0

And

survivor1

.

Now let's explain why each of the spaces in a generation is needed young. All objects we create are first stored in space eden. The Java Virtual Machine has automatic memory management enabled by default.

In cases where there are a lot of objects in the application – say, they are created in the thousands – memory eden will be completely filled with objects. Once the garbage collector notices this, it will remove any unused objects or objects that are not referenced. This mechanism is called a “Minor Garbage Collector”. The small garbage collector will carry all surviving objects into space survivor. From this we draw the following conclusion: generation young automatically falls under garbage collection to free up memory as needed. This operation will be performed quickly and in a short time.

Considering that so many objects can be created in different classes in the application code, the space allocated in memory for eden. Here we can assume that they will work GC1, GC2 And GC3, and perhaps many others. When garbage collection operations are called in such a huge number in the Java virtual machine, the small garbage collector has no choice but to check all the objects that are ready to be moved to survivor. Finally, the Java Virtual Machine will be ready to move all remaining objects into memory space survivor.

Surviving objects from the generation young move into generations oldand when space old is filled with objects, the main garbage collector is turned on.

Main Garbage Collector

So, the job of the master garbage collector begins when the generation

old

memory is completely filled with objects. Please note that it takes some time for the main garbage collector to start. When a Java developer (or team of developers) writes an application from scratch or automates this work using some framework, one must be extremely careful in handling generations

young

And

old

.

A big thing to think about when developing in Java is to try not to create unnecessary objects that would be used infrequently in your application code anyway. After all, if you create any objects, the garbage collector will dispose of them as soon as they complete their assigned tasks. To make it easier to understand the work of the garbage collector, you can formulate its purpose as follows:

Small garbage collector aims to recycle generation youngand generation old falls under the actions of the main garbage collector.

If we consider as an example how the Amazon or Walmart sites are structured, it turns out that a huge number of requests are received from them to the web server. When there is active traffic, delays will begin to appear as requests are processed. The fact is that the main garbage collector takes up quite a lot of memory space – this is necessary to destroy unused objects. A side effect in this case is that there is a large load on the CPU and RAM on those nodes that serve the site(s).

This picture means that you are creating too many instances of a particular class within the system. In this case, the main garbage collector will try to continuously and in large quantities destroy all unused objects. The work of the main garbage collector takes much more time than the work of the small one.

Memory related errors

I recommend that a Java developer learn to read and understand the output of error messages – after all, they can be used to clearly understand what is happening with the Java virtual machine; these are not errors in the application code itself that runs on it. The actual cause of the code error you are seeing is, for example, a memory leak, a garbage collector failure, it could be related to resource allocation and even synchronization problems. First we need to learn how to solve such problems, because this way we can quickly limit the affected area of ​​​​resources.

Please note that we will also need to track how resources are used. Performance testing of Java applications is highly recommended. To do this, you can, for example, profile each category, take multiple heap dumps, inspect and debug application code, and much more. If none of these measures work, then it is usually recommended to simply allocate more resources to your application. Below are common errors and what happens when they occur.

Setting the initial and maximum heap size

Initial heap size – XMS

This is the initial heap size. It usually allocates 1/64th of the total RAM available on the node you are using. Usually in this case I set the value 128MB. This value can be overridden via the command line using the option java -Xms128M.

Maximum heap size is XMX

This is the maximum memory that can be allocated to the heap. The smaller the total RAM power on the node you are using, the smaller it is. Typically this is where I set the default to 8192MB. This value can also be overridden via the command line using the option java -Xmx8192M.

Here is a complete example of a command that runs a Java application.

java -Xms128M -Xmx8192M app.jar

The initial and maximum values ​​can be changed based on the needs of the application. I usually select default values ​​that I assign to all the Java applications I work with. I typically have to deal with Minecraft servers, and Minecraft users typically need no more than 8GB of memory on their Minecraft server. I give them an additional

1GB

memory for performing background tasks, such as garbage collection.

Additional options

Conclusion

There are easily 500+ arguments that could be passed to the Java Virtual Machine to fine-tune Java garbage collection and memory to run applications. If you want to control other aspects, the number of such arguments will easily exceed 1000. Here I have explained only a very few arguments that help solve the most pressing issues related to managing Java applications and other programs.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *