Preface
In using memory analyzer tool (MAT) to analyze memory leaks (I), I introduced the causes and consequences of memory leaks. This article describes how mat analyzes the root cause of Leakage Based on heap dump. Because the test examples may be too simple and it is easy to find out the problem, I look forward to taking this as an example.
At first I had to talk about classloader. In essence, its job is to read the class files on the disk into the memory, and then call Java. lang. classloader. the defineclass method tells the system to process the memory image into a valid bytecode. Java provides the abstract class classloader. All user-defined class loaders instantiate the class of the class loader. System Class Loader loads the user class by default without specifying the loader. in Sun Java 1.5, sun. Misc. launcher $ appclassloader is used. For more details, see the following documents.
Prepare heap dump
Please refer to the pilot class below, which is not special.
/**
* Pilot class
* @ Author Rosen Jiang
*/
Package org. rosenjiang. Bo;
Public class pilot {
String name;
Int age;
Public pilot (string a, int B ){
Name =;
Age = B;
}
}
Then let's look at the oomheaptest class, how it breaks through the heap dump.
/**
* Oomheaptest class
* @ Author Rosen Jiang
*/
Package org. rosenjiang. test;
Import java. util. date;
Import java. util. hashmap;
Import java. util. Map;
Import org. rosenjiang. Bo. Pilot;
Public class oomheaptest {
Public static void main (string [] ARGs ){
OOM ();
}
Private Static void OOM (){
Map <string, pilot> map = new hashmap <string, pilot> ();
Object [] array = new object [1000000];
For (INT I = 0; I <1000000; I ++ ){
String d = new date (). tostring ();
Pilot P = new pilot (D, I );
Map. Put (I + "Rosen Jiang", P );
Array [I] = P;
}
}
}
Yes. Many pilot class instances are constructed above and put in arrays and maps. Because it is strong ref, GC naturally does not recycle these objects and keeps them in heap until the overflow occurs. Of course, you must configure the VM parameter-XX: + heapdumponoutofmemoryerror in eclipse before running. Okay. After a while, the memory overflows and the console displays the following information.
Java. Lang. outofmemoryerror: Java heap Space
Dumping heap to java_pid3600.hprof
Heap dump file created [78233961 bytes in 1.995 secs]
Exception in thread "Main" Java. Lang. outofmemoryerror: Java heap Space
Java_pid3600.hprof is both heap dump and can be found in the project root directory where the oomheaptest class is located.
Mat Installation
In two cases, You have to install mat with heap dump. You can choose the appropriate installation method at http://www.eclipse.org/mat/downloads.php. After the installation is complete, switch to the memory analyzer view. There is the open heap dump button in the upper left corner of eclipse. Find and open the java_pid3600.hprof file in the path just described. It takes some time to parse the hprof file, and a wizard will pop up. Simply finish. You will see the interface shown later.
The mat tool analyzes heap dump and intuitively shows a pie chart on the interface. The dark area of this figure is suspected to have memory leakage. The entire heap memory is only 64 MB, the dark area accounts for 99.5%. Next is a brief description, which tells us that the main thread occupies a large amount of memory and clearly states that the "Java. lang. thread "the instance has memory aggregation, and the keyword" Java. lang. thread. Therefore, the mat illustrates the problem in two simple sentences, even if the user has no experience in dealing with memory problems. There is also a "details" link below. Before you click it, consider the following question: Why is the object instance clustered in the memory? Why is it alive (not GC )? Yes -- strong
Ref, so try again.
After clicking the "details" link, in addition to the description displayed on the previous page, there are also the shortest paths to the accumulation point and accumulated objects sections, which describe the shortest path from GC root to clustering point, and complete reference chain. Observe the accumulated Objects section, Java. util. hashmap and Java. lang. the retained heap (size) of the object [1000000] instance is the largest. in the previous article, we know that retained heap indicates that
The shallow heap (size) sum of other class instances that can be collected by chain, so it is obvious that class instances are clustered in the hashmap and object arrays. Here we find an interesting phenomenon, that is, the shallow heap and retained heap of the object array are the same. According to the Article shallow and retained sizes, we can see that the shallow heap of the array and the general object (non-array) different, depending on the length of the array and the type of the elements in it, evaluate shallow for the Array
Heap, that is, the sum of shallow heap of all objects in the array set. Okay. Let's take a look at Org. rosenjiang. bo. why is the shallow heap of the pilot object instance 16? Because the object header is 8 bytes, the member variable Int Is 4 bytes, and the string reference is 4 bytes, the total length is 16 bytes.
Next, let's look at the accumulated objects by class area. As the name suggests, we can find the Class Name of the clustered object instance. Org. rosenjiang. Bo. Pilot class toutiao.com was instantiated for 290,325 times and then returned to see the program. I admit that I did it on purpose. There are also many useful reports to help you analyze problems, but the examples in this article are too simple to use. If it is useful in the future, you must write a detailed description in the article.
Perm Gen again
As we know in the previous article, Perm gen is a heterogeneous entity that stores class and method data (related to Class Loader) and interned strings (string resident ). The heap dump does not contain too much perm Gen information. So we can solve the problem with a small amount of information.
Look at the following code and use interned strings to break perm Gen.
/**
* Oompermtest class
* @ Author Rosen Jiang
*/
Package org. rosenjiang. test;
Public class oompermtest {
Public static void main (string [] ARGs ){
OOM ();
}
Private Static void OOM (){
Object [] array = new object [10000000];
For (INT I = 0; I <10000000; I ++ ){
String d = string. valueof (I). Intern ();
Array [I] = D;
}
}
}
The console prints the following information and imports the java_pid1824.hprof file to the mat. In fact, the situation seen in mat should be similar to "outofmemoryerror: Java heap space" (using arrays), because heap dump does not contain any information about interned strings. It should be emphasized here that you should pay more attention when using the intern () method.
Java. Lang. outofmemoryerror: permgen Space
Dumping heap to java_pid1824.hprof
Heap dump file created [121273334 bytes in 2.845 secs]
Exception in thread "Main" Java. Lang. outofmemoryerror: permgen Space
I am thinking about how to break down the class loader. After trying, we found that we can use ASM to dynamically generate classes to achieve our goal. ASM (http://asm.objectweb.org) is mainly used to process compiled classes (Compiled class), can generate, convert and analyze compiled classes (one of the functions is to implement dynamic proxy ), moreover, it runs fast and small enough and provides comprehensive documentation. ASM provides core APIs and tree APIs. The former is event-based and the latter is object-based. It is similar to the XML-based Sax and Dom parsing, but the performance of tree APIS is compromised. Since ASM is used below, we have to explain the structure of the compiled class here, including:
1. modifier (such as public and private), Class Name, parent class name, interface, and annotation section.
2. class member variable declaration, including modifier, name, type, and annotation for each member.
3. description of methods and constructor, including modifier, name, return and input parameter type, and annotation. Of course, the specific Java bytecode of these methods or constructor is also included.
4. In the constant pool section, the constant pool is an array containing numbers, strings, and type constants in the class.
The difference between the compiled class and the original class source code is that the compiled class only contains the class itself, and the internal class does not appear in the compiled class, but generates another compiled class file. Second, the compiled class does not contain comments. Third, the compiled class does not contain the package and import sections.
Here we also need to talk about the description of the compiled class on the Java type. The original type is represented by a single uppercase letter, Z represents Boolean, c Represents char, B Represents byte, s represents short, I represents int, f Represents float, j Represents long, D represents double; the description of the class type is represented by the internal name (internal name) plus the prefix L and the semicolon following it. The so-called internal name is the representation with the full-outsourcing path, for example, the internal name of string is Java/lang/string. For the array type, use square brackets to add the data element type. Finally, the description of the method is represented by parentheses. If the return value is void, it is represented by V. For details, refer.
The following code uses the ASM core API. Note that the classvisitor interface is the core and fieldvisitor and methodvisitor are both auxiliary interfaces. Classvisitor should be called in this way: Visit visitsource? Visitouterclass? (Visitannotation | visitattribute) * (visitinnerclass | visitfield | visitmethod) * visitend. That is to say, the visit method must be called first, then the visitsource method can be called at most once, and then the visitouterclass method can be called at most once, and then the visitannotation and visitattribute methods can be called multiple times, finally, the visitinnerclass, visitfield, and visitmethod methods are called multiple times. Call the visitend method as the end after the call.
Note that the classwriter class implements the classvisitor interface. You can use the tobytearray method to directly construct the compiled class into a binary form. Because we want to dynamically generate subclasses, we are only interested in classwriter. The first is the abstract class prototype:
/**
* @ Author Rosen Jiang
* Myabsclass class
*/
Package org. rosenjiang. test;
Public abstract class myabsclass {
Int less =-1;
Int equal = 0;
Int greater = 1;
Abstract int absto (Object O );
}
The second is the custom class loader, which is actually impossible. The defineclass method of classloader is protected. to load the class in the byte array form (because it is tobytearray), you only need to inherit the class and implement it by yourself.
/**
* @ Author Rosen Jiang
* Myclassloader class
*/
Package org. rosenjiang. test;
Public class myclassloader extends classloader {
Public class defineclass (string name, byte [] B ){
Return defineclass (name, B, 0, B. Length );
}
}
The last is the test class.
/**
* @ Author Rosen Jiang
* Oompermtest class
*/
Package org. rosenjiang. test;
Import java. util. arraylist;
Import java. util. List;
Import org. objectweb. ASM. classwriter;
Import org. objectweb. ASM. Opcodes;
Public class oompermtest {
Public static void main (string [] ARGs ){
Oompermtest o = new oompermtest ();
O. OOM ();
}
Private void OOM (){
Try {
Classwriter CW = new classwriter (0 );
CW. Visit (Opcodes. vswitches 5, Opcodes. acc_public + Opcodes. acc_abstract,
"Org/rosenjiang/test/myabsclass", null, "Java/lang/object ",
New String [] {});
CW. visitfield (Opcodes. acc_public + Opcodes. acc_final + Opcodes. acc_static, "less", "I ",
Null, new INTEGER (-1). visitend ();
CW. visitfield (Opcodes. acc_public + Opcodes. acc_final + Opcodes. acc_static, "equal", "I ",
Null, new INTEGER (0). visitend ();
CW. visitfield (Opcodes. acc_public + Opcodes. acc_final + Opcodes. acc_static, "Greater", "I ",
Null, new INTEGER (1). visitend ();
CW. visitmethod (Opcodes. acc_public + Opcodes. acc_abstract, "absto ",
"(Ljava/lang/object;) I", null, null). visitend ();
CW. visitend ();
Byte [] B = CW. tobytearray ();
List <classloader> classloaders = new arraylist <classloader> ();
While (true ){
Myclassloader classloader = new myclassloader ();
Classloader. defineclass ("org. rosenjiang. Test. myabsclass", B );
Classloaders. Add (classloader );
}
} Catch (exception e ){
E. printstacktrace ();
}
}
}
In a short time, the console reports an error.
Java. Lang. outofmemoryerror: permgen Space
Dumping heap to java_pid3023.hprof
Heap dump file created [92593641 bytes in 2.405 secs]
Exception in thread "Main" Java. Lang. outofmemoryerror: permgen Space
Open the java_pid3023.hprof file. Note the classes: 88.1k and Class Loader: 87.7k sections. From this point, we can see that Class Loader loads a large number of classes.
For further analysis, click the button circled in the red box and select Java basics -- class loader explorer. The page shown is displayed. The first column is the name of the Class Loader, and the second column is the number of defined classes, now let's talk about the defined and loaded classes. When the class needs to be loaded, the corresponding class loader first delegates the request to the parent class loader, only when the parent class loader fails to be loaded will the Class Loader define and load the class by itself. This is the structure of Java's "parent-parent delegate loading chain"; the third column is the class
The number of instances of the class loaded by loader.
In the class loader explorer, we can see whether the class loader has loaded too many classes. In addition, the duplicate classes function can also help analyze duplicate classes, so it is no longer here. It is certain that myabsclass has been repeatedly loaded for n times.
Last
In fact, the mat tool is very powerful. The above example code cannot use other analysis functions of mat at all, so we will not describe it any more. In fact, for Oom, there are more than two overflow errors I have listed, but I want to say that for Perm Gen, if the problem cannot be found, we recommend that you use the-verbose parameter of JVM. This parameter prints logs in the background and can be used to check which class loader has loaded the classes. For example: "[loaded Org. rosenjiang. test. myabsclass from Org. rosenjiang. test. myclassloader] ".
The full text is complete.
References
Memoryanalyzer blog
Java class loader Architecture
Classloader
Original Author: Rosen Jiang Source:
Http://www.blogjava.net/rosen