0

What I am doing is making a program that constantly takes screenshots of the users desktop and saves them as long as the user wants. I was initially placed a call to the method that captures in a while method but that was too slow and I need as many images take as soon as possible. What I decided to do was use threading.

While the program is running my PC get's slow (ie mouse flashes, moves slow, ect) and when I stop the program I get the following error.

Exception in thread "Thread-294" java.lang.OutOfMemoryError: Java heap space
at sun.awt.windows.WRobotPeer.getRGBPixels(Unknown Source)
at java.awt.Robot.createScreenCapture(Unknown Source)
at maple.Record.run(Record.java:29)

Here is the line that produces the error.

img = r.createScreenCapture(new Rectangle (0, 0, width, height));

Here is my class that contains the thread.

import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Vector;
import javax.imageio.ImageIO;
import javax.media.MediaLocator;

public class Record implements Runnable {

    Robot r;
    static int width = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
    static int height = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
    BufferedImage img;
    public static boolean stop = false;

    public void run() {

        try {

            r = new Robot();
            img = r.createScreenCapture(new Rectangle (0, 0, width, height));
            ImageIO.write(img, "png", new File(JavCapture.tmpLocation + "\\tmp\\" + System.currentTimeMillis() + ".png"));

        } catch (IOException | AWTException e) { e.printStackTrace(); }

    }

}

The error is produced when I am calling the JpegImagesToMovies that has been edited to work with .png files.

Please check out this link for more information on that.

How can I solve the problem, and how can I make the thread use less memory so that it doesn't slow the PC down.

Here is the call in the main method.

do {    
    (new Thread(new Record())).start();
} while (!Record.stop);
Community
  • 1
  • 1
user2612619
  • 1,119
  • 3
  • 11
  • 28

4 Answers4

5

From the looks of it you are creating a separate thread for every screenshot, this will run you out of memory very fast. Instead you should have a single thread that sits in loop, sleeps and takes screenshots from time to time.

    public class ScreenshotTaker implements Runnable {



        private volatile boolean done = false;



        public void run( ) {

            while (!done) {
            ... take screenshot...

            ...sleep ...
            }

        }

        public void setDone( ) {

            done = true;

        }

    }
Uku Loskit
  • 40,868
  • 9
  • 92
  • 93
  • Instead of creating new threads in a while loop put a while loop inside your run() method, run only a single instance. make sure you sleep for some time as well. – Uku Loskit Aug 18 '13 at 18:19
  • I do that and the thread is running but then the line after the thread runs while the thread is still running. I need to wait for the thread to end before executing that line. – user2612619 Aug 18 '13 at 18:29
  • you signal the thread to send by setting "done" via the setDone method, a thread is a separate line of operation, it is supposed to continue running the rest of your code. – Uku Loskit Aug 18 '13 at 18:32
  • 1
    user2612619, you can use thread.join() to accomplish that. Figure out how you can use it. Please check this tutorial: http://www.tutorialspoint.com/java/lang/thread_join.htm – SSaikia_JtheRocker Aug 18 '13 at 18:38
  • sometimes an approch or its implementation is wrong or misguided on so many levels that it's not easy to start an answer since the OP really really needs to read some tutorial… – Ralf H Aug 18 '13 at 18:45
  • What? How could you use join() on an app-lifetime thread that loops - it's just a silly idea. – Martin James Aug 19 '13 at 00:08
3

You're asking for trouble, you should go for some Threadpool implementation that would limit your thread count. To get idea on usage, see the official tutorial: http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html

As in your solution:

do {    
    (new Thread(new Record())).start();
} while (!Record.stop);

you create threads faster than they can process their job. Basically you just say create many threads as fast as possible, there is no guarantee on count of them processed till new one is created. So it's just a question of time till program dies (resources are exhausted = CPU + Memory)

Peter Butkovic
  • 11,143
  • 10
  • 57
  • 81
1

You should limit the thread creation process here

(new Thread(new Record())).start(); 

Those threads start almost together and bloats your memory up.

SSaikia_JtheRocker
  • 5,053
  • 1
  • 22
  • 41
0

With so little code to go with it is hard to say, but you should definitely make the following:

Robot r;
BufferedImage img;

into local variables instead of instance variables. The way you have it, the objects they refer to will be reachable for much longer than your program actually needs them. That's one road to an OutOfMemoryException.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • 2
    The lifespan of the `Runnable` object is by no means identical to the lifespan of its `run` method. And anyway, this is bad design just asking for trouble and giving nothing in return. – Marko Topolnik Aug 18 '13 at 18:21