0

I want to build histograms from several images. To perform this process, I get access to the DataBufferByte I recognize that the GC doesn't free the memory after building the histogram. What is wrong with this code?

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
import javax.imageio.ImageIO;


public class Histogram {

    HashMap<Color,AtomicInteger> histogram;

    public Histogram() {
        histogram = new HashMap<>();
    }

    public void build(BufferedImage image){

        int pixelLength = 3;

        byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();

        int red, green, blue;
        Color color;
        for ( int pixel = 0; pixel <= pixels.length - pixelLength; pixel+= pixelLength ) {

            blue= ((int) pixels[pixel] & 0xff); // blue
            green= (((int) pixels[pixel + 1] & 0xff) ); // green
            red = (((int) pixels[pixel + 2] & 0xff) ); // red
            color = new Color(red, green, blue);
            if(histogram.containsKey(color)){
                histogram.get(color).incrementAndGet();
            }
            else{
                histogram.put(color, new AtomicInteger(1));
            }
        }
        pixels = null;    
    }

    public static void main(String[] args) {
        String pathImage = "C://testjpg";
        try {
            for (int j = 0; j < 5000; j++) {
                BufferedImage i = ImageIO.read(new File(pathImage));

                Histogram h = new Histogram();

                h.build(i);
                i.flush();

            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }    
    }
}

thanks for your support :)

Nicolas Filotto
  • 43,537
  • 11
  • 94
  • 122
501 - not implemented
  • 2,638
  • 4
  • 39
  • 74
  • First of all, the GC does not reclaim memory immediately, it does so when it finds necessary. Second, `BufferedImage`'s `flush()` does not release the pixel data (it only flushes the "surface data", which may be a cached representation in video RAM, outside the JVM heap). The `BufferedImage` referenced by your `i` variable is *available for GC* after each iteration of your loop, but it may not happen right away. – Harald K Sep 23 '16 at 10:18
  • ...or in other words, there's nothing wrong here... :-) – Harald K Sep 23 '16 at 10:50
  • okay - thank you. I think it is very curious...I use the histogram code in a tomcat web application, that extracts histograms from more than 10.000 images in a loop. There the memory increases very rapidly (I'm not saving the histograms in this time, but tomcat takes more than 10GB of RAM o.o). If I run the stand alone program on a windows machine, it looks fine... – 501 - not implemented Sep 23 '16 at 12:54

1 Answers1

1

The GC does not run automatically and recalls the memory only when it needs it. You can force it using System.gc(), but be careful to not do it too often, else it will slow down your program.

Your code runs fine, and here is what I've tested:

public static void buildColorHistogram(BufferedImage image)
{
final int pixelLength = 3;

byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();

for (int pixel=0 ; pixel < pixels.length; pixel+=pixelLength)
    {
    final int blue = pixels[pixel] & 0xff ; // blue
    final int green= pixels[pixel + 1] & 0xff ; // green
    final int red  = pixels[pixel + 2] & 0xff ; // red
    Color color = new Color(red, green, blue) ;
        if ( histogram.containsKey(color) )
            histogram.get(color).incrementAndGet() ;
        else
            histogram.put(color, new AtomicInteger(1)) ;
    }

pixels = null ;
}

Be careful that some color images have also an alpha channel, which means that pixelLength will be 4 instead of 3.

Do you empty out (flush) the histogram when you're done with it? Because there are 16 millions combinations/triplets of color.

FiReTiTi
  • 5,597
  • 12
  • 30
  • 58