In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
The main content of this article is to explain "what is the principle of Java memory exception". Interested friends may wish to take a look. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn "what is the principle of Java memory exception"?
1. The process of creating an object
With regard to the creation of objects, the first reaction is the new keyword, so this article will mainly explain the process of creating objects with the new keyword.
Student stu = new Student ("Zhang San", "18")
Take the above code as an example, the virtual machine will first check whether the Student class has been loaded, if not, first load the class into the method area, and then create a stu instance object based on the loaded Class class object. It should be noted that the amount of memory required for the stu object can be fully determined after the Student class is loaded. After the memory allocation is complete, the virtual machine needs to initialize the instance data portion of the allocated memory space to zero, which is why we do not need initialization to create a variable when writing Java code. Then, the virtual machine sets the object header of the object as necessary, such as which class the object belongs to, how to find the class metadata (Class object), the lock information of the object, the GC generation age and so on. When the object header information is set, the constructor of the class is called.
In fact, to tell you the truth, the process of creating objects by virtual machines is much more than that. I just explain the general context here to make it easy for you to understand.
2. Memory layout of the object
The instance data just mentioned may be a little strange to some partners. This section explains the memory layout of the object in detail. After the object is created, it can be roughly divided into the following parts:
Object head
Instance data
Align fill
Object header: the object header contains some necessary information when the object is running, such as GC generation information, lock information, hash code, pointer to Class meta-information, etc., of which Javaer is more useful is the lock information and the pointer to the Class object, on lock information, later have the opportunity to explain concurrent programming JUC and then expand, on the pointer to the Class object is actually very easy to understand. For example, in the Student example above, when we get the stu object, we call Class stuClass=stu.getClass ();, it is based on this pointer to get the Class class object stored in the method area of the Student class to which the stu object belongs. Although it was a bit of a mouthful, I pondered this sentence several times and should have made it clear.
Instance data: the instance data part is the valid information really stored by the object, which is the content of various types of fields defined in the program code.
Aligned padding: the virtual machine specification requires that the object size must be an integral multiple of 8 bytes. The alignment fill is actually used to complete the size of the object.
3. Access location of object
When it comes to object access, let's also take the example of the student above. When we get the stu object, we call stu.getName (); directly, which actually completes the access to the object. But the cumbersome point here is that although stu is usually regarded as an object, it is actually not accurate. Stu is just a variable, and the pointer to the object is stored in the variable (if you have done C or C++, you should be more aware of the concept of pointer). When we call stu.getName (), the virtual machine finds the object in the heap according to the pointer and gets the instance data name. It should be noted that when we call stu.getClass (), the virtual machine first locates to the object in the heap according to the stu pointer, and then goes to the method area to get the Class object twice according to the pointer to the meta-information of the Class class stored in the object header. The details are as follows:
4. Actual combat memory exception
Memory exception is a problem that we often encounter in our work, but it is obviously not enough to solve the problem only by increasing the memory parameters. We should locate the problem by some means, whether it is because of the parameter problem or the program problem (infinite creation, memory leak). After locating the problem, we can take the appropriate solution, instead of finding the relevant parameters as soon as the memory overflows.
Concept
Memory leak: an object in the code should have been reclaimed by the virtual machine, but it was not recycled because it had a GCRoot reference. The next article explains the concept of GCRoot.
Memory overflow: the virtual machine cannot continue to create new objects because there are too many non-recyclable objects in the heap.
Before analyzing the problem, I will tell you how to troubleshoot the memory overflow problem. When the memory overflow occurs, the JVM virtual machine exits, so how do we know all kinds of information about the JVM runtime? the Dump mechanism will help us. We can add the VM parameter-XX:+HeapDumpOnOutOfMemoryError to let the virtual machine generate the dump file when the memory overflow exception occurs, and then use external tools (the author uses VisualVM) to analyze the cause of the exception.
The following demonstrates the memory overflow and how to locate it in conjunction with the code in the following aspects:
Java heap memory exception
Java stack memory exception
Method area memory exception
Java heap memory exception / * * VM Args: / / these two parameters ensure that the allocable memory in the heap is fixed to 20m-Xms20m-Xmx20m / / file generation location, which is generated in a directory-XX:+HeapDumpOnOutOfMemoryError / / file generation location on the desktop, and is generated in a directory / / file generation location on the desktop. To do this, generate a directory on the desktop-XX:HeapDumpPath=/Users/zdy/Desktop/dump/ * / public class HeapOOM {/ / create an inner class for creating objects using static class OOMObject {} public static void main (String [] args) {List list = new ArrayList () / / create objects infinitely, and while (true) {list.add (new OOMObject ());} in the heap
After the code is generated by Run, the exception is as follows:
Java.lang.OutOfMemoryError: Java heap space
Dumping heap to / Users/zdy/Desktop/dump/java_pid1099.hprof...
You can see that the dump file is generated to the specified directory. And OutOfMemoryError was exposed, and it also told you which area had the problem: heap space.
Open the VisualVM tool to import the corresponding heapDump file (how to use it, please consult the relevant materials by yourself). The corresponding instructions are shown in the figure:
After analyzing the dump file, we can see that the class OOMObject has created 810326 instances. So can it not overflow? Then look in the code to find out where the class is new. Troubleshoot the problem. (our sample code doesn't need to be checked, the While loop is too ferocious.) after analyzing the dump file, we can see that the class OOMObject has created 810326 instances. So can it not overflow? Then look in the code to find out where the class is new. Troubleshoot the problem. (our sample code does not need to be checked, the While loop is too fierce.)
Java stack memory exception
To be honest, the probability of StackOverFlowError in the stack is as small as the probability of going to an Apple store to buy a phone and finding it is Android. Because the author really hasn't encountered it in a production environment, except for testing the sample code by himself. First, let's talk about the exception. As mentioned earlier, the process of method call is the process of method frame entering and leaving the virtual machine stack. Then there are two situations that can cause StackOverFlowError. When a method frame (such as 2m memory is needed) enters the virtual machine stack (for example, 1m memory is left), StackOverFlow is reported. Let's start with a concept, stack depth: refers to the method frame in which there is no stack in the virtual machine stack. The stack capacity of the virtual machine is controlled by the parameter-Xss. The stack capacity is artificially reduced by a piece of code, and then an exception is triggered by recursive calls.
/ * VM Args: / / set stack capacity to 160K, default 1m-Xss160k * / public class JavaVMStackSOF {private int stackLength = 1; public void stackLeak () {stackLength++; / / recursive call to trigger exception stackLeak ();} public static void main (String [] args) throws Throwable {JavaVMStackSOF oom = new JavaVMStackSOF (); try {oom.stackLeak () } catch (Throwable e) {System.out.println ("stack length:" + oom.stackLength); throw e;}
The results are as follows:
Stack length:751 Exception in thread "main"
Java.lang.StackOverflowError
As you can see, there are 751 recursive calls, and the stack capacity is not enough.
The default stack capacity can reach 1000-2000 depth in normal method calls, so general recursion is bearable. If StackOverflowError appears in your code, check the code first instead of changing the parameters.
Incidentally, many people in multithreaded development, when creating a lot of threads, it is easy to appear OOM (OutOfMemoryError), then you can reduce the maximum heap capacity, or stack capacity to solve the problem, this is why. Take a look at the following formula:
Number of threads * (maximum stack capacity) + maximum stack value + other memory (ignored or generally unchanged) = machine maximum memory
When the number of threads is relatively large, and the number of threads cannot be reduced through the business, then without changing the machine, you can only set the maximum stack capacity lower, or set the maximum stack value to smaller.
Method area memory exception
At this point, the author intended to write an example of infinite creation of dynamic proxy objects to demonstrate method zone overflows and avoid talking about the transition of memory area changes between JDK7 and JDK8, but think about it and make this piece clear from beginning to end. In the previous article, when talking about the method area in detail, the Java memory structure of the JVM series mentioned that the method area in the JDK7 environment includes (runtime constant pool). In fact, this is not accurate. Because starting from JDK7, the HotSpot team wants to start to "permanent generation", we first make clear a concept, method area and "permanent generation" (PermGen space) are two concepts, method area is JVM virtual machine specification, any virtual machine implementation (J9, etc.) can not lack this interval, and "permanent generation" is only an implementation of the other side law area of HotSpot. In order to list the knowledge points clearly, I still use the form of a list:
JDK7 (including JDK7) previously had a "permanent generation" (PermGen space) to implement the method area. But in JDK7, many things have been gradually moved out of the permanent generation in the implementation, such as symbolic references (Symbols) transferred to native heap, runtime constant pool (interned strings) transferred to java heap; class static variables (class statics) to java heap.
So that's why I said in the previous article that it was incorrect to have a runtime pool in the method area, because it has been moved to java heap
Before JDK7 (including 7), the size of the permanent generation can be controlled by-XX:PermSize-XX:MaxPermSize.
JDK8 formally removes the "permanent generation" and replaces it with Metaspace (metaspace) as the implementation of the legal zone in the JVM virtual machine specification.
The biggest difference between metaspace and permanent generation is that metaspace is not in the virtual machine, but uses local memory. Therefore, by default, the size of metaspace is limited only by local memory, but it can still be controlled by parameters:-XX:MetaspaceSize and-XX:MaxMetaspaceSize.
Method area and run-time pool OOM
The Java permanent generation is a part of non-heap memory, which is used to store class names, access modifiers, constant pools, field descriptions, method descriptions, and so on. Because the runtime pool is part of the method area, the runtime pool is also included. We can specify the memory size of this area with the jvm parameter-XX:PermSize=10M-XX:MaxPermSize=10M.-XX:PermSize defaults to 1x64 of physical memory, and-XX:MaxPermSize defaults to 1x4 of physical memory. The String.intern () method is a Native method that returns a String object representing the string in the pool if there is already a string in the constant pool; otherwise, the string contained in the String object is added to the constant pool and a reference to the String object is returned. In JDK 1.6 and previous versions, because the constant pool is allocated in the permanent generation, we can indirectly limit the capacity of the constant pool by limiting the size of the method area through-XX:PermSize and-XX:MaxPermSize. By running the code below java-XX:PermSize=8M-XX:MaxPermSize=8M RuntimeConstantPoolOom, we can simulate the memory overflow of a running constant pool:
Import java.util.ArrayList;import java.util.List;public class RuntimeConstantPoolOom {public static void main (String [] args) {List list = new ArrayList (); int I = 0; while (true) {list.add (String.valueOf (iTunes +). Intern ());}
The running results are as follows:
[root@9683817ada51 oom] #. / jdk1.6.0_45/bin/java-XX:PermSize=8m-XX:MaxPermSize=8m RuntimeConstantPoolOomException in thread "main" java.lang.OutOfMemoryError: PermGen space at java.lang.String.intern (Native Method) at RuntimeConstantPoolOom.main (RuntimeConstantPoolOom.java:9)
Another situation is that we can simulate the method area memory overflow by constantly loading class. This exception is simulated by bytecode techniques such as CGLIB in "in depth understanding java Virtual Machine". We use different classloader here (the same class is different in different classloader). The code is as follows
Import java.io.File;import java.net.MalformedURLException;import java.net.URL;import java.net.URLClassLoader;import java.util.HashSet;import java.util.Set;public class MethodAreaOom {public static void main (String [] args) throws MalformedURLException, ClassNotFoundException {Set > (); URL url = new File ("). ToURI (). ToURL (); URL [] urls = new URL [] {url}; while (true) {ClassLoader loader = new URLClassLoader (urls) Class loadClass = loader.loadClass (Object.class.getName ()); classes.add (loadClass) } [root@9683817ada51 oom] #. / jdk1.6.0_45/bin/java-XX:PermSize=2m-XX:MaxPermSize=2m MethodAreaOomError occurred during initialization of VMjava.lang.OutOfMemoryError: PermGen space at sun.net.www.ParseUtil. (ParseUtil.java:31) at sun.misc.Launcher.getFileURL (Launcher.java:476) at sun.misc.Launcher$ExtClassLoader.getExtURLs (Launcher.java:187) at sun.misc.Launcher$ExtClassLoader. (Launcher.java:158) at sun .misc.launcher $ExtClassLoader$1.run (Launcher.java:142) at java.security.AccessController.doPrivileged (Native Method) at sun.misc.Launcher$ExtClassLoader.getExtClassLoader (Launcher.java:135) at sun.misc.Launcher. (Launcher.java:55) at sun.misc.Launcher. (Launcher.java:43) at java.lang.ClassLoader.initSystemClassLoader (ClassLoader.java:1337) at java.lang.ClassLoader.getSystemClassLoader (ClassLoader.java:1319)
Running the above code on jdk1.8 will not cause an exception because jdk1.8 has removed the permanent generation, and of course-XX:PermSize=2m-XX:MaxPermSize=2m will also be ignored, as follows
[root@9683817ada51 oom] # java-XX:PermSize=2m-XX:MaxPermSize=2m MethodAreaOomJava HotSpot (TM) 64-Bit Server VM warning: ignoring option PermSize=2m; support was removed in 8.0Java HotSpot (TM) 64-Bit Server VM warning: ignoring option MaxPermSize=2m; support was removed in
Jdk1.8 uses metaspace (Metaspace) instead of permanent generation (PermSize), so we can specify the size of Metaspace in 1.8 to simulate the above situation.
[root@9683817ada51 oom] # java-XX:MetaspaceSize=2m-XX:MaxMetaspaceSize=2m RuntimeConstantPoolOomError occurred during initialization of VMjava.lang.OutOfMemoryError: Metaspace
In the JDK8 environment, an exception will be reported:
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
This is because the dynamic proxy class, that is, the Class object to Metaspace, is generated when the creation agent of CGLib is called, so an exception occurs in While.
Remind me: although we call it "heap Dump" every day, dump technology is not only effective for the "heap" area, but also for OOM, that is to say, no matter what area, all those who can report OOM errors can use dump technology to generate dump files for analysis.
In applications that often generate a large number of Class dynamically, we need to pay special attention to the recovery of classes. In addition to the CGLib technology in the example, there are also a large number of JSP, reflection, OSGI and so on. It is important to note that when such an exception occurs, you should know what went wrong, and then see whether to adjust the parameters or optimize at the code level.
Additional-direct memory exception
Direct memory exception is very rare, and the mechanism is very special, because direct memory is not allocated directly to the operating system, and the memory calculated is not enough to manually throw an exception, so when you find that your dump file is very small, and there is no obvious exception, just tell you OOM, you can consider whether your code is directly or indirectly using NIO resulting in a direct memory overflow.
Java memory leak
One of the important advantages of Java is that it automatically manages memory collection through the garbage collector (Garbage Collection,GC). Programmers do not need to call functions to free memory. As a result, many programmers believe that there is no memory leak in Java, or that even if there is a memory leak, it is not the responsibility of the program, but the problem of GC or JVM. In fact, this idea is not true, because Java also has memory leaks, but its performance is different from that of C++.
As more and more server programs adopt Java technology, such as JSP,Servlet, EJB and so on, server programs often run for a long time. In addition, the total amount of memory is very limited in many embedded systems. The problem of memory leakage becomes very critical, even if there is a small amount of leakage in each run, the system is in danger of crashing after a long run.
How does Java manage memory?
To determine if there is a memory leak in Java, we must first understand how Java manages memory. The memory management of Java is the problem of object allocation and release. In Java, programmers need to request memory space for each object (except the basic type) through the keyword new, and all objects allocate space in the Heap. In addition, the release of the object is determined and executed by the GC. In Java, the allocation of memory is done by the program, while the release of memory is done by GC. This two-line approach really simplifies the work of programmers. But at the same time, it also aggravates JVM's work. This is also one of the reasons why Java programs run slowly. Because, in order for GC to release objects correctly, GC must monitor the running status of each object, including object application, reference, referenced, assignment and so on. GC needs to monitor.
The purpose of monitoring the state of the object is to release the object in a more accurate and timely manner, and the fundamental principle of releasing the object is that the object is no longer referenced.
In order to better understand the working principle of GC, we can consider the object as the vertex of the directed graph and the reference relation as the directed edge of the graph, and the directed edge points from the referrer to the cited object. In addition, each thread object can be used as the starting vertex of a graph. For example, most programs start from the main process, so the graph is a root tree that starts with the vertex of the main process. In this digraph, objects whose root vertices are reachable are valid objects, and GC will not recycle these objects. If an object (connected subgraph) and this root vertex are not reachable (note that the graph is a directed graph), then we think that this (these) objects are no longer referenced and can be recycled by GC.
Below, let's give an example of how to use a directed graph to represent memory management. For each moment of the program, we have a directed graph showing the memory allocation of the JVM. The right picture below is a schematic diagram of the program running to line 6 on the left.
Java uses directed graph for memory management, which can eliminate the problem of reference loop, for example, there are three objects that reference each other, as long as they and the root process are unreachable, then GC can also recycle them. The advantage of this approach is that the precision of managing memory is high, but the efficiency is low. Another commonly used memory management technique is to use counters. For example, the COM model uses counters to manage components, which is less precise than a directed graph (it is difficult to deal with circular references), but it is very efficient.
What is a memory leak in Java?
Below, we can describe what a memory leak is. In Java, a memory leak means that there are some allocated objects, which have the following two characteristics: first, these objects are reachable, that is, in a directed graph, there are paths that can be connected to them; second, these objects are useless, that is, the program will never use these objects again. If the objects meet these two conditions, the objects can be determined as memory leaks in Java, these objects will not be recycled by GC, but it takes up memory.
In C++, the range of memory leaks is larger. Some objects are allocated memory space that is then unreachable, and because there is no GC in C++, this memory will never be recovered. In Java, these unreachable objects are recycled by GC, so programmers do not need to consider this part of the memory leak.
Through analysis, we know that programmers need to manage edges and vertices on their own for cantilever, while for Java programmers only need to manage edges (there is no need to manage vertex release). In this way, Java improves programming efficiency.
Therefore, from the above analysis, we know that there are memory leaks in Java, but the scope is smaller than C++. Because Java guarantees from the language that any object is reachable, all unreachable objects are managed by GC.
To programmers, GC is basically transparent and invisible. Although we only have a few functions that can access GC, such as the function System.gc () that runs GC, according to the Java language specification, this function does not guarantee that JVM's garbage collector will execute. Because different JVM implementers may use different algorithms to manage GC. Typically, threads in GC have a lower priority. There are also many strategies for JVM to call GC. Some start to work when memory usage reaches a certain level, some are executed regularly, some are slow execution of GC, and some are interrupted execution of GC. But generally speaking, we don't need to care about that. Unless in some specific situations, the execution of GC affects the performance of the application, for example, for real-time systems based on Web, such as online games, users do not want GC to suddenly interrupt the execution of the application and carry out garbage collection, then we need to adjust the parameters of GC so that GC can release memory in a gentle way, such as decomposing garbage collection into a series of small steps, which is supported by HotSpot JVM provided by Sun.
A simple example of a memory leak is given below. In this example, we looped through the Object object and put the requested object into a Vector. If we only release the reference itself, then the Vector still references the object, so the object is not recyclable to GC. Therefore, if an object has to be deleted from the Vector after it has been added to the Vector, the easiest way is to set the Vector object to null.
Vector v=new Vector (10); for (int item1)
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.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.