Avatar billede Slettet bruger
07. april 2005 - 15:20 Der er 2 kommentarer og
2 løsninger

Memory leak

Hej,

Jeg har et problem med nedenstående lille program. Tilsyneladende er der et memory leak et sted. Hukommelsesforbruget (set med process manager i Windows) stiger stødt, når programmet kører. Jeg kan ikke se, hvor problemet skulle være. Nogle der har idéer?

På et døgn er det steget med 3 MB.

Har lagt en JAR fil med programmet her, hvis man vil prøve at køre det (JRE 1.5.x krævet):

http://925.dk/public/TDC_WATCH.jar

/*
Author: Martin Koch Andersen (martin@925.dk)
*/

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.*;
import java.util.Timer;
import java.text.SimpleDateFormat;
import java.text.NumberFormat;

public class TDCClock {
    private static final Locale LOCALE = new Locale("da");
    private static final String TIMEZONE_UTC_ID = "UTC";
    private static final String TIMEZONE_CET_ID = "Europe/Copenhagen";
    private static final TimeZone TIMEZONE_UTC = TimeZone.getTimeZone(TIMEZONE_UTC_ID);
    private static final TimeZone TIMEZONE_CET = TimeZone.getTimeZone(TIMEZONE_CET_ID);
    private static final String DEFAULT_FONT_FAMILY = "Verdana";
    private static final String LOGO_FILENAME = "logo.gif";
    private static BufferedImage IMAGE;
    private static long timerLastRun = 0;
    private static TimerTask timerTask = null;
    private static final GraphicsEnvironment GFX_ENV = GraphicsEnvironment.getLocalGraphicsEnvironment();
    private static final GraphicsDevice GFX_DEVICE = GFX_ENV.getDefaultScreenDevice();
    private static final GraphicsConfiguration GFX_CONFIG = GFX_DEVICE.getDefaultConfiguration();
    private static final Frame FRAME = new Frame("TDC watch", GFX_CONFIG);
    private static final Color COLOR_CENTER_BACKGROUND = new Color(16, 33, 137);
    private static Timer timer;

    private static class ExitOnESC extends KeyAdapter {
        public void keyPressed(KeyEvent e) {
            if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                System.exit(0);
            }
        }
    }

    private static class ClockTimerTask extends TimerTask {
        private static Graphics2D g;
        private static int imagePosX, imagePosY;
        private static Rectangle2D rectangleTop;
        private static Rectangle2D rectangleCenter;
        private static Rectangle2D rectangleBottom;
        private static double dateTextWidth;
        private static int windowWidth;
        private static BufferStrategy strategy = null;
        private static String UTC_TIME = "";
        private static String CET_TIME = "";
        private static String CET_DATE = "";
        private static Date date;
        private static String tempCETDate;

        private static final Color COLOR_TOP_BACKGROUND = new Color(255, 255, 255);
        private static final Color COLOR_BOTTOM_BACKGROUND = COLOR_CENTER_BACKGROUND;
        private static final Color COLOR_TEXT = new Color(255, 255, 255);
        private static final Font FONT_CLOCK = new Font(DEFAULT_FONT_FAMILY, Font.BOLD, 110);
        private static final Font FONT_DATE = new Font(DEFAULT_FONT_FAMILY, Font.BOLD, 75);
        private static final String CET_LABEL = "CET";
        private static final String UTC_LABEL = "UTC";
        private static final SimpleDateFormat FORMAT_UTC_TIME = new SimpleDateFormat("HH:mm:ss", LOCALE);
        private static final SimpleDateFormat FORMAT_CET_TIME = new SimpleDateFormat("HH:mm:ss", LOCALE);
        private static final SimpleDateFormat FORMAT_CET_DATE = new SimpleDateFormat("EEEEE dd.MM.yyyy", LOCALE);

        public ClockTimerTask() {
            // do nothing if this is not the first instance of timertask
            if (strategy != null)
                return;
            FORMAT_UTC_TIME.setTimeZone(TIMEZONE_UTC);
            FORMAT_CET_TIME.setTimeZone(TIMEZONE_CET);
            FORMAT_CET_DATE.setTimeZone(TIMEZONE_CET);

            Insets ins = FRAME.getInsets();
            windowWidth = FRAME.getWidth() - ins.left - ins.right;
            int windowHeight = FRAME.getHeight() - ins.top - ins.bottom;

            FRAME.createBufferStrategy(2);
            strategy = FRAME.getBufferStrategy();

            rectangleTop = new Rectangle(0, 0, windowWidth, windowHeight/5);
            rectangleCenter = new Rectangle(0, windowHeight/5, windowWidth, windowHeight - ((windowHeight/5) * 2));
            rectangleBottom = new Rectangle(0, windowHeight - (windowHeight/5), windowWidth, windowHeight/5);

            imagePosX = windowWidth/2 - IMAGE.getWidth()/2;
            imagePosY = (int) (rectangleTop.getHeight() - IMAGE.getHeight()) / 2;
        }

        public void run() {
            timerLastRun = System.currentTimeMillis();
            g = (Graphics2D) strategy.getDrawGraphics();
            g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
            render(g);
            g.dispose();
            strategy.show();
        }

        public void render(Graphics2D g) {
            date = new Date();
            UTC_TIME = (FORMAT_UTC_TIME.format(date));
            CET_TIME = (FORMAT_CET_TIME.format(date));

            g.setPaint(COLOR_CENTER_BACKGROUND);
            g.fill(rectangleCenter);
            g.setPaint(COLOR_BOTTOM_BACKGROUND);
            g.fill(rectangleBottom);
            g.setPaint(COLOR_TOP_BACKGROUND);
            g.fill(rectangleTop);
            g.drawImage(IMAGE, imagePosX, imagePosY, null);


            g.setFont(FONT_CLOCK);
            g.setColor(COLOR_TEXT);

            g.drawString(UTC_LABEL, 50, 350);
            g.drawString(UTC_TIME, 412, 350);

            g.drawString(CET_LABEL, 50, 530);
            g.drawString(CET_TIME, 412, 530);

            g.setFont(FONT_DATE);
            tempCETDate = FORMAT_CET_DATE.format(date);
            CET_DATE = tempCETDate.substring(0, 1).toUpperCase() + tempCETDate.substring(1);
            dateTextWidth = g.getFontMetrics().getStringBounds(CET_DATE, g).getWidth();
            g.drawString(CET_DATE, (int) (windowWidth/2 - dateTextWidth/2), (int) rectangleBottom.getY() + 120);
        }
    }

    public static void setup() {
        boolean fontFound = false;
        boolean displayModeFound1 = false;
        boolean displayModeFound2 = false;
        boolean localeFound = false;
        boolean timeZoneUTCfound = false;
        boolean timeZoneCETfound = false;
        final Label MESSAGE_LABEL = new Label();
        final Panel MESSAGE_PANEL = new Panel();
        final ExitOnESC KEYADAPTER = new ExitOnESC();
        final DisplayMode WANTED_DISPLAYMODE = new DisplayMode(1024, 768, 24, DisplayMode.REFRESH_RATE_UNKNOWN);
        final DisplayMode WANTED_DISPLAYMODE_2 = new DisplayMode(1024, 768, 16, DisplayMode.REFRESH_RATE_UNKNOWN);
        final Frame MESSAGE_FRAME = new Frame("TDC watch");

        MESSAGE_FRAME.setResizable(true);
        MESSAGE_FRAME.setSize(new Dimension(500, 200));
        MESSAGE_FRAME.setUndecorated(true);
        Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
        MESSAGE_FRAME.setLocation((screenDim.width - 500) / 2, (screenDim.height - 200) / 2);
        MESSAGE_FRAME.add(MESSAGE_LABEL, BorderLayout.NORTH);
        MESSAGE_PANEL.setPreferredSize(new Dimension(500, 30));
        MESSAGE_PANEL.setBackground(COLOR_CENTER_BACKGROUND);
        MESSAGE_FRAME.add(MESSAGE_PANEL, BorderLayout.SOUTH);
        FRAME.addKeyListener(KEYADAPTER);
        MESSAGE_FRAME.addKeyListener(KEYADAPTER);
        MESSAGE_FRAME.setVisible(true);
        MESSAGE_LABEL.setText("Checking dependencies...");

        /*
        logo image load and validation
        ----------------------------------------------------------------------------------------------------------------
        */

        try {
            IMAGE = ImageIO.read(TDCClock.class.getResource(LOGO_FILENAME));
        } catch (Exception e) {
            MESSAGE_LABEL.setText("Could not load logo from file: " + LOGO_FILENAME);
            return;
        }

        if (IMAGE == null) {
            MESSAGE_LABEL.setText("Could not load logo from file: " + LOGO_FILENAME);
            return;
        }

        /*
        locale validation
        ----------------------------------------------------------------------------------------------------------------
        */

        Locale[] locales = NumberFormat.getAvailableLocales();
        for(Locale locale : locales) {
            if (locale.equals(LOCALE)) {
                localeFound = true;
                break;
            }
        }

        if (!localeFound) {
            MESSAGE_LABEL.setText("Could not find locale: " + LOCALE);
            return;
        }

        /*
        time zone validation
        ----------------------------------------------------------------------------------------------------------------
        */

        String[] ids = TimeZone.getAvailableIDs();
        for (String id : ids) {
            if (id.equals(TIMEZONE_UTC_ID)) {
                timeZoneUTCfound = true;
            }
            if (id.equals(TIMEZONE_CET_ID)) {
                timeZoneCETfound = true;
            }
            if(timeZoneUTCfound && timeZoneCETfound)
                break;
        }

        if (!timeZoneUTCfound) {
            MESSAGE_LABEL.setText("Time zone with id: " + TIMEZONE_UTC_ID + " was not found.");
            return;
        }
        if (!timeZoneCETfound) {
            MESSAGE_LABEL.setText("Time zone with id: " + TIMEZONE_CET_ID + " was not found.");
            return;
        }

        /*
        font family validation
        ----------------------------------------------------------------------------------------------------------------
        */

        String[] fonts = GFX_ENV.getAvailableFontFamilyNames();
        for(String font : fonts) {
            if (font.equals(DEFAULT_FONT_FAMILY)) {
                fontFound = true;
                break;
            }
        }

        if (!fontFound) {
            MESSAGE_LABEL.setText("Could not find font family: " + DEFAULT_FONT_FAMILY);
            return;
        }

        /*
        display mode validation
        ----------------------------------------------------------------------------------------------------------------
        */

        DisplayMode[] modes = GFX_DEVICE.getDisplayModes();
        for(DisplayMode mode : modes) {
            if (mode.getWidth() == WANTED_DISPLAYMODE.getWidth() && mode.getHeight() == WANTED_DISPLAYMODE.getHeight() && mode.getBitDepth() == WANTED_DISPLAYMODE.getBitDepth()) {
                displayModeFound1 = true;
                break;
            }
        }

        if (!displayModeFound1) {
            modes = GFX_DEVICE.getDisplayModes();
            for(DisplayMode mode : modes) {
                if (mode.getWidth() == WANTED_DISPLAYMODE_2.getWidth() && mode.getHeight() == WANTED_DISPLAYMODE_2.getHeight() && mode.getBitDepth() == WANTED_DISPLAYMODE_2.getBitDepth()) {
                    displayModeFound2 = true;
                    break;
                }
            }
        }

        if (!displayModeFound1 && !displayModeFound2) {
            MESSAGE_LABEL.setText("Display mode (width: " + WANTED_DISPLAYMODE.getWidth() + ", height: " + WANTED_DISPLAYMODE.getHeight() + ", color depth: 24 or 16 bits) not available.");
            return;
        }

        /*
        full screen validation
        ----------------------------------------------------------------------------------------------------------------
        */

        if (!GFX_DEVICE.isFullScreenSupported()) {
            MESSAGE_LABEL.setText("Full screen not supported on this platform.");
            return;
        }

        FRAME.setCursor(FRAME.getToolkit().createCustomCursor((new ImageIcon(new byte[0])).getImage(), new Point(0,0), "Invisible Cursor"));
        FRAME.setUndecorated(true);
        FRAME.setIgnoreRepaint(true);
        GFX_DEVICE.setFullScreenWindow(FRAME);

        /*
        display change validation
        ----------------------------------------------------------------------------------------------------------------
        */

        if (!GFX_DEVICE.isDisplayChangeSupported()) {
            MESSAGE_LABEL.setText("Display change not supported on this platform.");
            return;
        }

        /*
        setup rest
        ----------------------------------------------------------------------------------------------------------------
        */

        MESSAGE_FRAME.dispose();

        if (displayModeFound1)
            GFX_DEVICE.setDisplayMode(WANTED_DISPLAYMODE);
        else
            GFX_DEVICE.setDisplayMode(WANTED_DISPLAYMODE_2);
    }

    public static void setupTimer() {
        System.out.println("Creating new timer");
        if (timer != null && timerTask != null) {
            timerTask.cancel();
            timer.cancel();
            timer.purge();
        }
        timerTask = new ClockTimerTask();
        timer = new Timer();
        timer.scheduleAtFixedRate(timerTask, 0, 1000);
    }

    public static void main(String args[]) {
        setup();
        /*
        The purpose of this thread is to monitor the timer.
        If computer clock is suddenly set back in time, a new
        timer and timertask will be created.
        */
        Thread timerLauncher = new Thread() {
            public void run() {
                // create initial timer and timertask
                setupTimer();
                while(true) {
                    // computer clock was set back in time, we have to create a new timer
                    if (timerLastRun > System.currentTimeMillis()) {
                        setupTimer();
                    }
                    try {
                        sleep(1000 * 15);
                    } catch (Exception e) { }
                }
            }
        };
        timerLauncher.start();
    }
}
Avatar billede Slettet bruger
07. april 2005 - 17:35 #1
Der er nok næppe tale om et memory leak, når alt kommer til alt.
Jeg troede at den hukommelse som JVM frigav ved GC blev retuneret til operativ systemet. Men det gør den vist ikke. I stedet retuneres den til Javas egen interne heap (der default er 64 MB stor). Så når man kigger i task-manageren ligner det et mem. leak, men er det ikke.

Er det ikke rigtigt forstået?
Avatar billede arne_v Ekspert
07. april 2005 - 21:00 #2
Det er helt rigtigt.

Du kan checke det interne forbrug med:

Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
Avatar billede Slettet bruger
08. april 2005 - 11:28 #3
Det er meget fint. Tak. Svar hvis du vil ha lidt points.
Avatar billede arne_v Ekspert
08. april 2005 - 11:52 #4
ok
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester