Java占用了太多内存
我的Java编写的应用程序占用了太多的内存 程序如何工作:用户从日历(GUI)中选择日期,应用程序将数据加载到JTable组件中。每次加载数据时,都会创建并设置新的TableModel。不创建新的JTable,只创建模型 问题出在哪里?:从日历和加载到JTable的每一个新的日期选择都会消耗大约2-3MB的内存。启动时,应用程序消耗cca 50-60 MB的RAM,在日历上单击几下(如20次)后,应用程序消耗整个堆大小(128MB)。应用程序崩溃,当然 我该怎么办?:我很确定数据库查询是可以的。我可能会以某种方式设置更大的堆大小(我在谷歌上搜索过,但这是我的计算机的唯一解决方案,用户不会这样做),或者我应该以某种方式删除带有DB数据的旧TableModel。但这不是垃圾收集器的工作吗?我可以强制它(System.gc()),但这没有帮助 谢谢你的建议 编辑:处理日历事件的代码(我删除了Javadoc,它是我的母语)Java占用了太多内存,java,heap,Java,Heap,我的Java编写的应用程序占用了太多的内存 程序如何工作:用户从日历(GUI)中选择日期,应用程序将数据加载到JTable组件中。每次加载数据时,都会创建并设置新的TableModel。不创建新的JTable,只创建模型 问题出在哪里?:从日历和加载到JTable的每一个新的日期选择都会消耗大约2-3MB的内存。启动时,应用程序消耗cca 50-60 MB的RAM,在日历上单击几下(如20次)后,应用程序消耗整个堆大小(128MB)。应用程序崩溃,当然 我该怎么办?:我很确定数据库查询是可以的。
你有没有运行过这样的探查器?我怀疑它会显示一些内存泄漏,因为引用在应该释放时被保留。请注意,
System.gc()
是对JVM的提示,并不强制执行gc循环
或者,您的应用程序可能只需要比JVM允许分配的内存更多的内存。JVM最多只能分配一个默认的最大值(取决于您的平台)。尝试通过以下方式增加此值:
java -Xmx256m {classname}
等等,看看这是否能永久解决问题。如果没有,那么这就指向内存泄漏。这里有一个猜测,但正如我在C#中看到的那样,您的日历/控件事件处理程序是否包含对未正确清理的数据的引用?当不再需要句柄时,请确保将其清空,因为循环依赖将导致大量泄漏。请阅读Veijko Krunic的伟大论文。他提出了类似问题的诊断途径 您不需要每次都将日期精确到毫秒。在我看来,一周中的同一天就足够了 就我个人而言,我会想出一种方法来预填充这个日历并缓存它。无需每次都重新创建它。值得一试。如果你每天都重复使用它,为什么每次都要重新创建呢?让计时器在每天午夜重新填充。将其设为只读并允许所有用户共享 也不需要每次都有“新”的日历。我会这样做:
Calendar cal = Calendar.getInstance();
让工厂来发放
我还建议你花点时间看看像JODA这样的图书馆。这肯定比你在这里做的更有效率
更新:也许可以帮助你找出你的内存泄漏。至少这是一个从哪里开始寻找的清单 显然,日历上的每次“点击”都会创建一些对象,这些对象不会被垃圾收集,因此内存使用率会增加,最终会崩溃。在没有实际运行代码的情况下,通过查看您的代码示例,我认为一个可能的罪魁祸首是在此处创建的匿名内部类:
monthView.getSelectionModel().addDateSelectionListener(new DateSelectionListener() {
...
}
您创建的新DateSelectionListener将引用此(WorkerMonthViewHandler),如果不了解initMonthView的使用方式,我无法确切了解这将如何导致问题,但我发现重构作为swing对象侦听器创建的匿名内部类有助于识别并最终解决过去的一些内存泄漏问题。只要侦听器所侦听的swing对象存在,侦听器就会存在,因此即使在您创建了新的WorkerMonthViewHandler(假设原始swing JTable仍然相同)之后,侦听器也会一直存在
如果你想进一步了解这个,试试这个
希望这有帮助。这听起来完全像是Swing组件的内存泄漏。有些组件被多次实例化,并附加到其他组件(通常作为侦听器)上,因此不能对其进行垃圾收集,因为仍然存在对它的有效引用。正如其他人指出的,任何分析器都将帮助您找到源代码
在应用程序开始时拍摄堆快照。然后,在按下按钮大约十次后,拍摄另一个堆快照并进行区分。应该有一组对象,您知道这些对象不应该仍在内存中,但它们是。然后,您可以找到它的引用所在,并修复这些引用。很难从代码中分辨出来,但您是否有可能继续添加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
}
}
public void initMonthView(){
List tasks=wops.getWorkerTasks(workerFrame.getWorker());//db select
for(任务:任务){
如果(!monthView.getSelection().contains(task.getPlannedStart()){
monthView.addFlaggedDates(task.getPlannedStart());
monthView.addFlaggedDates(task.gePlanneEnd());//不太重要
}
}
先生们,谢谢大家的回答。
我感谢每一个回复
我加了我自己的更明显,不能真正选择一个正确的
所以,当你仔细查看我在原始问题中发布的代码时,你会发现这两行代码
wtth = new WorkerTasksTableHandler(workerFrame,week);
wtth.createTable(); // sets model on JTable
结果是,正如你们中的一些人所注意到的,每次新的TableModel都是用它自己的监听器创建的。
所以现在我只重新加载数据(不是整个模型)并使用原始侦听器
看看图片,现在它消耗的RAM太少了,GC实际上可以工作:)
你提到数据库……你能告诉我们更多关于它在做什么,你在使用什么数据库,如果你正在关闭你的结果集等等……似乎更可能是罪魁祸首。此外,你是否保留了对旧模型的任何引用?你在模型中加载了多少数据?你的表有自定义渲染器吗?注意:过去我交换了JTable T所有的模型都包含了数千行……而且从来没有
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
}
}
wtth = new WorkerTasksTableHandler(workerFrame,week);
wtth.createTable(); // sets model on JTable