简体   繁体   中英

Memory leak with Runnable and ScheduledExecutorService

I'm making this status/menu bar app which displays the currently playing song in the status bar for Mac OS X. To get the player status from Spotify I have to create and execute an AppleScript and get the output from this. The result is then drawn using drawString() from Graphics2D, which is set onto a BufferedImage which is then set as the tray icon.

The whole code is 4 classes and easy to follow, available here: https://github.com/ZinoKader/Menify

Now onto the problem

My runnable seems to eat up memory like nothing I've seen before. Every second the application uses 2-3MB more RAM, and reaches for gigabytes if I leave it be. What I have tried so far is to flush and dispose of all my images and Graphics2D resources, flush and close every inputstream, outputstream and destroy the Process object I create in AppleScripthHelper.

Even something like this, just calling a static method starts piling up RAM really quickly.

final Runnable refreshPlayingText = () -> {
    AppleScriptHelper.evalAppleScript(ScriptConstants.SPOTIFY_META_DATA_SCRIPT);
}

//update every 50ms
mainExecutor.scheduleAtFixedRate(refreshPlayingText, 0, 50, TimeUnit.MILLISECONDS);

and AppleScriptHelper

class AppleScriptHelper {

private static final int EOF = -1;

static String evalAppleScript(String code) {

    String[] args = { "osascript", "-e", code };

    try {
        Process process = Runtime.getRuntime().exec(args);
        process.waitFor();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] bigByteArray = new byte[4096];

        InputStream is = process.getInputStream();
        copyLargeStream(is, baos, bigByteArray); //write to outputstream

        String result = baos.toString().trim();

        is.close();
        baos.flush();
        baos.close();
        process.destroyForcibly();

        return result;

    } catch (IOException | InterruptedException e) {
        Log.debug(e);
        return null;
    }
}

private static void copyLargeStream(InputStream input, OutputStream output, byte[] buffer) throws IOException {
    int n;
    while (EOF != (n = input.read(buffer))) {
        output.write(buffer, 0, n);
    }
    input.close();
    output.close();
  }

}

So the question is, what is eating up all of that RAM? Why is seemingly nothing being garbage collected?

What you're facing is not a memory leak!

According to the Java™ Tutorials for Processes and Threads ( https://docs.oracle.com/javase/tutorial/essential/concurrency/procthread.html ),

A process generally has a complete, private set of basic run-time resources; in particular, each process has its own memory space.

You're creating a new process every 50ms which is most likely what is taking a toll on your available memory.

Creating too many processes will result in thrashing and you will notice reduced CPU performance. Depending on what the process does, there is most likely a more efficient way to achieve your goal without creating 20 processes per second.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM