Question

My application needs about 10 GByte of RAM for specific input, where for regular inputs roughly 1 GByte is enough. Closer analysis with JProfiler reveals that (after GC) quite a lot of memory is used by standard classes from java.util.*:

LinkedHashMap$Entry, HashMap$Entry[], LinkedHashMap, HashMap$KeySet, HashMap$EntrySet, LinkedHashSet, TreeMap$Entry, and TreeMap (in this order), and related classes. The following entry is of a class in my own code, where the number of instances and the amount of used memory seems very reasonable.

In detail with a total heap usage of about 900 MByte I see the following Size entries in the All Objects view:

  • LinkedHashMap$Entry: 418 MByte
  • HashMap$Entry[]: 178 MByte
  • LinkedHashMap: 124 MByte
  • HashMap$KeySet: 15 MByte

The memory used by LinkedHashMap seems to be too high, even when considering that every LinkedHashSet is backed by a LinkedHashMap.

I recorded object allocation in JProfiler and observe the Allocation Hot Spots for LinkedHashMap. In there, I see entries that I do not understand:

  • The third entry shows a hot spot (with 6.5% of the allocated memory) named X.<init> where X is a class in my own code. The constructors of this method do not have anything to do with LinkedHashMap. Following this entry to Thread.run at the end shows a slow decline from 6.5% to 5.8% at Thread.run. What could be the problem with my code in X? Why is it shown here?
  • About 8% of allocated memory is shown in the hot spot named java.util.HashSet.iterator. Following this entry along the path with the highest percentage (first entry: 2.8%) I get several methods inside my code until, finally, java.lang.Thread.run is shown (with 2.8%). What does this mean? As far as I know the Thread.run method does not create instances of LinkedHashMap. What is the connection to the iterator method?

In general, how do I find the code that keeps references to (a lot of) LinkedHashMap objects? Using the Heap Walker I can only see lots of those instances, but do not see any pattern (even when observing the paths to the GC roots). In my experiments all instances seem to be in order.

Possibly important things:

  • My application constructs a result (for further processing) and, for this construction, the high memory usage is observed. The construction continually creates objects, so waiting for a stable point and then observing every single created LinkedHashMap object is not possible.
  • I have good computers available to debug (up to 48 cores and 192 GByte of RAM, possibly even more).
  • java version "1.7.0_13" (Java(TM) SE Runtime Environment (build 1.7.0_13-b20), Java HotSpot(TM) 64-Bit Server VM (build 23.7-b01, mixed mode))
  • JProfiler 7.2.2 (Build 7157), licensed
Was it helpful?

Solution

In general, how do I find the code that keeps references to (a lot of) LinkedHashMap objects?

In the heap walker, select "LinkedHashMap" and create a new object set. Then switch to the "References" view and show the "Cumulated incoming references". There, you can analyze references for the entire object set.

enter image description here

As for your question about the allocation hot spots and why the Thread.run method is shown: These are back traces, they show how the hot spot has been invoked and all numbers on the nodes are contributions to the hot spot at the top. The deepest node will already always be an entry point, typically a Thread.run method or the main method.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top