15

I am seeing 'java.lang.OutOfMemoryError: PermGen space' while running ~300 JUnit tests and using Spring context. Having a tough time figuring out what's eating up PermGen since:

  • in steady state the app consumes about 90m of permgen space
  • I've tried -XX:MaxPermSize=256m for unit tests - still running out
  • With -XX:+TraceClassLoading and -XX:+TraceClassUnloading enabled, I see no additional "loading" events while executing the last 20-30 tests before the OutOfMemoryError.

The latter seemingly suggests that something besides Class objects is filling PermGen, no? If so, what could it be? For example, are there circumstances when class instances are stored in PermGen?

Here's my VM info:

$ java -version
java version "1.6.0_25"
Java(TM) SE Runtime Environment (build 1.6.0_25-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode)

related

FWIW, the root of my problem that precipitated this post turned out to be somewhat trivial: I assumed that Maven Surefire plugin inherits VM settings from MAVEN_OPTS (or VM instance running mvn) when it forks a VM - it does not (boo). One has to specify those explicitly using argLine in plugin's configuration. HTH.

entpnerd
  • 10,049
  • 8
  • 47
  • 68
Nikita
  • 6,019
  • 8
  • 45
  • 54
  • 1
    JProfiler or YourKit will give you more information about what's in there. – bmargulies Jun 14 '11 at 17:40
  • @bmergulies - Great idea to use a profiler! – Jesse Webb Jun 14 '11 at 17:44
  • Without seeing your code, my first thought is that the spring context is being instantiated for each test case. – Woot4Moo Jun 14 '11 at 17:46
  • @Nikita consider adding your last paragraph explicitly as an answer. Once folks understand the question, they tend not continue reading a question all the way through - myself included. If I saw that answer, I would upvote it. – entpnerd Dec 07 '16 at 20:08

3 Answers3

4

Sometimes abuse of String.intern() can cause out of PermGen space errors since interned String instances are stored in PermGen.

This might be what you are seeing - try eliminating unnecessary String.intern() calls to see if this solves the problem. In general, I wouldn't recommend using String.intern() unless you are sure that both of the following are true:

  • You are sure that only a limited number of Strings will be added
  • You actually need such Strings to share the same object identity (e.g. if many instances of the same strings would consume unacceptable amounts of memory or you need to rely on == for String comparison for complex performance reasons)
mikera
  • 105,238
  • 25
  • 256
  • 415
  • right, forgot about String.intern() - accepting answer (unfortunately, in my case this was NOT the cause of OutOfMemoryError) – Nikita Jun 14 '11 at 21:04
  • @Gweebz i accepted the answer that most directly addressed the explicit question I posted. If that is wrong, I don't want to be right ;- – Nikita Jun 15 '11 at 21:47
2

Interned Strings are also stored in permgen, although hundreds of megabytes of string sseems unlikely. Remember each Spring Bean for which you are using proxies is generating new classes on the fly at runtime to implement the interfaces that you're proxying. (Or your classes if you're using CGLIB proxies, etc.) So if you're creating a 'fresh' Spring ApplicationContext for each JUnit, you are in fact cranking out 300 copies of all your proxies etc.

Also remember instances of Class are unique per classloader, not across the entire permgen space, so there can in fact be duplicates depending on how your runs are set up (if it involves deployment in a container or something, although that also seems unlikely in a JUnit :) ).

Affe
  • 47,174
  • 11
  • 83
  • 83
  • Good points, but I am not creating a new Spring ctx per test - a ctx is created initially, shared across all tests, and refreshed once during the 300 test run. On generating new classes on the fly: however the class is generated, should it not cause a [Loading xxx] log entry (due to -XX:+TraceClassLoading) at the time it is generated? I am not seeing those afer awhile – Nikita Jun 14 '11 at 21:07
1

I notice you are running a 64 bit JVM. These are known to use twice the actually memory on your machine because it requires twice as large of memory spaces per allocation.

If you have JUnit tests which actually load up a spring context (not so Unit after all), these will instantiate ALL of the beans in your appContext. This will most likely require more than 128mb (256mb on a 64bit box) of memory.

In my experience, it is not absurd to allocate half a gig or more to a large test suite on a 64 bit machine. Try upping it to 512mb or even 1gb.

These are the options I run one of my larger project's test suite with...

-Xms256m
-Xmx512m
-XX:MaxPermSize=512m
Jesse Webb
  • 43,135
  • 27
  • 106
  • 143
  • You can alleviate that with CompressedOops if you do not need a very large heap: http://wikis.sun.com/display/HotSpotInternals/CompressedOops – gpeche Jun 14 '11 at 18:42