0

Experts, need some help.

I have created JFrame in say class 'A' and have added JTabbedPane using NetBeans IDE, also have added a first JPanel to this JTabbedPane. On this JPanel, I have JCheckbox that adds and removes new tab (instance of JPanel) based on the checked/unchecked event. The panel being added & removed is defined in another say class 'B' that extends JPanel. This JPanel has a timer task which runs in a specific interval, get some data from REST resource and update the contents in the JPanel's body as shown below:

private void refreshAgentUtilizationData() {
    TimerTask updateAgentDetailsTask = new TimerTask() {
        @Override
        public void run() {
            agentObj.updateData();
            javax.swing.SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    memChart.repaint();
                    System.out.println("This is from Agent monitor timer task...");
                }
            });
        }
    };
    agentMonTimer.scheduleAtFixedRate(updateAgentDetailsTask, 0, master_pollingInterval);
}

JCheckBox action performed (in class 'A') looks below,

Some details first:

AgentMon_ChartsUI = Class that extends JPanel and being added to JTabbedPane i.e. class B

agentMonTabs = JTabbedPane that resides in say class A

    private void agentMonSwitchActionPerformed(java.awt.event.ActionEvent evt) {                                               
    if (agentMonSwitch.isSelected()) {
        AgentMon_ChartsUI agentChartPane = new AgentMon_ChartsUI();
        Icon agentIcon = new javax.swing.ImageIcon(getClass().getResource("/resources/abc.png"));
        agentMonTabs.addTab("Agent runtime monitor", agentIcon, agentChartPane);
        agentMonTabs.setSelectedIndex(agentMonTabs.indexOfTab("Agent runtime monitor"));
    } else {
        agentMonTabs.remove(agentMonTabs.indexOfTab("Agent runtime monitor"));
    }
}

The problem is: I am not able to find graceful way to dispose the instance of JPanel which is removed on checkbox's uncheck event. When I uncheck it, I can see the tab is being successfully removed and it looks like the panel is now gone, but I can see that the System.out.println... is still being executed in timer's job. This means that Jcheckbox > Uncheck simply removes the tab but does not dispose it.

I checked other questions on StackOverflow (this & this) and it is confirmed that once the references are set to null, the GC will take care of it. I am not sure in this case, how should I set the reference to null, as I am simply removing the panel from JTabbedPane. I monitored my application for good amount of time and I didn't see GC clearing it out. Am I looking at it correctly? What is correct & recommended way to dispose/nullify the panel which was removed from JTabbledPane?

Stack2Heap
  • 75
  • 2
  • 12
  • Either provide some kind of `dispose` method for `B` which `A` can call, it's a little messy as you need to cast the result of the tab to `B` or override `removeNotify` of `B` which will tell you when the component is removed from it's parent container, don't forget to call it's super first – MadProgrammer Apr 20 '20 at 22:14
  • Another idea might be to have a seperate controller for the timer (which is linked to the panel), so when you remove the panel for the UI you can instruct the controller to also dispose of the associated timer – MadProgrammer Apr 20 '20 at 22:31

2 Answers2

2

There are a number of possible ways you might deal with this. One of the simplest would be to override the removeNotify method of the JPanel. This is called when ever the component is removed from it's parent container.

@Override
public void removeNotify() {
    super.removeNotify()
    agentMonTimer.cancel();
    agentMonTimer.purge();
    agentMonTimer = nil;
}

NB: You can cancel the TimerTask instead, if the Timer is shared, but you will need to maintain a reference to it when you create it

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thank you @MadProgrammer! removeNotify() turned out as lifesaver. I am writing JVIsualVM like resource monitor specific to my requirements and end users will be adding/removing loads of panels. With your help, I can at least feel relaxed knowing that no one is hogging resources in the background, thanks! – Stack2Heap Apr 22 '20 at 00:44
0

I'm not sure I'm following all of this, but it sounds like the tab you are trying to dispose of is represented by a JPanel subclass B which contains a reference to a timer, and that the evidence that the timer is continuing to run is your evidence that B still exists?

But if the timer existence is dependent on that instance of B, wouldn't something in the timer infrastructure have a reference to B? It is not the least clear to me why you aren't trapping the event and disposing of the timer class as well, you can't expect dispose code to do that for you.

In addition, you cannot just monitor the program for a period and decide whether the GC has done whatever it will do. What instances it cleans up, and how soon and so forth, are not entirely time-based. The ones I've spent any time with don't run until the program is low on memory, to start with.

arcy
  • 12,845
  • 12
  • 58
  • 103
  • Thanks for inputs. Methods referred to in Timer task are mostly void() and just updates some charts/fields hence they are not really aware of parent container's action. I could fix the problem with @MadProgrammer's suggestion. Thanks for your help too :) – Stack2Heap Apr 22 '20 at 00:47