Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the memory relationship between Linux and JVM

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly introduces "what is the memory relationship between Linux and JVM". In daily operation, I believe many people have doubts about the memory relationship between Linux and JVM. The editor consulted all kinds of materials and sorted out simple and easy-to-use methods of operation. I hope it will be helpful to answer the doubts about "what is the memory relationship between Linux and JVM?" Next, please follow the editor to study!

Introduction

On some servers with 8g physical memory, a Java service is mainly running, and the system memory allocation is as follows: the JVM heap size of the Java service is set to 6g, and a monitoring process takes up about 600m Linux itself uses about 800m. On the face of it, physical memory should be sufficient, but in practice, there will be heavy use of SWAP (indicating that physical memory is out of use), as shown in the following figure. At the same time, since the simultaneous occurrence of SWAP and GC can cause serious stutters in JVM, we have to ask: where on earth is the memory?

To analyze this problem, it is important to understand the memory relationship between JVM and the operating system. Next, it mainly analyzes the memory relationship between Linux and JVM.

1. Linux and process memory model

JVM runs on Linux system as a process (Process). Understanding the memory relationship between Linux and process is the basis of understanding the relationship between JVM and Linux memory.

The following figure shows an overview of the relationship between memory at the hardware, system and process levels.

From a hardware point of view, the memory space of a Linux system consists of two parts: physical memory and SWAP (located on disk). Physical memory is the main memory area used when Linux is active; when physical memory is out of use, Linux will put some of the temporarily unused memory data into the SWAP on disk to free up more available memory space; and when you need to use the data located in SWAP, you must first swap it back into memory.

From the point of view of the Linux system, except for the BIN area of the boot system, the whole memory space is mainly divided into two parts: kernel memory (Kernel space) and user memory (User space).

Kernel memory is the memory space used by Linux itself, which is mainly used by program logic, such as program scheduling, memory allocation, connecting hardware resources and so on. User memory provides the main space for each process, and Linux provides the same virtual memory space for each process; this makes processes independent of each other and does not interfere with each other. The way to achieve this is to use virtual memory technology: give each process a certain amount of virtual memory space, and only when the virtual memory is actually used, the physical memory is allocated.

For 32 Linux systems, the virtual memory space of 3G is generally allocated as user space, and the virtual memory space of 4G is allocated as kernel space; the partition of 64-bit system is similar.

From the point of view of the process, the user memory (virtual memory space) that the process can directly access is divided into five parts: code area, data area, stack area, stack area, unused area. The machine code in which the application is stored in the code area, which cannot be modified during operation, has the characteristics of read-only and fixed size. The data area stores the global data, static data and some constant strings in the application, and its size is also fixed. Heap is the space dynamically applied by the program at run time, which belongs to the memory resources directly applied and released when the program is running. The stack area is used to store data such as incoming parameters, temporary variables, and return addresses of functions. An unused area is a preparatory area for allocating new memory space.

2. Process and JVM memory model

JVM is essentially a process, so its memory model also has the general characteristics of a process. However, JVM is not an ordinary process, and it has many new features in the memory model for two main reasons:

1. JVM transplants many things that originally belong to the category of operating system management to JVM, in order to reduce the number of system calls.

2. Java NIO, which aims to reduce the overhead of system calls used to read and write IO.

The comparison between JVM process and normal process memory model is shown in the following figure:

To be clear, this model is not an accurate model of JVM memory usage, but rather omits some (although important) internal details of JVM from an operating system point of view. The following explains the memory characteristics of JVM processes from two aspects: user memory and kernel memory.

1. User memory

The figure above highlights that the code and data areas of the JVM process model refer to the JVM itself, not the Java program. The normal process stack area is generally used only as a thread stack in JVM. The heap area of JVM is the most different from the normal process, which is described in detail below:

The first is the eternal generation. Permanent generation is essentially the code area and data area of Java programs. Classes (class) in Java programs are loaded into different data structures throughout the region, including constant pools, fields, method data, method bodies, constructors, and special methods, instance initialization, interface initialization, etc. This area is part of the heap for the operating system; for Java programs, it is the space for the program itself and static resources, allowing JVM to interpret and execute Java programs.

The second is the new generation and the old age. The new generation and the old era are the heap space really used by Java programs, which is mainly used for the storage of memory objects, but its management is essentially different from that of ordinary processes.

When an ordinary process allocates space to memory objects at run time, such as when C++ performs the new operation, it will trigger a system call to allocate memory space, which will be returned by the thread of the operating system after allocating space according to the size of the object; at the same time, when the program releases the object, such as C++ performs the delete operation, it will also trigger a system call to inform the operating system that the space occupied by the object has been reclaimed.

JVM uses memory differently from normal processes. JVM applies to the operating system for a whole memory area (the specific size can be adjusted in the JVM parameter) as the heap of Java programs (divided into new and old generations); when Java programs apply for memory space, such as performing new operations, JVM will allocate to Java programs according to the required size in this space, and Java programs are not responsible for informing JVM when the object's space can be freed. Garbage object memory space is reclaimed by JVM.

The advantages of JVM's memory management method are obvious, including: first, reducing the number of system calls, JVM does not need operating system intervention when allocating memory space to Java programs, and only needs to apply for memory from the operating system or notify recycling when the Java heap size changes, while ordinary programs need system calls to participate in each memory space allocation and recovery. Second, to reduce memory leaks, ordinary programs do not (or do not timely) inform the operating system of memory space release is one of the important reasons for memory leaks, and unified management by JVM can avoid memory leaks caused by programmers.

Finally, there is the unused area, which is the preparatory area for allocating new memory space. For ordinary processes, this area can be used for the request and release of heap and stack space, and this area is used for each heap memory allocation, so the size changes frequently; for JVM processes, this area is used when adjusting the heap size and thread stack, while the heap size is generally less adjusted, so the size is relatively stable. The operating system dynamically resizes this area, and this area is usually not allocated actual physical memory, but only allows the process to apply for heap or stack space in this area.

two。 Kernel memory

Applications usually do not deal directly with kernel memory, which is managed and used by the operating system; however, as Linux focuses on performance and improvements, some new features allow applications to use kernel memory or map to kernel space. It is under this background that Java NIO is born. It makes full use of the new features of Linux system and improves the IO performance of Java programs.

The figure above shows the distribution in the linux system in the kernel used by Java NIO. Nio buffer mainly includes: the ByteBuffer and Java programs used by nio when using all kinds of channel actively use ByteBuffer.allocateDirector to apply for assigned Buffer. In PageCache, the memory used by nio mainly includes: FileChannel.map mode to open files to occupy the Cache required by mapped, FileChannel.transferTo and FileChannel.transferFrom (marked nio file in the figure).

The usage of NIO Buffer and mapped can be monitored through JMX, as shown in the following figure. However, FileChannel is implemented using native PageCache through system calls, and the process is transparent to Java, and it is not possible to monitor the amount of memory used.

Linux and Java NIO open up space in kernel memory for programs to use, mainly to reduce unwanted replication, so as to reduce the overhead of IO operating system calls. For example, when sending data from a disk file to a network card, using the normal method and NIO, the data flow is compared as shown in the following figure:

Copying data between kernel memory and user memory is a resource-consuming and time-consuming thing, and from the figure above, we can see that 2 copies of data between kernel memory and user memory are reduced by NIO. This is one of the important mechanisms of Java NIO's high performance (the other is asynchronous non-blocking).

As can be seen from the above, kernel memory is also very important for the performance of Java programs, so when dividing the use of system memory, be sure to set aside some free space for the kernel.

III. Case study

1. Memory allocation problem

Through the above analysis, you can summarize the memory consumed by JVM by omitting the smaller areas:

JVM memory ≈ Java permanent generation + Java heap (new and old) + thread stack + Java NIO

Going back to the question raised at the beginning of the article, the original memory allocation is: 6g (java heap) + 600m (monitoring) + 800m (system), leaving about 600m of unallocated memory.

Now analyze the allocation of this 600m memory:

(1) the retention of Linux is about 200m, which is necessary for the normal operation of Linux.

(2) the number of threads in Java service is 160. the default thread stack size of JVM is 1m, so 160m memory is used.

(3) Java NIO buffer, which is found to occupy a maximum of 200m through JMX

(4) the Java service uses NIO to read and write a large number of files, which requires the use of PageCache. As analyzed above, it is not easy to estimate the size quantitatively.

The first three items add up to 560m, so it can be concluded that Linux physical memory is insufficient.

Careful people will find that two servers are given in the introduction, one SWAP takes up 2.16g at most and the other SWAP takes up 871 m at most; however, it seems that our memory gap is not that large. In fact, this is due to the simultaneous use of SWAP and GC, and as you can see from the figure below, the use of SWAP and long-term GC occur at the same time.

The simultaneous occurrence of SWAP and GC can lead to long GC time, severe JVM stutters, and, in extreme cases, service crashes. The reasons are as follows: when JVM performs GC, it needs to traverse the used memory of the corresponding heap partition; if part of the heap is swapped into SWAP when GC, when traversing to this part, it needs to be swapped back to memory, and because of insufficient memory space, the other part of the heap in memory needs to be changed to SWAP So in the process of traversing the heap partition, (in extreme cases) the entire heap partition is written to SWAP in turn. The recovery of SWAP by Linux is lagging behind, and we will see a large amount of SWAP usage.

The above problems can be solved by reducing the heap size or increasing physical memory.

Therefore, we draw a conclusion: Linux systems deploying Java services need to avoid the use of SWAP in memory allocation; how to allocate them needs to comprehensively consider the memory requirements of JVM for Java permanent generation, Java heap (new generation and old generation), thread stack and Java NIO in different scenarios.

two。 Memory leak problem

Another example is that for a server with 8g memory, the Linux uses 800m, the monitoring process uses 600m, and the heap size is set to 4g; there is about 2.5g of available memory in the system, but there is also a large amount of SWAP footprint.

The problem is analyzed as follows:

(1) in this scenario, the memory used by the Java permanent generation, Java heap (new generation and old generation) and thread stack is basically fixed, so the reason for taking up too much memory is located on the Java NIO.

(2) according to the previous model, the memory used by Java NIO is mainly distributed in the System area and PageCache area of Linux kernel memory. Looking at the monitoring records, as shown in the figure below, we can see that the PageCache shrank sharply before the occurrence of SWAP, that is, when the physical memory was out of use. Therefore, it is possible to locate a memory leak in the Java NIO Buffer in the System area.

(3) because the DirectByteBuffer of NIO needs to be reclaimed in the later stage of GC, the program that applies for DirectByteBuffer continuously needs to call System.gc () to avoid memory leakage of DirectByteBuffer referenced in old area caused by FullGC for a long time. After analyzing this, we can infer that there are two possible reasons: first, the Java program does not call System.gc () when necessary; second, System.gc () is disabled.

(4) the last step is to check the JVM startup parameters and the DirectByteBuffer usage of the Java program. In this example, look at the JVM startup parameters and find that enabling-XX:+DisableExplicitGC causes System.gc () to be disabled.

At this point, the study on "what is the memory relationship between Linux and JVM" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report