简体   繁体   中英

Java consumes too much memory

my Java written application consumes way too much memory.

How does program work : User selects a date from calendar (GUI) and application loads data into JTable component. Everytime data is loaded, new TableModel is created and set. No new JTable is created, just model.

What is the problem? : every new day selection from calendar and loading to JTable consumes about 2-3 MB of memory. On start app consumes cca 50-60 MB of RAM, after few "clicks" on calendar (like 20), application consumes full Heap Size (128MB) . Application crashes, of course ...

What should I do? : I am pretty sure database querys are OK. I might somehow set bigger heap size (I googled, but that would be only solution for my computer, users wont do this) OR I should somehow remove old TableModel with DB data. But shouldn't this be Garbage collector's work? I am able to force it (System.gc()) but that doesn't help ...

Thank you for any advice!

EDIT : Code for Handling events of calendar (I deleted Javadoc, it is in my mother tongue)

package timesheet.handlers;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import org.jdesktop.swingx.JXMonthView;
import org.jdesktop.swingx.event.DateSelectionEvent;
import org.jdesktop.swingx.event.DateSelectionListener;
import timesheet.database.WorkerOperations;
import timesheet.frames.WorkerFrame;
import timesheet.logictrier.*;


public class WorkerMonthViewHandler {
    private JXMonthView monthView;
    private WorkerFrame workerFrame;
    private WorkerOperations wops;
    private Date[] week = new Date[5];
    private WorkerTasksTableHandler wtth;

    public WorkerMonthViewHandler(WorkerFrame workerFrame) {
        this.workerFrame = workerFrame;
        this.monthView = workerFrame.getWorkerMonthView();
        wops = workerFrame.getWorkerOperations(); // for DB usage
    }

    public void initMonthView() {
        List<Task> tasks = wops.getWorkerTasks(workerFrame.getWorker()); // db select
        for (Task task : tasks) {
            if (!monthView.getSelection().contains(task.getPlannedStart())) { 
                monthView.addFlaggedDates(task.getPlannedStart());
                monthView.addFlaggedDates(task.gePlannedEnd()); // not really important
            }
        }
        monthView.setSelectionDate(new Date());
        monthView.getSelectionModel().addDateSelectionListener(new DateSelectionListener() {
            public void valueChanged(DateSelectionEvent dse) {
                Date d = monthView.getSelectionDate();
                for (int i=0; i<week.length; i++) {
                    if (d.equals(week[i])) {     
                        return;
                    }
                }
                Calendar cal = new GregorianCalendar();
                cal.setTime(d);
                long dayMs = 24 * 60 * 60 * 1000;
                switch (cal.get(Calendar.DAY_OF_WEEK)) {
                    case(Calendar.MONDAY) : {
                        week[0] = new Date(cal.getTimeInMillis());
                        week[1] = new Date(cal.getTimeInMillis()+dayMs);
                        week[2] = new Date(cal.getTimeInMillis()+2*dayMs);
                        week[3] = new Date(cal.getTimeInMillis()+3*dayMs);
                        week[4] = new Date(cal.getTimeInMillis()+4*dayMs);
                    } break;
                    case (Calendar.TUESDAY) : {
                        week[0] = new Date(cal.getTimeInMillis()-dayMs);
                        week[1] = new Date(cal.getTimeInMillis());
                        week[2] = new Date(cal.getTimeInMillis()+1*dayMs);
                        week[3] = new Date(cal.getTimeInMillis()+2*dayMs);
                        week[4] = new Date(cal.getTimeInMillis()+3*dayMs);
                    } break;
                    case (Calendar.WEDNESDAY) : {
                        week[0] = new Date(cal.getTimeInMillis()-2*dayMs);
                        week[1] = new Date(cal.getTimeInMillis()-dayMs);
                        week[2] = new Date(cal.getTimeInMillis());
                        week[3] = new Date(cal.getTimeInMillis()+1*dayMs);
                        week[4] = new Date(cal.getTimeInMillis()+2*dayMs);
                    } break;
                    case (Calendar.THURSDAY) : {
                        week[0] = new Date(cal.getTimeInMillis()-3*dayMs);
                        week[1] = new Date(cal.getTimeInMillis()-2*dayMs);
                        week[2] = new Date(cal.getTimeInMillis()-1*dayMs);
                        week[3] = new Date(cal.getTimeInMillis());
                        week[4] = new Date(cal.getTimeInMillis()+1*dayMs);
                    } break;
                    case (Calendar.FRIDAY) : {
                        week[0] = new Date(cal.getTimeInMillis()-4*dayMs);
                        week[1] = new Date(cal.getTimeInMillis()-3*dayMs);
                        week[2] = new Date(cal.getTimeInMillis()-2*dayMs);
                        week[3] = new Date(cal.getTimeInMillis()-dayMs);
                        week[4] = new Date(cal.getTimeInMillis());
                    } break;
                    case (Calendar.SATURDAY) : {
                        week[0] = new Date(cal.getTimeInMillis()-5*dayMs);
                        week[1] = new Date(cal.getTimeInMillis()-4*dayMs);
                        week[2] = new Date(cal.getTimeInMillis()-3*dayMs);
                        week[3] = new Date(cal.getTimeInMillis()-2*dayMs);
                        week[4] = new Date(cal.getTimeInMillis()-dayMs);
                    } break;
                    case (Calendar.SUNDAY) : {
                        week[0] = new Date(cal.getTimeInMillis()-6*dayMs);
                        week[1] = new Date(cal.getTimeInMillis()-5*dayMs);
                        week[2] = new Date(cal.getTimeInMillis()-4*dayMs);
                        week[3] = new Date(cal.getTimeInMillis()-3*dayMs);
                        week[4] = new Date(cal.getTimeInMillis()-2*dayMs);
                    } break;
                }
                wtth = new WorkerTasksTableHandler(workerFrame,week);
                wtth.createTable(); // sets model on JTable
            }
        });
    }

    public void reportTask() {
        wtth.reportTasks(); // simple DB insert
    }
}

Using NetBeans profiler : Date taken: Sun Feb 28 14:25:16 CET 2010 File: C:...\\private\\profiler\\java_pid4708.hprof File size: 72,2 MB

Total bytes: 62 323 264
Total classes: 3 304
Total instances: 1 344 586
Classloaders: 18
GC roots: 2 860
Number of objects pending for finalization: 0

Have you run a profiler like YourKit against this ? I suspect it'll show some memory leak due to references being held when they should be released. Note that System.gc() is a hint to the JVM, and doesn't force a GC cycle.

Alternatively , your application may simply require more memory than the JVM is permitted to allocate. The JVM will only allocate up to a default maximum (dependent on your platform). Try increasing this via:

java -Xmx256m {classname}

etc. to see if this fixes the problem permanently. If it doesn't, then this points to a memory leak.

Read Veijko Krunic's great paper How to Fix Memory Leaks in Java . He suggests a diagnostic path for similar problems.

Clearly some amount of objects are getting created for each 'click' on the calendar, These objects aren't getting garbage collected, hence the increasing memory usage and eventual crash. Without actually running the code, from looking at your code sample I'd say a likely culprit is the anonymous inner class created here:

monthView.getSelectionModel().addDateSelectionListener(new DateSelectionListener() {
  ...
}

The new DateSelectionListener you create will have a reference to this (the WorkerMonthViewHandler), I can't see exactly how that would cause an issue without knowing more about how initMonthView is used, but I've found refactoring anonymous inner classes created as listeners on swing objects has helped identify and ultimately solve a number of memory leaks in the past. The listeners will exist as long as the swing object they are listening to exists, so will hang around even after you create a new WorkerMonthViewHandler assuming the original swing JTable is still the same.

If you want some further reading on this try this, http://www.javalobby.org/java/forums/t19468.html .

Hope this helps.

A wild guess here, but as I see this a lot in C#, are your calendar/controls event handlers holding references to data that doesn't get cleaned up properly? Make sure you null out handles when you no longer need them as circular dependencies will lead to large leaks.

You don't need dates down to millisecond accuracy every time. It looks to me like having the same day of week is sufficient.

Personally, I'd figure out a way to prepopulate this calendar and cache it. No need to re-create it each time. It's worth a try. If you reuse it each and every day, why re-create it each time? Have a Timer repopulate it at midnight each day. Make it read-only and allow all users to share it.

Don't need a "new" Calendar each time, either. I'd do it this way:

Calendar cal = Calendar.getInstance();

Let the factory dole it out.

I'd also recommend looking at a library like JODA for time. It's certain to be more efficient than what you're doing here.

UPDATE: Maybe this can help you ferret out your memory leak. At least it's a checklist of where to start looking.

This entirely sounds like a memory leak with your Swing components. There's some component which is being instantiated multiple times, and attached to something else (usually as a listener) so it can't be garbage collected, since there's still a valid reference to it. As someone else pointed, any profiler will help you find the source.

Take a heap snapshot at the beginning of the application. Then after you hit the button about ten times, take another heap snapshot and do a diff. There should be a set of objects which you know shouldn't still be in memory, but are. Then you can find out what's holding the reference to it, and fix those.

It's hard to tell from the code, but any chance you are continuously adding FlaggedDates ?

public void initMonthView() {
    List<Task> tasks = wops.getWorkerTasks(workerFrame.getWorker()); // db select
    for (Task task : tasks) {
        if (!monthView.getSelection().contains(task.getPlannedStart())) { 
            monthView.addFlaggedDates(task.getPlannedStart());
            monthView.addFlaggedDates(task.gePlannedEnd()); // not really important
        }
    }

Gentelmen, thank you all for answers. I appreciate every single response .

I added my own to be more visible, cant really choose one correct.

So when you look more carefully to the code I posted in original question, you'll find these two lines

        wtth = new WorkerTasksTableHandler(workerFrame,week);
        wtth.createTable(); // sets model on JTable

Result is, everytime NEW TableModel is created with its own Listener, as some of you noticed. So now I only reload data (not whole model) and use original Listener.

Take a look to the picture, now it consumes way too less RAM and GC actually works :) 替代文字

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