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();
}
}
