I remember during my internship and job search, an interviewer asked me a question about JVM tuning. It was like, “Do you know about JVM tuning?” I said I didn’t know much about it, and then the interviewer told me that your interviewer could learn about it, and the JVM would work better if adjusted. At the time, I didn’t understand why the JVM had to be tuned. I wondered if some of the parameters of the JVM were not set properly in the first place. But I think the JVM has been around for such a long time that it even had this problem in the first place? It hasn’t been resolved yet?
Is it possible that the poor performance of the JVM is not the fault of the JVM, but the poor quality of the code written by the programmers? Later I looked up some information: Does the average Java project require JVM tuning? In most cases, the JVM doesn’t need to be tuned, but sometimes programmers write programs that perform poorly, and the problem can’t all be blamed on the JVM.
Why learn about the JVM?
Locate the problem. It’s possible that the system isn’t performing well, but we won’t be able to figure out right away what’s wrong with the program, so we need to diagnose the JVM to see what’s wrong. To make a diagnosis, you must first understand the JVM. This article is not for those who are new to Java, and it can be confusing. So what do we know about the JVM? The first is the runtime zoning (to be distinguished from the JMM here), the second is the garbage collector and garbage collection algorithm, and the third is the JVM monitoring tool. By doing this, we can diagnose the JVM and locate problems.
In fact, I have written similar articles before, but this time I have made an overall discussion, which is more systematic.
The JVM runtime area
This is where my first blog discussed how the memory that the JVM requests from the operating system at runtime is divided into regions. It was this article:
- For JVMs, do you just know the heap and the stack?
In this post we’re going to talk about these concepts again, a little bit more systematically, a little bit more comprehensively, and when I watch the videos, I talk about heaps and stacks, but I’m going to look at it a little bit more closely and it’s not. For JVMs, there are HotSpot VM (Oracle), TaobaoVM (Taobao), Bisheng JDK (Huawei), etc. Many well-known computer manufacturers have their own customized JDK. Will these magic changes lead to different versions of the Java language? How does it affect Java write once and run everywhere features? In Java and the answer is not, there is a thing called the JVM specification, described above if you want to customize the JDK, magic to change the JVM, then also please in within the scope of this change, if wild magic to change the JDK, many mature open source framework on it may not be able to customize in your JDK run successfully, in order to ensure that all manufacturers custom JDK at the same time, Oracle has introduced the JVM specification. As long as you do not violate the specification, it does not affect the operation of bytecode. Outside of the specification, run your own free customization.
As I write here, I suddenly recall a question that one of my classmates asked me when I was in college. Why do I define an interface and let the class implement it, and then just write it in the class? I used is another example, introduced the use of the interface, if be the team to have a function that you complete, the function complete, with interface when the time comes somebody else just call your interface, don’t need to care about the specific implementation, it can effectively realize the decoupling, we can understand the interface as the specification, agreed when the time comes to implement this method. It’s true that interfaces are more like specifications, because you’re not writing the implementation class.
JVM specification address:
- https://docs.oracle.com/javas…
Now it’s 15, because the JDK is 15. This time we looked at JDK8, and to JDK11, the runtime area of the JVM was essentially unchanged. The same JVM specification also constrains run-time areas, like interfaces, and the corresponding virtual machine implements the specification, like implementation classes. The JVM specification specifies that the JVM runtime area has the following sections:
- The PC Register program counter
- Java Virtual Machine Stacks
- Heap Heap
- Method Area
- The run-time Constant Pool
- If Native Method Stacks can be improved, the latest JDK15 has the same feature, but there are still insufficient memory options available. In fact, there are adjustments, but the specification still divides the runtime zones into these zones. But different virtual machines seem to have different implementations, such as the most widely used Hotspot VM, tested in IDEA:
Test any main function and you’ll find that the console will output the following:
Heap Heap, we know, but what the hell is this Meta Space? Hotspot has added another piece to the runtime area required in the specification? No, it’s actually a method area implementation, and in HotSpot 1.6 the method area implementation is a permanent generation and is within the scope of garbage collection, as opposed to young and old generations. Version 1.7 moves the string constant pool to the heap. Version 1.8 removes the implementation of the permanent generation completely and uses Metaspace to implement the method area. The natural question then is why the Hotspot virtual machine has removed the permanent generation and implemented the method section instead of the MetaPace section. In fact, in the early days, there were many versions of the virtual machine, not 1.6, 1.7, 1.8, but various vendors’ implementations of the JVM. The most commonly used JVM is the one provided by Oracle. There’s JRockit, there’s the IBM JVM, there’s Apache Harmony, and on top of that, Google developed Dalvik.
Then Oracle bought Sun and began to unify the world, integrating Hotspot with JROKit. The integration must have been a combination of the best of both, and JROKit didn’t have this kind of implementation permanently, and it worked well. So what exactly is the method area used to store information? The JVM specification specifies that the Method area stores metadata for classes and methods, as well as pools of constants such as Class and Method. Whenever a class is first loaded, its metadata is placed in the method area.
Permanent generation size is limited, too much before 1.8 class loading, the permanent generation memory Settings May result in permanent generation permanent overflow, namely the evil Java. Lang. OutOfMemoryError: PermGen, we have to according to the actual situation to adjust the size of the permanent generation. In JRokit is not the concept of permanent generation, and running well, there won’t be annoying. Java lang. OutOfMemoryError: PermGen. inJEP 122: Remove the Permanent GenerationIt describes the reasons for removing the permanent generation and using the Meta Space instead. If you are interested, please refer to this draft. There is little information available about the metadata area, but only the official Java Virtual Machine specification and Oracle Blog describe it. The official description is as follows:
In JDK 8, classes metadata is now stored in the native heap and this space is called Metaspace.
The metadata area of the class is stored in the local heap. This area is called the meta space.
Local heap means to directly use the memory space provided by the operating system. The default size of the space is only limited by the local memory, roughly speaking, it is the amount of the local memory left. If the local memory is not enough, I will apply for it from the operating system (most operating systems have virtual memory). We can specify this with -XX: MaxMetaspacesize. In addition, the JVM dynamically resizes MaxMetaspacesize based on the runtime profile by default. If Metaspace’s space footprint reaches a set maximum, a GC is triggered to collect loaders for dead objects and classes. Depending on the nature of JDK 8, both G1 and CMS do a good job of collecting Metaspace sections (usually along with Full GC).
The heap is the main object of the garbage collector. Different garbage collectors divide the heap differently. In JDK8, add -XX:+PrintGCDetails.
Let’s introduce what the output means when -XX:+PrintGCDetails is added:
-
I’m PSYoungGen about Parallel Scavenge in my Young Generation.
-
The memory space claimed when an object is created takes precedence over this area
After garbage collection, the surviving objects in the Eden space will be moved To the Survivor space, which is divided into two areas, one is To Survivor and one is From Survivor. The two areas have the same space size. When the young generation triggers garbage collection, the surviving objects in the Eden Space are put into the empty survivor Space (usually to Space), and the objects in the other survivor Space (i.e. From Space) that cannot be collected are put into the To Space. Then the identities of “To Space” and “From Space” are switched. After the young generation triggers the GC, a tenuring threshold (the object is moved to the old age after the threshold is reached) and the sizes of the age zones are calculated and resized as appropriate.
Garbage collection is triggered when the Eden section is fully occupied, and it happens that the To section is not large enough To hold objects that are still alive after the Eden Space and From Space are garbage collected. This is something that you want To avoid From the JVM perspective by promotion thresholds, but it is not completely guaranteed. - from space
- to space
-
-
Paroldgen Parallel Old (a version of Parallel cavenge)
- object space
- Next let’s do the same test in JDK11 and add -XX:+PrintGCDetails to VM Options to see how the output is different from JDK8: From this output, we first see the state of the –XX:+PrintGCDetails parameter as obsolete in JDK 11, suggesting that we use -Xlog:gc* instead. Then I noticed that the output seemed to be completely different from JDK8 except for Metaspace. This is because by default JDK 11 uses a Garbage-First Garbage Collector called G1. G1 weakens the concept of generation and uses the concept of regionalization to manage memory. G1 divides the heap into equal-sized partitions: The old space is called the old space (this is my own name), Eden, Survivor, Humongous. Objects are allocated to this area when the size of an object is equal to half of the Region. The go belongs to the senior area). With this in mind, you can see the GC parameters that are printed under JDK 11. The default partition size is 1M. G1 is available in JDK6U14 and JDK7U4. In JDK8, you can specify the use of the G1 garbage collector with -XX:+UseG1GC.
To emphasize, some erroneous sources regarding heap, stack, and the like called JVM runtime zoning refer to it as the JMM Java Memory Model. Simply put, the Java Memory Model is a specification designed to solve the problems of concurrent programming across platforms.
To summarize
By now we have a clear understanding of the JVM runtime zone partition. Prior to JDK8, the JVM runtime zone was:
- The PC Register program counter
- Java Virtual Machine Stacks
- Permanent generation (Method Area)
- Run-time Constant Pool (in the permanent generation
- Native Method Stacks
-
Heap heap
- Young generation - Eden Space - To Space - From Space
- Old generation
- The permanent generation
After JDK8, the permanent generation was removed in favor of meta-space, and the memory region in the heap was partified depending on the chosen garbage collector, G1(JDK9 became the default garbage collector,) and ZGC(JDK 11 was introduced, The JDK15 default garbage collector, Shenandoah GC(the garbage collector introduced in JDK12), weakens the concept of generation and uses partitioning to manage memory. As of JDK 16, except for the three partitioned garbage collectors above, all garbage collectors use generational memory management.
The permanent generation is not in the heap, but is physically a contiguous memory with the heap.
After JDK8, if you choose generational garbage collector:
If you choose a partitioned garbage collector, such as G1, then the Java runtime partitioning looks like this:
The heap is the main area of concern for the garbage collector. Objects also allocate memory on the heap, which means they can also allocate memory outside the heap. It is not easy to explain this, but it is not the topic of this article.
- Are Java class objects allocated memory on the heap?
- Please, stop saying that Java objects allocate space on heap memory!
System.gc
Runs the garbage collector. Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects.
To call the garbage collector, calling this GC method means that the JVM will put some effort into collecting the garbage objects to reduce memory footprint, and when the method returns, the JVM will do its best to recover memory from the garbage objects.
Note the best effort here, which means doing nothing at all. The Hot Spot in this article, like other JVMs, by default executes GC immediately upon calling the method and waits for the GC to complete before returning the method. The exception to this rule is that System.gc() is not supported by ZGC, so it is useless to call System.gc(). As of JDK17(no proposal for a new garbage collector is seen in JDK17), other garbage collectors can trigger GC via System.gc().
The finalize () method
If an object implements the finalize() method, it will be called during the collection phase of the object. Note that the JVM only manages memory for us. Management includes allocation and release. As with other resources, the programmer has to release them himself. This is mainly to avoid leakage of the resources originally held by the object after it dies. Java provides Finalize mechanism so that users can register Finalize () as a callback method to customize the behavior of GC to clean up the object. In java.io.FileInputStream, we rewrite Finalize () to release the corresponding file handle resource. JDK 8 + marked this approach as obsolete, replaced by a Cleaner. For a discussion in this regard, please see:
-
The new version of Java will discard Object.Finalize () and add a new java.lang.Ref.Cleaner
Into the GC
When an object in the heap is deemed to be useless by the JVM, the JVM’s Garbage collector will remove the memory of the object that is deemed to be useless, cleaning up the instance data of the object. It can be seen from the above that garbage collection can be divided into two parts, one is how to define garbage, the other is how to collect garbage. If we will be the memory for a room, the user program can be understood as the users of the room, garbage collector is the clean room, inferred from the common sense, in when you were young, my mother give you clean the room, basic is suspended the right to use your room for a period of time, and then let you to use again, This is The stop-the-world of The Java World, where The JVM suspends The user thread during a certain phase of garbage collection and waits until The end of that phase before resuming The thread.
How to Define Garbage
Reference counting
Reference counting holds the number of references to an object by allocating a space in the object’s header. If the object is referenced by another object, its reference count is increased by 1, if the reference to the object is deleted, its reference count is decreased by 1, and when the reference count to the object is 0, the object is determined to be garbage.
Object object = new Object();
If object points to an object at address 0xAAFF666, then we say that the object stored in address 0xAAFF666 has a reference to object. Then set object to null. The object at 0xaaff666 is considered garbage. This algorithm spreads garbage collection over the entire application, rather than suspending the entire application while garbage collection is taking place until all objects in the heap are processed. Therefore, garbage collection using reference computing algorithms is not strictly a stop-the-world garbage collection mechanism. The main reason this algorithm was abandoned was because it couldn’t solve the circular reference problem, like the following:
public class GcDemo { public Object instance; public static void main(String[] args) { GcDemo a = new GcDemo(); GcDemo b = new GcDemo(); a.instance = b; b.instance = a; a = null; b = null; }}
Assume that a previously referred to address 0xFFFA and b referred to address 0xFFFB. If a is set to null, the instance of the object at 0xFFFB points to 0xFFFB, and the instance of the object at oxFFFB points to 0xFFFA. Like this:
From this, we draw out the garbage determination algorithm used in JAVA accessibility analysis algorithm.
Accessibility analysis algorithm
The basic idea of Reachability Analysis is to search down from these nodes through some objects of GC Roots as seven points. The path that the search takes is called the Reference Chain. When an object is not linked by any reference to the GC Roots node (that is, it is not reachable from the GC Roots node), the object is proved to be unavailable. Which objects can be used as GC Roots? In the Java language, there are several types of objects that can be used as GC Roots (not all of them, but see the various Root object types listed in the Eclipse heap memory analysis):
- Object referenced in the virtual stack (the local variable scale in the stack frame)
- Object referenced by a class static variable
- Object referenced by a class constant
- Object referenced in JNI’s native method stack
- Global objects in JNI
- The object used by the living thread
The basic unit of storage is the stack frame. Every time a method is called, a stack frame is put on the stack. We can think of this as a stack frame that stores the information necessary for the execution of the method.
Based on this thinking, the circular reference problem was solved, we know that the program was based on the method of the basic execution unit, a method is a basic execution unit, Java is also starting from the main method, we now come to combined with concrete example look at the accessibility analysis algorithm is how to solve the problem of circular references:
private static void testGC() {
GcDemo a = new GcDemo();
GcDemo b = new GcDemo();
a.instance = b;
b.instance = a;
}
After testGC() completes, the objects that a and b point to are no longer GC Roots, so they can be labeled garbage. So why can these objects be GC Roots, and what’s special about these objects? Because if these objects are labeled as garbage, garbage collector collection, then it will affect the normal execution of the program. When the JVM calls a method, it forms a stack frame for that method. If the object for that stack frame is recycled, then the method cannot be executed. The same is true for class static variables and constants, which are used at runtime by methods.
How to Recycle Garbage
Let’s say you’re rich and you don’t want to clean your house, so you hire a housekeeper to help you clean your house. If the housekeeper is more technical when it comes to picking up trash, mark the garbage area with stickers and then take out the garbage in a coordinated way. Then the housekeeping uses the mark-erase algorithm. The problems existing in the domestic is the space utilization rate is not high, the room has a lot of small space, and then you have a large object, oh, no, is a large furniture wants to moved in, you carefully calculated, found that these together enough free space, but the space is not connected together, can’t move the furniture to come in, so you have made a room.
To avoid this situation, you let the housekeeping in a clean strategy, domestic cut your room for the two areas, using only one piece at a time, and then use the need of that piece of garbage collection, garbage collection, the rest of the articles for daily use to move to another, so to buy other articles for daily use, avoids the clearly feel the room, But there’s just no room for awkward questions. But very quickly you find a new problem, the room utilization goes down, because your house is 160 square meters, and with this housekeeping strategy, you can only use 80 square meters. This is the tag copy algorithm.
Soon you get uncomfortable with this cleaning strategy, and you ask the housekeeper to rearrange the house after each cleaning. But you soon find that you are not comfortable. You love to be clean. You need to clean every hour because your daily necessities change location frequently and your housekeeping is slow. This housekeeping uses the mark-organize (also known as mark-compress) algorithm.
So the smart you soon began to tell the aunt, the room is divided, which area to carry out the marking – clear strategy, which area to carry out the marking – copy strategy, which area to carry out the marking – tidy strategy.
An introduction to the garbage collector
Prior to JDK 1.3, Serial GC was the only option, and the garbage collector was single-threaded, which meant that both the mark and sweep phases required the entire JVM to be suspended. This is also a generational garbage collector, with the new generation using tag copy and the old generation using tag collation algorithm. However, with the development of The Times, Serial GC no longer meets the needs of the server side. Java starts to convert Serial to parallel processing. Java starts to convert Serial to parallel processing, which is called Parnew (garbage collector, which is not recommended by JDK anymore). This garbage collector has less data. Parallel Scavenge and Parallel Old, the current garbage collectors in JDK 8, are for you if you care about throughput. The JVM provides two parameters that allow you to control throughput accurately:
- -XX:MaxGCPauseMillis: Controls the maximum garbage collection pause time, a number of milliseconds greater than 0
- -XX: GCTimeratio: Set the throughput size directly, an integer greater than 0 and less than 100. This is also a double-edged sword. The lower you set the throughput, the lower the garbage collection pause time of the JVM. Because the GC time reduction is used for the acquisition of the younger generation, if the tuning is too small, the collection frequency is greatly increased and the throughput is decreased. For a related discussion, see this article:
- The JVM’s Parallel Scavenge and Parallel Old garbage collector have been deprecated since JDK9 due to the fact that there are too many parameters in the garbage collector. With the theoretically better option G1), the CMS collector is a collector that aims to obtain the shortest collection pause time because the GC worker thread and the user thread can execute concurrently while the CMS collector is working, thus reducing the collection pause time. The CMS collector can only be used for older collections, and is based on tag scavenging algorithms.
Before G1, ZGC, and Shenandoah, assuming that the JDK’s default garbage collector does not meet our needs, we need to choose the corresponding garbage collector combination according to the business:
- Serial GC(different versions for different generations)
- PARNEW (tag copy) + CMS(tag cleanup) GC
- As a developer, my vision is that I can write code without having to pay attention to all these different metrics. Soon the appearance of G1, ZGC and Shenandoah met my needs, no need to choose, excellent performance (this seems to be a bit controversial, after all, there is no silver bullet, selection on demand is the right principle), fewer parameters. G1: Garbage First uses the concept of partitioning to manage heap memory. It is designed to “collect as much Garbage as possible First”, with the goal of minimizing the pauses caused by processing very large heaps. So instead of waiting for memory to run out (e.g. Serial, Parallel) or close to running out (e.g. CMS Garbage-Collector) to start garbage collection, G1 internally uses heuristic algorithms to find regions with high collection benefits in the old age for collection. ZGC: is a low-latency garbage collector from JDK11(Linux only). Its design goals include:
- The pause time should not exceed 10ms
- The pause time does not increase with the size of the heap or the size of the active object
-
Shenandoah GC supports 8MB-4TB heap (16TB in the future) : With the introduction of JDK 12, the throughput of ZGC decreased in order to pursue low pause, while the pause time of Shenandoah was similar to that of ZGC, the average pause time was 10ms, the average pause time of ZGC was 1ms, and the maximum pause time was not more than 10ms, but the throughput decreased.
Full GC and Mirror GC
The JVM’s action of reclaiming the memory of an object can be divided into two broad categories:
-
Partial GC: Schemas that do not collect the entire heap
-Young GC: Collect only Young generation GC -Old GC: Collect only Old generation GC. This pattern - Mixed GC: Collect the entire young generation and part of the old generation. Only G1 has this mode.
- Full GC: A pattern that collects the entire heap, including all parts of the young generation, the old generation, the permanent generation (which JDK8 removes if it exists), and so on. Major GC is usually the equivalent of Full GC, collecting the entire GC heap, but because HotSpot VM has evolved over the years, the terminology has become completely confusing. When someone asks you XX GC, be sure to ask what kind he is talking about.
GC trigger condition
As discussed above, all Young GC triggers are when the Eden section is full. The CMS GC only collects old ages, and the trigger condition is that the old age usage rate exceeds a certain value. The G1 trigger condition is when the heap usage ratio exceeds a certain value.
Reference types
In Java, where everything is an object-oriented world, there are two types of data, the primitive and the reference types. Reference types are the main focus of garbage memory collection. Reference type can also be divided into: strong reference type (Object Object = new Object(), SoftReference type (softreReference), WeakReference type (WeakReference), phantomReference type (phantomReference)
Strong reference
In general, the most common way we create objects in Java is as follows:
Object object = new Object();
Object stores the address of an object in the heap. We also call object pointing to new Object(). New Object() creates an Object that has a reference to it. We call this type a strong reference type. There are two general situations in which strongly referenced objects fail:
-
End of life cycle
That is, when it becomes clear that the object will no longer be used, the object will wait to be collected by GC. Many online sources will say that the JVM will never reclaim memory for objects that are strongly referenced, or created in a new way. If a method returns void, and the method is called over and over again, the JVM will be OOM and memory will not be freed. This does not happen with the JVM, because it is wrong to say that strongly referenced objects will not be reclaimed even if they appear in OOM. But the blog on the Internet is basically copy each other, a mistake, that is all wrong. Let’s verify the above statement:
public class JVMDemo { public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 5; i++) { testGC(); } System.out.println("--------------------------"); TimeUnit.SECONDS.sleep(10); List<String> list = new ArrayList<>(); } private static void testGC() throws InterruptedException { Byte[] bytes = new Byte[1024 * 1024 * 10]; }}
Let’s limit the heap memory with the JVM parameters. My computer is 16G, so I don’t limit the heap memory, so it may be difficult to run out of garbage collection.
- Add -XX:+PrintGCDetails to VM Options (to tell the JVM to print garbage collection information when garbage collection occurs) -XMX128M (maximum heap size available is 128M) -XMS64M (heap start memory is 64M), -XX:+ PrintheapatGC: The memory usage of the heap before print memory collection. Output: This is enough to disprove many bloggers’ claims that the JVM will never reclaim its memory for a strongly referenced type at any time. Some of you might also say, how can you prove that the GC information printed out is the result of the testGC() method? Quite simply, we can comment out calls to testGC() in the for loop. You will notice that the GC information will not be output. The moral of this story is that we should understand the combination of theory and practice, and that bloggers on the Internet should have the ability to discriminate among themselves, because they may be wrong. Because when to write the object to be recycled is also turned over a pile of data, found that there are different, there are a lot of problems. Fortunately, I finally found more reliable information:
- Java object life cycle
- Reference types, object accessibility, and collection handling in Java
-
The Truth About Garbage Collection
An overview of the life cycle of the object
Roughly speaking, the life of an object looks like this:
-
The Created stage
In response to new, memory is allocated on the heap and the variable is initialized.
-
In Use phase
Object = new Object(); Object = new Object(); The new object is held by the object.
-
Invisible phase
Even if a strong reference holds an object, but the reference is a local variable, it will enter this phase, which is a non-mandatory phase, like this:
private void run() { try { Object object = new Object(); TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } // Object is visible in the try. System.out.println(" Hello World "); System.out.println(" Hello World "); System.out.println(" Hello World "); }
-
Unreachable stage
An object in the unreachable phase is a state where the object is no longer reachable by any of the reference chains that are strongly referenced by GC Roots.
GC Roots will be analyzed in conjunction with accessibility algorithms (which solve the problem of how to determine if an object is garbage). -
Collected phase
An object enters the “collection phase” when the garbage collector finds that the object is already in an unreachable phase and the garbage collector is ready to reallocate the memory space of the object. If the object has already overridden the finalize() method, and it has not been executed before, then the operation of the method is performed. If the Finalize method has already been executed, the object will go directly to the finalization phase. The Finalize () method can be seen as a self-help method to some extent, although in some ways this self-help method is bad and has some problems. In JDK8 + versions, the method has been marked as obsolete.
-
The end of the royal career
When an object is still unreachable after the finalize() method is executed, the object enters the finalization phase. At this stage, wait for the garbage collector to reclaim the object space.
Note that the object failed to save itself by waiting to be collected by the garbage collector. -
Deallocated reallocation stage
This is the final phase of garbage collection. If the object is judged unreachable after the previous phase, then the memory occupied by the object is candidate for reallocation. Whether the memory space is emptied or reallocated, and when this happens, depends on the specific virtual machine implementation.
With the Servlet lifecycle, the thread lifecycle, and the class lifecycle, there are just four of them, which can be called the four life cycles of Java.Soft references
Depending on the JVM memory condition: if there is sufficient memory, the GC will not arbitrarily reclaim soft reference objects; If the JVM runs out of memory, the GC will proactively reclaim soft reference objects. Often used as a cache. How much memory is not enough? Is there a parameter to control it? As we have seen above, different garbage collections trigger garbage collections at different times. For example, when I was using the JDK’s default Parallel Scavenge test, I often ran out of memory and the soft references were not recycled. The JVM is running out of memory and the GC is actively collecting soft reference objects. I wonder if this is due to multi-threading. The garbage collector thread has already crashed the JVM before the garbage collector thread has started working. Then I flipped through Softrefeence’s notes:
All soft references to softly-reachable objects are guaranteed to have been cleared before the virtual machine throws an OutOfMemoryError. Otherwise no constraints are placed upon the time at which a soft reference will be cleared or the order in which a set of such references to different objects will be cleared. Virtual machine implementations are, however, encouraged to bias against clearing recently-created or recently-used soft references.
Before OOM occurs, the JVM guarantees that all soft reference objects will be cleared, but there is no restriction on which soft reference objects the virtual machine will recycle or in what order. Virtual machine implementations generally tend to clear softreferences that are newly created or newly used.
After looking up some information, it is found that insufficient memory is not only related to available memory, but also related to time. The JVM website I look up to such a parameter: – XX: SoftRefLRUPolicyMSPerMB. Parameter description is as follows:
Soft references are kept alive longer in the server virtual machine than in the client. The rate of clearing can be controlled with the command line option -XX:SoftRefLRUPolicyMSPerMB=<N>, which specifies the number of milliseconds a soft reference will be kept alive (once it is no longer strongly reachable) for each megabyte of free space in the heap. The default value is 1000 ms per megabyte, which means that a soft reference will survive (after the last strong reference to the object has been collected) for 1 second for each megabyte of free space in the heap. Note that this is an approximate figure since soft references are cleared only during garbage collection, which may occur sporadically.
Soft references in the JVM server mode of survival time is longer than the survival time of the client, can clear speed – XX: SoftRefLRUPolicyMSPerMB
This parameter specifies how long each MB of memory soft reference will live in the heap, in milliseconds. The default parameter is 1 second,
SoftReference overview:
The get method is used to get the corresponding reference object.
Example:
class SoftObject { } public class SoftReferenceDemo { public static void main(String[] args) { SoftReference<SoftObject> softRef = new SoftReference<>(new SoftObject()); List<Byte[]> byteList = new ArrayList<>(); While (true) {if (softref.get () == null) {system.out. println(" Soft reference object has been reclaimed.....") {if (softref.get () == null) {system.out. println(" Soft reference object has been reclaimed....."); ); System.exit(0); } else {System.out.println(" fill... "); ); byteList.add(new Byte[1024 * 1024]); }}}}
The IDEA is specified in the VM Options, – XX: SoftRefLRUPolicyMSPerMB = 0. Then there it is: the soft reference object has been reclaimed…… Note that memory is limited by -xms and -xmx.
A weak reference
The corresponding class is WeakReference, which is characterized by the fact that as long as GC is executed, the WeakReference will be recovered.
public class WeakReferenceDemo { public static void main(String[] args) throws InterruptedException { WeakReference<MyObject> weakReference = new WeakReference<>(new MyObject()); System.out.println(weakReference.get() == null ? "Has been recycled" : "is not recycled "); System.gc(); // Let the GC thread execute TimeUnit.Seconds.Sleep (5); System.out.println(weakReference.get() == null ? "Has been recycled" : "is not recycled "); }}
Output results:
Phantom reference
Corresponding PhantomReference classes, generally not used alone, generally and reference queue (ava) lang. Ref. ReferenceQueue) are used together.
When the GC is collecting an object, if it finds that the object still has a dummy reference, it puts the dummy reference on the reference queue, and then (after the dummy reference is dequeued) it returns the object. Therefore, we can use virtual references to perform some additional operations on the object before it is GC.
Sample code:
class MyObject{ } public class PhantomReferenceDemo { public static void main(String[] args) throws InterruptedException { MyObject myObject = new MyObject(); ReferenceQueue<MyObject> myObjectReferenceQueue = new ReferenceQueue<>(); / / reference object + reference queue PhantomReference < MyObject > PhantomReference = new PhantomReference < > (MyObject, myObjectReferenceQueue); myObject = null; // Let the GC execute System.gc(); TimeUnit.MILLISECONDS.sleep(1000); System. Out.println (" GC to perform..." ); / /, and print the corresponding reference object System. Out. The println (myObjectReferenceQueue. The poll ()); }}
When the object referenced by the virtual reference implements the Finalize method, the queuing time will be delayed.
When we talk about JVM tuning
After the above discussion, we can say that the JVM tuning, I originally had some problems with the understanding of the tuning, I thought that the tuning was good in the beginning, after tuning to run better. But the real situation is that it doesn’t run as well as it would like, and then look at what’s wrong with the JVM. In general, it’s not the garbage collection of the JVM, it’s the writing of the program. We diagnose the JVM and see where the doctor is. However, there are also special cases, that is, really need to adjust (the field of big data needs this), but also can not blind adjustment, we need to analyze the operation of the program, comprehensive give the best parameters, but have not understood the situation began to jump, all called blind adjustment.
A memory leak
I used to think of a memory leak as something like C++, with a destructor called twice, called a memory leak. I don’t understand why Java still has memory leaks. In fact, a memory leak is defined as:
A Memory Leak is a situation where the dynamically allocated heap Memory in a program is not released or cannot be released due to some reason, resulting in a waste of system Memory, resulting in a program running slowly or even a system crash and other serious consequences. — Baidu Baike
A typical scenario is if you have a large table, do a full table scan, go back to Java and generate a large object, and generate it frequently. After large objects are generated (garbage collectors other than ZGC, Shenandoah GC, and G1 take time), a memory leak occurs when a GC is triggered if memory runs out.
JVM monitoring tool -MAT
Java has been around for a long time, and there are many diagnostic tools like JVisualVM, JConsole, and so on, but I’ll just cover the simple, easy-to-use, and free ones. The Eclipse MAT, my personal favorite JVM memory analysis tool, easily diagnoses memory leaks.
The download address is as follows:
https://www.eclipse.org/downl…
After downloading:
Typically, we export dump files to the VM with the suffix hprof for analysis.
Jmap-dump :format=b,file= file location Process No
Then we will launch the export HPROF file and import the MAT for analysis.
To summarize
This is an overview of the JVM series, covering the whole world. Basically around JVM tuning, I feel that JVM tuning is like a medical treatment for the JVM, with the dump tool being the output report and the MAT being the analysis tool.
The resources
- Delve into the JVM road Metaspace |
- Java 8: From PermGen to Metaspace
- What is the difference between a Major GC and a Full GC? What about the trigger?
- Ali P8 Technical Officer: As a Java developer, it is really important to understand the JVM’s GC logs
- Why use a single-threaded Full GC when Concurrent Mode failure occurs in a CMS GC?
- Look at the default garbage collector JDK1.8 used by the JVM
- JVM memory regions (Eden Space, Survivor Space, Old Gen, Code Cache, and Perm Gen)
- GC(Allocation Failure) triggers some sorting of JVM knowledge points
- What happens when the Eden section of the JVM is full and the S0 section is full and there are still living objects in the Eden section that are not copied?
- Seven JVM garbage collector features, advantages, disadvantages, and usage scenarios
- Some key technologies for the Java Hotspot G1 GC
- Possibly the most comprehensive G1 study note
- Java HotSpot VM Options
- The JVM parameter
- JDK11 official documentation
- G1: One Garbage Collector To Rule Them All
- 1. Tuning Garbage Ragnar garbage collector
- Frontier practice: How does the garbage collector evolve? Ali technology
- Shenandoah GC: A new concurrent compressed garbage collector from JDK12
- Using MAT to Analyze JVM Memory Problems from Beginner to Master (Part II)
- MAT From Beginner to Master (1)
- The interviewer why use yuan | JVM space to replace the permanent generation?
- JVM commonly used memory parameter configuration
- JVM actual combat: GC log parsing
- What is the meaning of the independence of the JVM parameter XMS from XMX, and are there any pitfalls if you force XMS = XMX?
- 2021-2-28: What exactly happens after calling System.gc()?
- The System.gc() call is not quite the same as what I see in the Java book, is I wrong?
- Java object life cycle
- Garbage Collection Roots
- WeakReference vs. SoftReference
- JVM G1 (Garbage-First Garbage Collector) Collector profile
- Java JVM Stack Frame
- Classic garbage collector
- How to check the server default garbage collector is which? How do I configure the garbage collection collector on a production environment? What is your understanding of garbage collector?
- Use the Memory Analyse Tool to analyze Memory spills.
- The JVM memory analysis tool MAT and its practice (Suggested Collection)
- Shenandoah GC: A new concurrent compressed garbage collector from JDK12
- How to evaluate the Shenandoah GC?