为什么这个Java程序占用这么多内存?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20430257/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 01:51:56  来源:igfitidea点击:

Why is this Java program taking up so much memory?

javamemory

提问by Joe

I have a small piece of code that takes a screenshot of my desktop every five minutes. However I'm a little confused by the amount of memory it takes up - often it will creep up to 200mb of RAM, which I'm sure is excessive... Can anyone tell me a) sensible ways to reduce the memory footprint or b) why it's going up at all?

我有一小段代码每五分钟截取一次我的桌面。但是,我对它占用的内存量感到有些困惑 - 通常它会爬到 200mb 的 RAM,我敢肯定这是过多的......谁能告诉我 a)减少内存占用的明智方法或b)它为什么涨可言

/**
 * Code modified from code given in http://whileonefork.blogspot.co.uk/2011/02/java-multi-monitor-screenshots.html following a SE question at  
 * http://stackoverflow.com/questions/10042086/screen-capture-in-java-not-capturing-whole-screen and then modified by a code review at http://codereview.stackexchange.com/questions/10783/java-screengrab
 */
package com.tmc.personal;

import java.awt.AWTException;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;

class ScreenCapture {

    static int minsBetweenScreenshots = 5;

    public static void main(String args[]) {
        int indexOfPicture = 1000;// should be only used for naming file...
        while (true) {
            takeScreenshot("ScreenCapture" + indexOfPicture++);
            try {
                TimeUnit.MINUTES.sleep(minsBetweenScreenshots);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    //from http://www.coderanch.com/t/409980/java/java/append-file-timestamp
    private  final static String getDateTime()
    {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd_hh:mm:ss");
        df.setTimeZone(TimeZone.getTimeZone("PST"));
        return df.format(new Date());
    }

    public static void takeScreenshot(String filename) {
        Rectangle allScreenBounds = getAllScreenBounds();
        Robot robot;
        try {
            robot = new Robot();
            BufferedImage screenShot = robot.createScreenCapture(allScreenBounds);
            ImageIO.write(screenShot, "jpg", new File(filename + getDateTime()+ ".jpg"));
        } catch (AWTException e) {
            System.err.println("Something went wrong starting the robot");
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("Something went wrong writing files");
            e.printStackTrace();
        }
    }

    /**
     * Okay so all we have to do here is find the screen with the lowest x, the
     * screen with the lowest y, the screen with the higtest value of X+ width
     * and the screen with the highest value of Y+height
     * 
     * @return A rectangle that covers the all screens that might be nearby...
     */
    private static Rectangle getAllScreenBounds() {
        Rectangle allScreenBounds = new Rectangle();
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice[] screens = ge.getScreenDevices();

        int farx = 0;
        int fary = 0;
        for (GraphicsDevice screen : screens) {
            Rectangle screenBounds = screen.getDefaultConfiguration().getBounds();
            // finding the one corner
            if (allScreenBounds.x > screenBounds.x) {
                allScreenBounds.x = screenBounds.x;
            }
            if (allScreenBounds.y > screenBounds.y) {
                allScreenBounds.y = screenBounds.y;
            }
            // finding the other corner
            if (farx < (screenBounds.x + screenBounds.width)) {
                farx = screenBounds.x + screenBounds.width;
            }
            if (fary < (screenBounds.y + screenBounds.height)) {
                fary = screenBounds.y + screenBounds.height;
            }
            allScreenBounds.width = farx - allScreenBounds.x;
            allScreenBounds.height = fary - allScreenBounds.y;
        }
        return allScreenBounds;
    }
}

采纳答案by Mike Wasson

The other answers are right that Java will use as much memory as it is allowed to, at which point it will garbage collect. To work around this, you can specify a smaller max heap size in the JVM settings. You do this with the -Xmx setting. For example, if you think you only need 32MB, run it as:

其他答案是正确的,Java 将使用尽可能多的内存,此时它将进行垃圾收集。要解决此问题,您可以在 JVM 设置中指定较小的最大堆大小。您可以使用 -Xmx 设置执行此操作。例如,如果你认为你只需要 32MB,运行它:

java -Xmx32M [your main class or jar here]

The heap of your program (the non-stack memory) will never take more than 32MB, but it will crash if it needs more than that at once (and that's where you'll need to profile). I don't see any obvious leaks in your program (assuming ImageIO doesn't require any cleanup), though, so I think you'll be fine.

您的程序堆(非堆栈内存)永远不会超过 32MB,但如果它一次需要超过 32MB,它就会崩溃(这就是您需要分析的地方)。不过,我没有在您的程序中看到任何明显的泄漏(假设 ImageIO 不需要任何清理),所以我认为您会没事的。

回答by chrylis -cautiouslyoptimistic-

For a modern computer, 200MB is not an excessive amount of memory. The JVM will let the heap grow for a while if you're creating and discarding lots of objects so that your program doesn't get bogged down with garbage collection. Let your program run for several hours and then check back if you think there's a problem.

对于现代计算机来说,200MB 并不是太多的内存。如果您要创建和丢弃大量对象,JVM 将让堆增长一段时间,这样您的程序就不会因垃圾收集而陷入困境。让您的程序运行几个小时,然后检查您是否认为有问题。

回答by expozed

JVM garbage collector will eventually clear your memory heap. For manually clearing that heap call Runtime.getRuntime().gc();, but I don't advise doing that for every 5 minutes.

JVM 垃圾收集器最终会清除您的内存堆。对于手动清除该堆调用Runtime.getRuntime().gc();,但我不建议每 5 分钟执行一次。