views:

125

answers:

3

I've got an item that appears to continuously repaint when it exists, causing the CPU to spike whenever it is in any of my windows. It directly inherits from a JLabel, and unlike the other JLabels on the screen, it has a red background and a border. I have NO idea why it would be different enough to continuously repaint. The callstack looks like this:

Thread [AWT-EventQueue-1] (Suspended (breakpoint at line 260 in sItem)) 
    sItem.paint(Graphics) line: 260 
    sItem(JComponent).paintToOffscreen(Graphics, int, int, int, int, int, int) line: 5124   
    RepaintManager$PaintManager.paintDoubleBuffered(JComponent, Image, Graphics, int, int, int, int) line: 1475 
    RepaintManager$PaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1406  
    RepaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1220   
    sItem(JComponent)._paintImmediately(int, int, int, int) line: 5072  
    sItem(JComponent).paintImmediately(int, int, int, int) line: 4882   
    RepaintManager.paintDirtyRegions(Map<Component,Rectangle>) line: 803    
    RepaintManager.paintDirtyRegions() line: 714    
    RepaintManager.seqPaintDirtyRegions() line: 694 [local variables unavailable]   
    SystemEventQueueUtilities$ComponentWorkRequest.run() line: 128  
    InvocationEvent.dispatch() line: 209    
    summitEventQueue(EventQueue).dispatchEvent(AWTEvent) line: 597  
    summitEventQueue(SummitHackableEventQueue).dispatchEvent(AWTEvent) line: 26 
    summitEventQueue.dispatchEvent(AWTEvent) line: 62   
    EventDispatchThread.pumpOneEventForFilters(int) line: 269   
    EventDispatchThread.pumpEventsForFilter(int, Conditional, EventFilter) line: 184    
    EventDispatchThread.pumpEventsForHierarchy(int, Conditional, Component) line: 174   
    EventDispatchThread.pumpEvents(int, Conditional) line: 169  
    EventDispatchThread.pumpEvents(Conditional) line: 161   
    EventDispatchThread.run() line: 122 [local variables unavailable]   

It basically just continually hits that over and over again as fast as I can press continue. The code that is "unique" to this particular label looks approximately like this:

bgColor = OurColors.clrWindowTextAlert;
textColor = Color.white;
setBackground(bgColor);
setOpaque(true);
setSize(150, getHeight());
Border border_warning = BorderFactory.createCompoundBorder(
        BorderFactory.createMatteBorder(1, 1, 1, 1, OurColors.clrXBoxBorder),
        Global.border_left_margin);
setBorder(border_warning);

It obviously does more, but that particular block only exists for these labels that are causing the spike/continuous repaint.

Any ideas why it would keep repainting this particular label?

+4  A: 

Lots of the code is missing, but I'll take an educated guess.

The "unique" part of the class is probably within a section of the code responsible for rendering the Label. If this is true, then calling all these setXXX() methods probably render the object dirty, which means it needs to be repainted, which will enter this block of code again, which will then use the public interfaces updating the widget, which will then render the object dirty, causing the cycle to repeat.

Eventually such a thing will consume all spare cycles causing the CPU to max for a label that's obviously not doing much.

Try setting the appropriate values in a place which is outside the render loop. Most of these items look like they could be set in a constructor.

--- Edit after confirmation that it's the setBorder(...) ---

Setting a new border likely triggers a recalculation of the bounding box of the widget, as a border might be larger or smaller than the previous border. That and the new border might contain a different screen presentation (raised, lowered, etc) that the old one.

None of these items need to be set in the rendering section, but I'll bet that with the other items, a peliminary check is made to see if the new item equals(...) the old item. If so, then (as an optimization) the dirty bits aren't set and the request to refresh isn't made to the rendering engine.

With a border, such a check would have to cover several elements, including some which are compiled bytecode (the actual drawing instructions). Since it's no longer a simple optimization to check equality when considering borders, odds are they don't attempt to check for equality at all and just flag the widget for repainting.

Edwin Buck
Great ideas, but the logic involved requires them to be set in the render loop unfortunately. There's all kinds of logic that makes things look/behave slightly differently. You were definitely on the right track though as creating that border in the render loop appears to be the problem (see my answer). Unsure why though.
Morinar
The logic to set such items doesn't need to be in the paint() method, setting them outside the paint method will cause the paint method to be called. Think about it, when drawing something, you don't change your mind about what you're drawing, because then you'll need to redraw it. If this Label must be changed, it should be done before drawing it, and the change should then (correctly) trigger the need to redraw.
Edwin Buck
I'm sure you are right, but this code has been in production for 10ish years and I'm not about to throw a wrench in things. Thanks for the border information.
Morinar
A: 

It's the border. Creating the border during every paint loop some how makes it continuously repaint. If I merely create the border once as a class scoped private object and set it in the paint loop, it sets the border correctly and doesn't continuously repaint. If anybody knows why that would make a difference, I'd appreciate that info as well. Feel free to comment on this and I'll accept my answer when I can or add a new answer with tons of verbosity and I'll accept yours.

Morinar
+2  A: 

See the code in JComponent.setBorder(). It compares the old and new border using simple comparison - which will always return false since these are two distinct objects. If the condition holds, the component will be repainted. Hence the infinite loop.

As a general rule - do not call any setters on the component in its paint methods. Just as you saw, setting the border will lead to an infinite repaint loop. This can happen with any other setting, and if not in this version of VM, maybe in the next one. The right way to do this is to change the properties of the component (view) when the model changes and let Swing figure out when it will be repainted - or call repaint() yourself.

Kirill
Matthieu BROUILLARD