The purpose of this article is two: first, the code validates the contents of each run-time region described in the Java Virtual Machine specification, and second, when the actual memory overflow exception is encountered at work, it is possible to quickly determine which region's memory overflow based on the information of the exception, knowing what code might cause the memory overflow in these areas. , and what to do when these exceptions occur.
Java Heap Overflow
The Java heap is used to store object instances, as long as the objects are constantly created, and the GC roots to the object to avoid the garbage collection mechanism from clearing these objects, a memory overflow exception occurs when the number of objects reaches the maximum heap capacity limit. The parameter-xx:+heapdumponoutofmemoryerror allows the virtual machine to dump the current memory heap dump snapshot for subsequent analysis when a memory overflow exception occurs.
Public classheapoom{Static classoomobject{} Public Static voidMain (String[]args) {list
list
=NewArrayList
();
while(true) {List.add (Newoomobject ());}}} Run Result: java.lang.OutOfMemoryError:java heap Spacedumping heap tojava_pid3404.hprof ... Heap dump file created[22045981 bytes in0.663 secs]
To solve the anomaly in this area, the general approach is to analyze the dump-dump snapshot by means of a memory image analysis tool (such as Eclipse Memory Analyzer), with a focus on verifying that the objects in memory are necessary. That is, the first to know whether there is a memory leak (memories Leak) or memory overflow (Overflow).
In the case of a memory leak, further tools can be used to view the reference chain of the leaked object to the GC roots. You can then find out what paths are associated with GC roots and cause the garbage collector to not reclaim them automatically. By mastering the type information of the leaking object and the information of the GC roots reference chain, the location of the leaking code can be more accurately located.
If there is no leakage, in other words, the objects in memory must still exist, then you should check the virtual machine's heap parameters (-xmx and-XMS), compared with the machine physical memory to see if it can be larger, from the code to check if there are some object life cycle is too long, hold the state of the time is too long, Try to reduce the memory consumption during program run time.
Virtual machine stack and local method stack Overflow
Since the virtual machine stack and the local method stack are not distinguished in the hotspot virtual machine, the-xoss parameter (setting the local method stack size) exists for the hotspot, but is actually invalid, and the stack capacity is only set by the-XSS parameter. With regard to the virtual machine stack and the local method stack, two exceptions are described in the Java Virtual Machine specification:
A Stackoverflowerror exception is thrown if the thread requests a stack depth that is greater than the maximum allowed depth for the virtual machine .
Throws a OutOfMemoryError exception if the virtual machine cannot request enough memory space on the scale stack .
Here the exception is divided into two situations, seemingly more rigorous, but there are some overlapping places: when the stack space can not continue to allocate, whether the memory is too small, or has been used in the stack space is too large, it is essentially only two descriptions of the same thing.
Try the following two methods can not cause the virtual machine to produce OutOfMemoryError exception, the result of the attempt is to get Stackoverflowerror exception,
- Use the-XSS parameter to reduce the stack memory capacity. Result: Throws a Stackoverflowerror exception, and the output stack depth shrinks when the exception occurs.
- A large number of local variables are defined, increasing the length of the local variable table in this method frame. Result: The stack depth of the output is scaled down when the Stackoverflowerror exception is thrown.
Public classjavavmstacksof{Private intStacklength=1; Public void stackleak (){stacklength++; stackleak ();//Generate Stack frame } Public Static voidMain (String[]args)throwsthrowable{javavmstacksof oom=Newjavavmstacksof ();Try{oom.stackleak ();}Catch(Throwable e) {System.out.println ("Stack Length:" +oom.stacklength);Throwe;}}} Stack Length:2402Exceptioninthread"Main" java.lang.StackOverflowErrorat Org.fenixsoft.oom.VMStackSOF.leak (Vmstacksof.java:20) at Org.fenixsoft.oom.VMStackSOF.leak (Vmstacksof.java:21st) at Org.fenixsoft.oom.VMStackSOF.leak (Vmstacksof.java:21)
The experimental results show that, under a single thread , the virtual machine throws a Stackoverflowerror exception when memory cannot be allocated, either because the stack frame is too large or the VM stack is too small .
If the test is not limited to single-threaded, it is possible to create a memory overflow exception by constantly creating threads, but the resulting memory overflow exception is not associated with the stack space being large enough, or, to be precise, the larger the memory allocated for each thread's stack, the more likely it is to produce a memory overflow exception. In fact, the reason is not difficult to understand, the operating system allocated to each process memory is limited, such as 32-bit Windows limit to 2GB. The virtual machine provides parameters to control the maximum value of both the Java heap and the memory of the method area. The remaining memory is 2GB (operating system limit) minus XMX (maximum heap capacity), minus maxpermsize (maximum method area capacity), the program counter consumes little memory and can be ignored. If the memory consumed by the virtual machine process itself is not counted, the remaining memory is "partitioned" by the virtual machine stack and the local method stack. The larger the stack capacity each thread allocates, the less natural the number of threads can be established, and the easier it is to run out of memory when the thread is established. An error stack can be read when an Stackoverflowerror exception occurs, which is relatively easy to find. Also, if you use the virtual machine default parameters, the stack depth in most cases (because each method presses into the stack's frame size is not the same, so only in most cases) to achieve 1000~2000 completely no problem, for normal method calls (including recursion), this depth should be fully sufficient. However, if a memory overflow caused by multithreading has been established, in the case of not reducing the number of threads or replacing a 64-bit virtual machine, you can only exchange more threads by reducing the maximum heap and reducing the stack capacity. Without this experience, it would be hard to think of a way to solve a memory overflow by means of "reducing memory".
Public classjavavmstackoom{Private voidDontstop () { while(true) {}} Public voidStackleakbythread () { while(true) { thread thread =new Thread (NewRunnable () {@Override Public voidrun () {dontstop ();}); Thread.Start ();}} Public Static voidMain (String[]args)throwsthrowable{javavmstackoom oom=Newjavavmstackoom (); Oom.stackleakbythread ();}} Note In particular, if you want to try to run the above code, remember to save the current work first. Because Java threads are mapped to the operating system's kernel threads in virtual machines on the Windows platform, there is a greater risk of execution of the above code, which can cause the operating system to feign death. Run result: Exception in thread"Main" Java.lang.OutOfMemoryError: Unable to createNew nativeThread
Method area and run-time-constant pool overflow
Because the run-time-constant pool is part of the method area, overflow testing for these two areas is carried out together. As mentioned earlier, JDK 1.7 begins a gradual "go to a permanent generation", where you can test the code to see how it actually affects the program. String.intern () is a native method that, if the string constant pool already contains a string equal to this string object, returns a String object that represents the string in the pool, otherwise the string contained in this string object is added to the constant pool and returns this str A reference to the ING object. In JDK 1.6 and earlier, because the constant pool is allocated within the permanent generation, we can limit the method area size by-xx:permsize and-xx:maxpermsize, thereby indirectly limiting the capacity of the constant pool.
Public classruntimeconstantpooloom{ Public Static voidMain (String[]args) {//use list to keep constant pool references to avoid full GC recycle constant pool behaviorList <String> list=NewArrayList
();
//The 10MB permsize is enough to generate oom in the integer range. intI=0; while(true) {List.add (string.valueof (i++).Intern ());}} Running Result: Exceptioninthread"Main"Java.lang. Outofmemoryerror:permgen spaceat java.lang.String.intern (Native Method) at Org.fenixsoft.oom.RuntimeConstantPoolOOM.main (Runtimeconstantpooloom.java:
As you can see from the running results, the run-time pool overflow, followed by OutOfMemoryError, is "PermGen space", indicating that the run-time pool is part of the method area (the permanent generation in the Hotspot virtual machine). Running this program with JDK 1.7 does not result in the same results, and the while loop continues. A more interesting effect can be derived from the implementation of this constant pool of strings.
public class runtimeconstantpooloom{ public static void Main (String[]args) {String str1 =new StringBuilder ("Computer"). Append ("Software") Span style= "color: #000000"). toString (); System.out.println (Str1.intern () == STR1); String str2 =new StringBuilder ("ja"). Append ("va" ==STR2);}}
This code runs in JDK 1.6, gets two false, and runs in JDK 1.7 and gets a true and a false. The difference is due to the fact that in JDK 1.6, the Intern () method copies the first encountered string instance to the permanent generation, returns a reference to the string instance in the permanent generation, and the string instance created by StringBuilder on the Java heap, so it is not necessarily the same reference , False is returned. The Intern () implementation of JDK 1.7 (and some other virtual machines, such as JRockit) will no longer replicate the instance, just record the first occurrence of the instance reference in the constant pool . So the reference returned by intern () and the string instance created by StringBuilder are the same. The str2 comparison returns false because the string "Java" has already occurred before execution of Stringbuilder.tostring (), the string constant pool already has its reference, does not conform to the "first appearance" principle, and "Computer software" This string is the first occurrence and therefore returns true.
The method area is used to store information about class, such as its name, access modifier, Chang, field description, method description, and so on. The basic idea for testing these areas is that the runtime generates a large number of classes to fill the method area until it overflows.
Public classjavamethodareaoom{ Public Static voidMain (String[]args) { while(true) {Enhancer enhancer=Newenhancer (); Enhancer.setsuperclass (Oomobject.class); Enhancer.setusecache (false); Enhancer.setcallback (NewMethodinterceptor () { PublicObject Intercept (object Obj,method method,object[]args,methodproxy proxy)throwsthrowable{returnProxy.invokesuper (Obj,args);}} ); Enhancer.create ();}}Static classoomobject{}} Run Result: caused By:java.lang.OutOfMemoryError:PermGen spaceat Java.lang.ClassLoader.defineClass1 (Native Method) at Java.lang.ClassLoader.defineClassCond (Classloader.java:632) at Java.lang.ClassLoader.defineClass (Classloader.java:616) ...8 more
Method area Overflow is also a common memory overflow exception, a class to be collected by the garbage collector, the criterion is more stringent. In applications where a large number of classes are generated on a regular basis, special attention needs to be paid to the class recycling status. In addition to the above-mentioned programs using Cglib bytecode enhancement and dynamic language, there are also common: a large number of jsps or applications that generate JSP files dynamically (JSP needs to be compiled into Java class The first time it is run), OSGi-based applications (even the same class file, Loaded by different loaders will also be considered different classes), etc.
Native Direct Memory Overflow
The directmemory capacity can be specified by-xx:maxdirectmemorysize, and if not specified, the default is the same as the Java heap maximum (-xmx specified), bypassing the Directbytebuffer class, The memory allocation is obtained directly from the unsafe instance through reflection (the Getunsafe () method of the unsafe class restricts only the boot class loader from returning the instance, that is, the designer expects only classes in the Rt.jar to use the unsafe functionality). Because, although allocating memory using Directbytebuffer also throws a memory overflow exception, it throws an exception when it does not actually request to allocate memory to the operating system, but calculates that the memory cannot be allocated, and then throws the exception manually . The real way to apply for allocating memory is unsafe.allocatememory ().
Public classdirectmemoryoom{Private Static Finalint _1mb=1024*1024; Public Static voidMain (String[]args)throwsexception{Field Unsafefield=unsafe.class. Getdeclaredfields () [0]; Unsafefield.setaccessible (true); unsafe unsafe= (Unsafe) unsafefield.get (NULL); while(true) {unsafe.allocatememory (_1MB);}}} Running Result: Exceptioninthread"Main"Java.lang.OutOfMemoryErrorat sun.misc.Unsafe.allocateMemory (Native Method) at Org.fenixsoft.oom.DMOOM.main (Dmoom.java:20) A memory overflow caused by directmemory, an obvious feature of which is that there is no visible exception in the heap dump file,
If you find that the dump file is small after an oom, and the program uses NIO directly or indirectly, consider whether this is the cause.
OutOfMemoryError unusual Poor lift