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?

Ordous
  • 3,844
  • 15
  • 25
  • 2
    Does it help to construct and manipulate the Swing GUI objects on the [event dispatch thread](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html)? – trashgod Sep 01 '17 at 20:31
  • @trashgod 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. – Ordous Sep 02 '17 at 11:06
  • If your code doesn't reach a safe point, you may have to yield explicitly, for [example](https://stackoverflow.com/q/35154352/230513). – trashgod Sep 02 '17 at 11:47
  • @trashgod 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? – Ordous Sep 04 '17 at 18:47
  • Your example runs on the [initial thread](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html). – trashgod Sep 04 '17 at 18:57
  • @trashgod 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. – Ordous Oct 31 '17 at 20:41
  • @trashgod 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 https://docs.oracle.com/javase/7/docs/api/javax/swing/Timer.html, so it on EDT anyway. – Antoniossss Oct 31 '17 at 20:45
  • @Antoniossss 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. – Ordous Oct 31 '17 at 20:47
  • 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 issue – Antoniossss Oct 31 '17 at 20:50
  • 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. – Antoniossss Oct 31 '17 at 20:50
  • @Antoniossss The result is the same, although I have to wait a bit with it minimized if the interval is 10 seconds – Ordous Oct 31 '17 at 20:54
  • But to be honest I dounbt that this is java related question as environement includes uncrontrolled VM and remote desktops. – Antoniossss Oct 31 '17 at 20:54
  • @Ordous 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 :) – Antoniossss Oct 31 '17 at 20:55
  • @Antoniossss 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). – Ordous Oct 31 '17 at 20:59
  • 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 ;) – Antoniossss Oct 31 '17 at 21:05
  • 1
    @Antoniossss 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. – Ordous Oct 31 '17 at 21:18
  • AAAh and you are using OpenJDK - that was worth mentioning:) Yes you can submit a bug report and let real speciallists handle this :) – Antoniossss Oct 31 '17 at 21:19
  • @Ordous: Is [this](https://docs.oracle.com/javase/8/docs/technotes/guides/2d/flags.html#d3d) relevant? If the VM gets a single processor, it might expose a latent race condition. – trashgod Oct 31 '17 at 22:48
  • The relevant part is that is OpenJDK not oracle's jvm ;P – Antoniossss Oct 31 '17 at 23:11
  • @Antoniossss 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. – Ordous Oct 31 '17 at 23:52
  • @Ordous can you post an answer on this if it is resolved? Or ask Antoniossss to do so? – River Nov 07 '17 at 16:10
  • @River 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. – Ordous Nov 07 '17 at 16:24
  • 1
    @RootG How is that a possible duplicate?... – Ordous Nov 09 '17 at 14:01
  • @Ordous 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. – Görkem Mülayim Nov 09 '17 at 14:33

1 Answers1

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

Ordous
  • 3,844
  • 15
  • 25