День добрый.
Возникла следующая проблема. К сожалению весь проект большой, выкладывать бессмысленно.
Программа реализует подбор параметров некоего регрессора путем многократных запусков с разными параметрами.
Каждый запуск производится в отдельном потоке.
Поскольку вариантов слишком много, создал специальный диалог - монитор, который дает разрешение на создание и запуск новых потоков.
Во время тестирования каждого варианта производится отображение процента выполнения на ProgressBar в том-же диалоге-мониторе. Причем для каждого потока создается свой ProgressBar (точнее TestPanel), размещаемый в диалоге. Число ProgressBar не превышает 7 для 4-ядерной машины, причем только три тестовых потока работают (специально ограничил, загрузка процессора колеблется в районе 75%).
Если тестирование одного набора параметров занимает хотя бы несколько секунд, все работает нормально.
А вот если тестирование происходит почти мгновенно, то диалог-монитор вообще не обновляется и через некоторое время выводится сообщение out of memory.
Отследил число Thread - не изменяется, то есть старые, отработавшие, освобождаются.
freeMemory конечно пляшет, но да это нормально. Если тесты работают долго, то снижения общего объема памяти не наблюдается.
А вот если тесты отрабатывают быстро, то память относительно быстро кончается (где-то после полутора тысяч тестов).
Текст монитора:
Код |
package myProgress;
import java.awt.BorderLayout; import java.awt.Container; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.SwingUtilities;
/** * This class serves to monitor the state of serial tests. * Each test is identified by thread number and has its own * proress bar and information label. * All tests can write to protocol windows */ public class TestProgressMonitor extends JDialog { @SuppressWarnings("compatibility:-1122537410441458989") private static final long serialVersionUID = -4159357951657064586L;
//For singleton private static final TestProgressMonitor me = new TestProgressMonitor(null);
//Protocol area private final JTextArea protocol = new JTextArea();
//CloseButton private JButton stopB = new JButton("Close monitor");
//To maintain threads private int numProc = 1; private int applics = 0;
//Panel for test subpanels private final JPanel tests = new JPanel(); private transient ArrayList<TestPanel> testPanels = new ArrayList<TestPanel>();
private TestProgressMonitor(JDialog d) { super(d, "Tests monitor", true); setDefaultCloseOperation(0); Container cont = getContentPane(); cont.setLayout(new BorderLayout()); cont.add(new JScrollPane(protocol), BorderLayout.CENTER); tests.setLayout(new BoxLayout(tests, BoxLayout.Y_AXIS)); cont.add(tests, BorderLayout.NORTH); Runtime rt = Runtime.getRuntime(); int n = rt.availableProcessors(); if (n > 2) numProc = n - 1; stopB.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { synchronized (stopB) { stopB.notifyAll(); } me.setVisible(false); } }); cont.add(stopB, BorderLayout.SOUTH); setSize(500, 500); setLocationRelativeTo(null); }
/** * @return the single instance of this class object */ public static TestProgressMonitor getInstance() { return me; }
/** * Define the possibility of start new test */
public boolean mayStart(TestPanel tp) { return testPanels.indexOf(tp) < numProc; }
/** * @return null if adding granted and protocol to wait otherwise */ public Object mayAdd() { if (testPanels.size() + applics > numProc * 2) return protocol; else { applics++; System.out.println("Grant new "+applics); return null; } }
/** * start the monitor */ public synchronized void start() { SwingUtilities.invokeLater(new Runnable() { public void run() { protocol.setText(""); me.setVisible(true); } }); }
/** * Add new row to protocol */ public void appendProt(final String str) { SwingUtilities.invokeLater(new Runnable() { public void run() { protocol.append(str + "\n"); } }); }
/** * return lock which notified only when dialog closed */
public final Object getLock() { return stopB; }
/** * Return exist panel for test which is asked. If there is no such panel - create it! */ public synchronized TestPanel getMyPanel() { Thread tr = Thread.currentThread(); for (TestPanel tp : testPanels) if (tr.equals(tp.thread)) return tp; //There is no such thread. Create! final TestPanel tp = new TestPanel(); testPanels.add(tp); applics--; SwingUtilities.invokeLater(new Runnable() { public void run() { stopB.setEnabled(false); tests.add(tp.pan); me.validate(); me.repaint(); } }); System.out.println("Create new "+applics+" "+testPanels.size()+" "+Thread.activeCount()+" "+Runtime.getRuntime().freeMemory()); return tp; }
/** * @param tp is the TestPanel to remove */ public synchronized void stop(final TestPanel tp) { tp.thread = null; notify(); synchronized (protocol) { protocol.notify(); } testPanels.remove(tp); System.out.println("Remove "+testPanels.size()); SwingUtilities.invokeLater(new Runnable() { public void run() { if (testPanels.size() == 0) stopB.setEnabled(true); tests.remove(tp.pan); me.validate(); me.repaint(); } }); }
/** * This class serves for work with one test. */ public class TestPanel { //Panel for test data protected final JPanel pan = new JPanel(); protected final JLabel lab = new JLabel("Start"); protected final JProgressBar pb = new JProgressBar(0, 1);
//For threade identifying protected Thread thread;
public TestPanel() { thread = Thread.currentThread(); pan.setLayout(new BoxLayout(pan, BoxLayout.Y_AXIS)); pan.add(lab); pan.add(pb); }
/** * @param text is new text for label * @param size is the size to add to current maximum */ public void setOptions(final String text, final int size) { SwingUtilities.invokeLater(new Runnable() { public void run() { pb.setMaximum(size + pb.getMaximum()); lab.setText(text); validate(); repaint(); } }); }
/** * @param size is the size to add to current maximum */ public void setOptions(final int size) { SwingUtilities.invokeLater(new Runnable() { public void run() { pb.setMaximum(size + pb.getMaximum()); repaint(); } }); }
/** * Add 1 to current value */ public void addStep() { SwingUtilities.invokeLater(new Runnable() { public void run() { pb.setValue(pb.getValue() + 1); } }); }
/** * Stop work of this test */ public void stop() { me.stop(this); } } }
|
У меня сложилось впечатление, что поскольку отрисовка не происходит, то в EDT происходит накопление запросов, каждый из которых ссылается на свой TestPanel. Как следствие TestPanel относящиеся к уже отработавшим тестам не освобождаются.
Вопрос, как принудить EDT работать?
Если заменить все invokeLater на invokeAndWait - начнут тормозить тесты, не хотелось бы.
Что посоветуете?