Enjoy Sharing Technology!

Software,Develope,Devops, Security,TroubleShooting

Wednesday, November 17, 2021

OutOfMemoryError(3): Permgen space

 A series of articles on this topic:

Note: Permgen (permanent generation) belongs to the concept of JDK1.7 and previous versions; in order to adapt to the development of Java programs, JDK8 and later versions use less restrictive MetaSpace instead

JVM limits the maximum memory usage of Java programs, which can be configured through startup parameters. The heap memory of Java is divided into multiple areas, as shown in the following figure:



The maximum value of these areas is specified by the JVM startup parameters -Xmx and -XX:MaxPermSize. If not explicitly specified, it is determined according to the operating system platform and the size of the physical memory.

java.lang.OutOfMemoryError: PermGen space error message means: Permanent Generation (Permanent Generation) memory area is full.

Cause Analysis

Let's first look at what PermGen is used for.

In JDK1.7 and earlier versions, permanent generation is mainly used to store class definitions loaded/cached in memory, including class names, fields, methods and bytecodes (method bytecode); and constant pool information; classes associated with object arrays/type arrays, and class information optimized by the JIT compiler.

It is easy to see that the usage of PermGen is related to the number/size of classes loaded into memory by the JVM. It can be said that the main reason for java.lang.OutOfMemoryError: PermGen space is that the number of classes loaded into the memory is too large or the size is too large.

The simplest example

We know that the usage of PermGen space has a lot to do with the number of classes loaded by the JVM. The following code demonstrates this situation:

import javassist.ClassPool;

public class MicroGenerator {

  public static void main(String[] args) throws Exception {

    for (int i = 0; i < 100_000_000; i++) {

      generate("eu.plumbr.demo.Generated" + i);

    }

  }

  public static Class generate(String name) throws Exception {

    ClassPool pool = ClassPool.getDefault();

    return pool.makeClass(name).toClass();

  }

}

This code generates many classes dynamically in the for loop. As you can see, it is very simple to use the javassist tool class to generate a class.

Executing this code will generate a lot of new classes and load them into memory. As more and more classes are generated, they will fill up the Permgen space, and then throw a java.lang.OutOfMemoryError: Permgen space error, of course , Other types of OutOfMemoryError may also be thrown.

To see the effect quickly, you can add appropriate JVM startup parameters, such as: -Xmx200M -XX:MaxPermSize=16M and so on.

OutOfMemoryError during Redeploy

Note: If Tomcat generates a warning during development, you can ignore it. It is recommended not to redploy in the production environment, just close/or Kill related JVM directly, and then start from the beginning.

The following situation is more common. When redeploying a web application, it is likely to cause a java.lang.OutOfMemoryError: Permgen space error. It stands to reason that when redeploying, a container such as Tomcat will use a new classloader to load a new class. Let the garbage collector clean up the previous classloader (along with the loaded class).

But the actual situation may not be optimistic. Many third-party libraries, as well as some restricted shared resources, such as threads, JDBC drivers, and file system handles (handles), will cause the previous classloader to be unable to be completely uninstalled. Then in redeploy, The previous class still resides in PermGen, and each redeployment will generate tens of MB or even hundreds of MB of garbage.

Suppose that when an application is started, the JDBC driver is loaded through the initialization code to connect to the database. According to the JDBC specification, the driver will register itself to java.sql.DriverManager, that is, add an instance of itself to a static in DriverManager area.

Then, when the application is uninstalled from the container, java.sql.DriverManager still holds a JDBC instance (Tomcat often warns), and the JDBC driver instance holds a java.lang.Classloader instance, so the garbage collector has no way. Reclaim the corresponding memory space.

The java.lang.ClassLoader instance holds all the loaded classes, usually tens/hundreds of MB of memory. As you can see, redeploy will take up another piece of PermGen space of the same size. After multiple redeploys, it will cause java.lang.OutOfMemoryError: PermGen space error. In the log file, you should see related error messages.

solution

1. Solve the OutOfMemoryError generated when the program starts

When the program starts, if PermGen is exhausted and an OutOfMemoryError is generated, it is easy to solve. Increase the size of PermGen so that the program has more memory to load the class. Modify the -XX:MaxPermSize startup parameter, similar to the following:

java -XX:MaxPermSize=512m com.yourcompany.YourClass

The above configuration allows the JVM to use the maximum PermGen space of 512MB, if it is not enough, an OutOfMemoryError will be thrown.

2. Solve the OutOfMemoryError generated during redeploy

We can perform heap dump analysis-after redeploy, perform a heap dump, similar to the following:
jmap -dump:format=b,file=dump.hprof <process-id>

Then use a heap dump analyzer (such as the powerful Eclipse MAT) to load the dump file. Find out the duplicate classes, especially the class corresponding to the classloader. You may need to compare all the classloaders to find the one currently in use.

Eclipse MAT  official website download address: http://www.eclipse.org/mat/downloads.php

For an inactive classloader (inactive classloader), you need to determine the shortest path GC root first, and see which one prevents it from being recycled by the garbage collector. In this way, you can find the source of the problem. If it is a third-party library, Then you can search Google/StackOverflow to find the solution. If it is your own code problem, you need to dereference at the right time.

3. Solve OutOfMemoryError generated during runtime
If an OutOfMemoryError occurs during operation, first confirm whether the GC can unload the class from PermGen. The official JVM is quite conservative in this regard (after loading a class, it has been allowed to reside in memory, even if the class is no longer used). However, modern applications will dynamically create a large number of The life cycle of these classes is basically very short, and the old version of JVM cannot handle these problems well. Then we need to allow the JVM to uninstall the class. Use the following startup parameters:

-XX:+CMSClassUnloadingEnabled

By default, the value of CMSClassUnloadingEnabled is false, so it needs to be specified explicitly. After enabling, GC will clean up PermGen and uninstall useless classes. Of course, this option only takes effect when UseConcMarkSweepGC is set. If you use ParallelGC, or Serial GC, you need to switch to CMS:
-XX:+UseConcMarkSweepGC

If it is determined that the class can be unloaded, if OutOfMemoryError still exists, then a heap dump analysis is required, similar to the following command:
jmap -dump:file=dump.hprof,format=b <process-id> 

Then load the heap dump through a heap dump analyzer (such as Eclipse MAT). Find the heaviest classloader, that is, the one with the largest number of loaded classes. Compare the class loader with the loaded class and the number of corresponding instances, find the top part, and analyze it one by one.

For each suspected class, you need to manually trace to the code that generates these classes to locate the problem.





Share:

0 comments:

Post a Comment

Search This Blog

Weekly Pageviews

Translate

Blog Archive