简体   繁体   中英

Java timers allocate too much memory

I have noticed that my java project consumes a lot of memory. After short investigation using Visualvm.exe I've identified the threads that were consuming most of memory, these were timers threads. I am using three kinds of timers in my project:

  1. java.util.Timer
  2. javax.swing.Timer - almoust same as previous, but with a more flexible logic like .restart() and .isRunning()
  3. scheduledExecutorService - for harder time-based tasks with complex logic

When inspected in visualvm, all of these thread are growing in memory consumption with time, the more code/objects I put into them, the faster they grow in memory. But, after some threshold (around 50-100 mb per thread on my machine), application stops growing in memory size.

This wouldn't be a problem if I've used one or two timers, but I need to use really many, and this 50-100 mb threshold created really significant memory consuption over time.

Can someone please share experiecne on this matter?

I've also tried to replace timer with recursive Thread, and it kinda worked, though it's rather uncommon approach.

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

50 to 100MB per thread sounds very wrong. The default Java thread stack size is 1MB. If you are getting memory overheads of this size, there is something else going on to cause it. (Did you change the default stack size via a java command line option?)

The other observation is that it would be a good idea to change your application's architecture to avoid using lots of timers. Timers are inherently expensive.

But if "lots of timers" are unavoidable, perhaps you should look into hash wheel timers:

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