Monday, August 9, 2010

Understanding PermGen errors

Great article from Alessandro Ribeiro.

“java.lang.OutOfMemoryError: PermGen space ”: have you seen this error before ? If you have never seen it, you haven't probably been involved in creating large java server applications as of lately. Let's understand what this error means.

When you get OutOfMemoryError, it means that (so obvious) your application doesn't have enough memory to go on. But the rest of the message, “PermGen space” means that it's not the common object heap space that it's lacking, but only the memory space for the binary code of classes and methods is lacking.

Let's go deeper into this subject to understand properly what PermGen means. Well, PermGen is an acronym for “Permanent Generation”. The term Generation comes from the concept of “generational collection” and the term “generational collection” refers to an heuristic in the garbage collection mechanism that is used to intelligently identify only the most probable objects for garbage collection. There are 3 kinds of Generations in the VM memory: the young generation, the tenured generation and the permanent generation.

Let's go back to the original “Permanent Generation” term. As I said, it identifies a particular set of objects which are candidate for garbage collection and handled differently by the GC. These kind of objects which belong to the “Permanent Generation” are only the binary codes of all classes and methods. So, the binary code of classes and methods are not stored in the same memory area as the object instances. Now, I think you already understand what this error mean. “java.lang.OutOfMemoryError: PermGen space ” means that the virtual machine ran out of space for loading the binary code of classes and methods.

There are two steps ahead: knowing understanding why this error has happened to your application, learning to monitor the permanent generation size in order to prevent the occurrence of this error; and learning how to increase the permanent generation size.

Why has this error happened ?

The default size for permanent generatoin is 64M. But how come my not so large web application, which totals 2 megabytes of class code, could exceed this limit ? You're probably not counting it correctly. Your web application probably runs inside Jboss, Tomcat or another J2EE server. Add the size of Java SE libraries, the size of Jboss, the size of all libraries used by Jboss internally, the size of all libraries in the WEB-INF/lib directory of you WAR and you will get a large, large binary code area.

Aren't Java classes loaded on demand ? Yes, they are loaded on demand. So, you will probably have to factor that large number by 3 or 4. But there are other factors for the enlargement of the permanent generation area. If you web application has JSP pages, remember that every JSP page is converted into a servlet class file, before being executed. So, you should add that to the previous number. Also, maybe there are other web applications running under Jboss than you own. If you use Hibernate, for each persistent class that you create, Hibernate will dynamically create an enhanced class. If you use RMI or EJB, many kinds of proxy classes will be created and loaded in memory.

Now that you believe that the size of the binary code of classes and methods can exceed 64M, you have probably given up summing the sizes of all classes created and loaded by the VM, Jboss and you application. You'd better off monitoring the use of PermGen area than trying to calculate its size. That leads us to the next step: how can I monitor the increase in PermGen area.

How can I monitor the increase of PermGen area ?

Let's continue, where we stopped in my last post: How can I monitor the increase in PermGen area ?

For monitoring PermGen, we'll learn to use a very simple tool, which is part of Java 5, called JSTAT. For those who use Linux or any other Unix-like OS, jstat is very similar to the TOP tool. JSTAT will connect to some VM, local or remote, and monitor some critical resources. Let's see how it works. Just type JSTAT and you'll see its command-line options:

JPS (Java Process Status)

Let's understand the options. The 1st thing you need to know is the VMID (Virtual Machine ID). What is the VMID ? Right now, there are 2 Java applications running on my computer, each in its own virtual machine. Each virtual machine has its own VMID. To discover the VMID that the application you need to monitor is using, you'll have to learn another tool: JPS (Java Process Status). There is a very similar tool in the Unix world called PS. JPS will list all running VMs with their VMIDs. Just type JPS on the command-line and you'll see this:

Let's understand JPS's output. In its basic form, jps outputs the VMID and the simple name of the executable class that is running in the virtual machine. In this case, I can infer the application I want to monitor, Jboss, is the first one listed, not because I know that "Main" is the name executable class, but because I can exclude the 2nd line, which is obviously EasyEclipse and the 3rd line, which is JPS. JPS itself runs inside a virtual machine and therefore, is always reported by jps. If, by the simple name of main class you cannot discover the VMID of your application, I suggest you type "jps -l" which will show the complete name of the main class for each VM. In most cases, that will do.

Now that we know that the VMID of Jboss is 1136, let's use JSTAT to monitor Jboss usage of PermGen memory. To use JSTAT, you need specify a single parameter that will identify the class of monitors you want to use. In our case, we will use "-gcpermcapacity" which will show statistics of the sizes of the Permanent Generation.

The important metrics

There are 3 important numbers to monitor regarding permanent generation size: the maximum permanent size, the current permanent size, and the current permanent utilization. Now, look at jstat's ouput. The 1st column shows the minumum size for the permsize. That's a parameter you can set in the initialization of the virtual machine. The 2nd column shows the maximum size for permsize. This is also a paramter you can set in the initialization of the virtual machine. Now, compare the 2nd column to the 3rd column. The permanent size fluctuate between the minimum and the maximum number. Now, it's just the minimum number. But that doesn't mean it's all used. To know how much of the current permanent size is being used, we'll have to look at another number, the Permanent Capacity Utilization, which you can see using the "-gcutil" parameter. If [minimum] = [maximum] permanent size, then [current] = [maximum] = [minimum] permanent size. Therefore, this number will not be very useful. But, if [minimum] < [maximum], knowing if [current] is getting closer to [maximum] is of vital importance. If [current] is only a few megabytes bellow maximum, maybe this is the right time to change Jboss Installation, so that its virtual machine is initialized with a greater MAXIMUM permanent size.

Well, we'll learn how to do that in part3.

There are two command-line parameters that you must use to define the size of the permgen area: -XX:PermSize -XX:MaxPermSize. See the example bellow:

java -Xms64m -Xmx128m -XX:PermSize=64m -XX:MaxPermSize=256m MainClass

In this example, we are instructing the Java Virtual Machine to create an initial object heap size of 64 megabytes (-Xms) and maximum size of 128 megabytes (-Xmx). And we are defining a PermGen heap area of initially 64 megabytes (-XX:PermSize) and maximum size of 256 megabytes (-XX:MaxPermSize). Remember that the initial size of the heap area in this example, is the sum of the initial object heap area and the initial permgen heap area, that is, 64+64=128 megabytes. And the same for the maximum size of the heap area: 128+256=384 megabytes.

As a practical example, let's see how to increase permgen area size in Jboss. Locate the jboss.bat (in Windows) or jboss.conf (in Unix). Look for a linke containing a redefinition of the shell script variable JAVA_OPTS. You see bellow how the default configuration in Windows:

Uncomment thes line to set PermGen area size:

Now that we have learned how to monitor and fix permgen errors, I advice you not to blindly increase the permgen size everytime there is a permgen error. Monitor how the size of the permgen area is growing. The Permgen area shouldn't be constantly growing over a long period. The Java language features dynamic class loading, so classes will be loaded on demand and the permgen heap size will grow as well. But, if in the long term, your permgen area is still steadly growing, you are probably using some kind of bytecode generation framework which is misbehaving or you are using it incorrectly. Analyse that and rethink what you are doing.

2 comments:

  1. Hi,
    i'm trying to change PermSize inside run.conf for jboss 4.2.2GA, but still i can't see any changes after restart jboss....do you have any suggestions?

    ReplyDelete