Swing using lots of CPU when calling repaint in a minimized RDP session
Asked Answered
H

1

11

I'm observing some weird behavior with Java 8 (several versions, in particular 1.8.0_111) when running a Swing app in a VM. The VM is a Windows 10 machine running in VMware that I am remote-desktoping into. I have not tried to do this with an actual desktop, rather than a VM, but am planning to ASAP to potentially remove an extra failure point.

I've managed to reproduce it with this minimal program:

public static void main(String[] args) {
    SwingUtilities.invokeLater(() -> {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel panel = new JPanel();
        for (int i = 0; i < 3; i++) {
            JPanel subpanel = new JPanel();
            JComboBox<?> box = new JComboBox<>();
            subpanel.add(box);
            panel.add(subpanel);
        }
        frame.add(panel);
        frame.pack();
        frame.setVisible(true);

        Timer timer = new Timer(1000, e -> {
            frame.repaint();
        });
        timer.setRepeats(true);
        timer.start();
    });
}

Now if I just start it normally, it repaints, no problems at all, just a frame with 3 empty combo boxes, as expected. The problem appears if I minimize the RDP session window. If that happens (and the frame was not iconified), then Swing starts eating up unhealthy amounts of CPU until I open the RDP window again.

I have tried minimizing the code example further, however reducing the combo-box count to 2, removing the subpanel or setting a single-fire timer (rather than repeating, even if the repaint would happen while the RDP is minimized) all prevented the bug from happening.

Here's the CPU utilization graph:
http://i67.tinypic.com/23rwglx.png

I've tried profiling the application during these spikes to try and see what the hell is happening. Here's the result from the profiler in JVisualVM:
http://i68.tinypic.com/apdwed.png

And the sampler (after removing the package filter):
http://i67.tinypic.com/2071735.png

I couldn't readily see what could be eating up the CPU in ProcessingRunnable. Does anyone have any experience with what Swing does when it suddenly has no screen to draw onto?

Hardware answered 1/9, 2017 at 19:14 Comment(24)
Does it help to construct and manipulate the Swing GUI objects on the event dispatch thread?Panaggio
@Panaggio Unfortunately I won't be able to answer that conclusively until Monday, but I highly doubt it - the main application where this is a problem constructs it's components entirely on the EDT.Hardware
If your code doesn't reach a safe point, you may have to yield explicitly, for example.Panaggio
@Panaggio I don't quite understand - in this case it seems that it's the EDT that is in a busy wait, not another thread, so how can I yield if I don't have any custom painting code?Hardware
Your example runs on the initial thread.Panaggio
@Panaggio So Monday took a while to come (got sidetracked by other projects), but I've been able to carve out some time for this again. I had some troubles reproducing the effect reliably previously, and I think some of the results were false (from another background window in the same session). But I've updated the code to use only the EDT, and it still persists (see update). I've also tried to remove any fluff, but removing the subpanel or reducing the count to 2 or making the timer non-repeating all prevent the issue.Hardware
@Panaggio to be honest, I think its still on EDT due to the Timer nature, and looks like documentation says im right, quote the action event handlers for Timers execute on another thread -- the event-dispatching thread from docs.oracle.com/javase/7/docs/api/javax/swing/Timer.html, so it on EDT anyway.Mcdevitt
@Mcdevitt The Timer does fire the event on the EDT, you are right, but trashgod was correct, in that Swing components should all be constructed on the EDT, regardless of where they are used later. However, it's a non-issue, since the problem persists when the entire thing happens on the EDT as well.Hardware
But this is not "component" but utitlity. By Component we should mean controlls and containers, like JLabel, JButton or JPanel. But as you said is not issueMcdevitt
Is the result the same if you change the interval to 100 and 10000 ?? If so, the problem is not with the code you have shown.Mcdevitt
@Mcdevitt The result is the same, although I have to wait a bit with it minimized if the interval is 10 secondsHardware
But to be honest I dounbt that this is java related question as environement includes uncrontrolled VM and remote desktops.Mcdevitt
@Hardware so I guess Im right, this is not java (code) related. Who knows what is happening with minimalized RDP client and with VM that its connected to it (on the same machine). Maybe someone will :)Mcdevitt
@Mcdevitt Well, I don't expect this to be general knowledge at all, rather some weird interaction between screen size and some internal Swing code. However it's clear that there is something going on in the VM (it's the CPU hog) and it's connected to this code (like I posted in the question - even removing one of the combo boxes prevents this).Hardware
I think its just coincidence, component component count change to N+-1 would have no impact at all, especially for such static controll like combobox. I belive its happening, but only by accident its connected to controlls count (or you only thought it was). Lets stay tuned ;)Mcdevitt
@Mcdevitt See last update - you are right. Changing the stack away from D3D fixes everything up nicely. What would be the next step here? Posting a bug report for OpenJDK? I'm not quite familiar with the environment around these things.Hardware
AAAh and you are using OpenJDK - that was worth mentioning:) Yes you can submit a bug report and let real speciallists handle this :)Mcdevitt
@Ordous: Is this relevant? If the VM gets a single processor, it might expose a latent race condition.Panaggio
The relevant part is that is OpenJDK not oracle's jvm ;PMcdevitt
@Mcdevitt Actually, I wrote that because that was the first place I found a similar bug when googling. I am using Oracle's JVM, sorry for the confusion.Hardware
@Hardware can you post an answer on this if it is resolved? Or ask Antoniossss to do so?Pedestal
@Pedestal I was hoping to get an answer from Oracle before I do that (I've posted this as a bug report). Not sure how quickly they answer though, or whether they'll be able to reproduce it easily, if they don't do it within a couple of days of the bounty expiring I'll post an answer with the JDK bug links and workaround.Hardware
@RootG How is that a possible duplicate?...Hardware
@Hardware Sorry may bad. I used to have CPU usage issue with setVisible method and I solved the problem with the answers in that link, It seems that there is no information about CPU usage. I have removed the link.Affluent
H
2

I have posted this as a bug report to Oracle. I'll update this answer as any details emerge (it's accepted or rejected) with the link.

A couple of similar bugs were already posted with "Cannot reproduce" as the resolution, however they do not involve VMs, only RDP sessions (I have not been able to trigger this bug by RDPing to my work desktop, rather than a VM).

I did find a workaround - apparently switching away from D3D stack and forcing OpenGL stack (-Dsun.java2d.d3d=false option when launching) prevents this from happening. I've now added some code to check whether the app is being started on a VM and set the options accordingly.

EDIT
The bug report has been accepted, although is in "Cannot reproduce" for the moment. http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8191018

Hardware answered 8/11, 2017 at 18:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.