At work, we have several tomcat servers running several webapps, about half of which must perform some image processing.
Before performing image processing, these web applications execute ImageIO.scanForPlugins() to retrieve the corresponding image readers and records in memory. Although before it was just launched at any time, the image should be processed, now we start scanning only when we initialize webapps (since after launch we do not add jars, why start scanning more than once?)
A few days later, the tomcat instance crashed due to an OutOfMemoryError . Fortunately, we had the HeapDumpOnOutOfMemoryError option, so I looked at a bunch of heaps. In the dump, I found that 97% of the memory was taken by an instance of javax.imageio.spi.PartialOrderIterator . Most of this space was occupied by him, supporting a java.util.LinkedList , which had 18 million elements. The linked list consists of javax.imageio.spi.DigraphNode , which contains image readers and records loaded by ImageIO.scanForPlugins() .
"Yeah," I thought, "we have to run the scan in a loop somewhere, and we just add the same elements over and over." But, I decided that I should double check this assumption, so I wrote the following test class:
import javax.imageio.ImageIO; public class ImageIOTesting { public static void main(String[] args) { for (int i = 0; i < 100000; i++) { ImageIO.scanForPlugins(); if (i % 1000 == 0) { System.out.println(Runtime.getRuntime().totalMemory() / 1024); } } } }
However, when I run this class in a server environment, the amount of memory used never changes!
A quick search of the package source javax.imageio shows that the check checks if the service provider is already registered and if it unregisters the old provider before registering the new one. So now the question is: why do I have this giant list of service providers? Why are they stored as a directed graph? And more importantly, how can I prevent this?