Java计时器分配了太多内存

时间:2017-11-12 02:25:49

标签: java multithreading memory-leaks timer scheduledexecutorservice

我注意到我的java项目消耗了大量内存。在使用Visualvm.exe进行简短调查后,我发现了消耗大部分内存的线程,这些是计时器线程。 我在我的项目中使用了三种计时器:

  1. java.util.Timer中
  2. javax.swing.Timer - 与之前相同,但具有更灵活的逻辑,如.restart()和.isRunning()
  3. scheduledExecutorService - 用于具有复杂逻辑的基于时间的较难的任务
  4. 在visualvm中检查时,所有这些线程随着时间的推移在内存消耗中增长,我投入的代码/对象越多,它们在内存中的增长就越快。但是,在一些阈值(我的机器上每个线程大约50-100 MB)之后,应用程序在内存大小上停止增长。

    如果我使用了一个或两个计时器,这不会成为一个问题,但我需要使用很多,这个50-100 MB的阈值会随着时间的推移产生非常显着的内存消耗。

    有人可以就这件事分享经验吗?

    我还尝试用递归线程替换计时器,虽然这种方法很不常见,但它有点奏效。

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.Timer;
    import java.util.TimerTask;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    public class Main {
        private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        private Timer utilTimer = new Timer();
        private javax.swing.Timer swingTimer;
    
        public static void main(String[] args) throws InterruptedException {
            new Main();
        }
    
        private Main() {
            executor.scheduleAtFixedRate(new Runnable() {
                public void run() {
                    Thread.currentThread().setName("executor");
                }
            }, 0, 500, TimeUnit.MILLISECONDS);
    
            utilTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                    Thread.currentThread().setName("utilTimer");
                }
            }, 0, 500);
    
            swingTimer = new javax.swing.Timer(500, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    Thread.currentThread().setName("swingTimer");
                }
            });
            swingTimer.setRepeats(true);
            swingTimer.start();
    
            recursiveThread();
        }
    
        private void recursiveThread() {
            Thread thread = new Thread() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    recursiveThread();
                }
            };
            thread.setName("recursiveThread");
            thread.start();
        }
    }
    

1 个答案:

答案 0 :(得分:0)

每个线程50到100MB听起来非常错误。默认的Java线程堆栈大小为1MB。如果你得到这个大小的内存开销,还有其他事情可以导致它。 (您是否通过java命令行选项更改默认堆栈大小?)

另一个观察是,改变应用程序的体系结构以避免使用大量计时器是个好主意。定时器本质上很昂贵。

但是如果"很多计时器"是不可避免的,也许你应该研究哈希定时器: