0

Is there a good way to work with set/getPreferredSize, set/getMinimumSize, and set/getMaximumSize (or a way around them) without allocating a Dimension object every now and then? (Other than extending the JComponents)

My application makes intensive use of this methods on a mouse drag for resize/move and I'm concerned about the all the Dimension objects laying around.

I know methods like set/getBounds can work with an already allocated object passed to them, I can't seem to find the equivalent for the other methods.

Bigger
  • 1,807
  • 3
  • 18
  • 28
  • 2
    This shouldn't be a problem - ever. The Dimension objects are very light-weight. – Niels Abildgaard May 27 '13 at 14:28
  • When programming for android even the iterators limit the use of Lists on rendering loops (soon you get the OutOfMemoryException). This isn't an android-related question though, but I'm just saying this isn't exactly micro-optimization when on a render loop. – Bigger May 27 '13 at 14:31
  • And if this wasn't a concern on the first place, then why would they take the effort to make getBounds(Rectangle rv), getLocation(Point p) and getSize(Dimension d)? Short answer is, Preferred/Minimum/Maximum are not ment to change so often. What to do in my particular case?, that's the question. – Bigger May 27 '13 at 14:35

4 Answers4

4

Is there a good way to work with set/getPreferredSize, set/getMinimumSize, and set/getMaximumSize

Yes: don't use them! Use an appropriate LayoutManager instead. LayoutManager's are responsible for computing those values, so the good way is to delegate this to LayoutManager. BorderLayout and GridBagLayout provide already a very good starting point for layout. You may from time to time have to use FlowLayout and/or GridLayout. If you are willing to use third-party libraries, MigLayout is also a valid LayoutManager.

Consider reading this answer about Should I avoid the use of set[Preferred|Maximum|Minimum]Size methods in Java Swing?

Anyway, Dimension objects have a very small memory footprint and are usually garbage collected very soon after being instantiated, so you should not worry about them.

If you feel that you are having performance issues, use a profiler to help you find them.

Community
  • 1
  • 1
Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
  • No, Layout managers are responsible for *reading* those values, interpreting them and figuring out the right bounds. You are supposed to set the Preferred, Minimum and Maximum values (preferably) once. What you are not supposed to touch is setSize and setLocation. – Bigger May 27 '13 at 15:03
  • 2
    @マルちゃんだよ No you should not call those methods (read the link I provided). `LayoutManager` are reponsible of computing those preferred size, not you. If you want to force the size of a custom component, then override `getPreferredSize()`. – Guillaume Polet May 27 '13 at 15:06
  • The link points to a question about personal preferences. For the sake of being objective, at least BoxLayout, GroupLayout and SpringLayout (AFAIK), read those values. Other layouts like Grid, GridBag, etc, *don't use* them, because of the way the are designed (ie: GridLayout stretches everything to fill the container). http://docs.oracle.com/javase/tutorial/uiswing/layout/index.html. Please take a look at http://docs.oracle.com/javase/tutorial/uiswing/layout/custom.html speciffically the description of the layoutContainer method to understand what a Layout Manager does. – Bigger May 27 '13 at 15:18
  • We are getting a bit off-topic anyway. I don't want to use getPreferredSize, I want to manipulate its Dimension object directly or indirectly without allocating anything. – Bigger May 27 '13 at 15:26
  • @マルちゃんだよ `GridLayout` and `GridBagLayout` use them as well (but they don't necessarily respect them, depending on the constraints provided). Preferred size are computed recursively based on the preferred size of the contained children (and this is done by the `LayoutManager`).Eventually, you end up on "non-container" components (such as `JCheckBox, JTextField, JTextArea`, ...)which implement their preferred size by delegating this to their associated `XXXUI`, or you end up on Custom components where it is ok to override `getPreferredSize()`. Still, there is no need to call `setPreferredSize()` – Guillaume Polet May 27 '13 at 15:33
  • The layout manager won't call setPreferredSize to overwrite the values you set if you use it, so it's up to you to either use the method the API provided *or* to override the getPreferredSize method to do some fancy dynamic stuff inside there. This whole bikeshed talk doesn't apply here since a totally new set of Container-Relative (as opposed to the traditional component-relative) layout managers have been written for this application, which happens to be a GUI builder. My question, again, is about (J)Component internals, and a way to hack them. – Bigger May 27 '13 at 15:43
  • @マルちゃんだよ Then read the code of `JComponent` and `Container`. But you will very soon see that those methods rely on the XXXUI or on the `LayoutManager`'s. I have written a GUI-builder myself too and there is absolutely no need to call `setPreferredSize()`. And the link provided above those not point to a set of "personal preferences" but to good practices which provides better code especially if you want your code to work across various platforms and various Look&Feel (calling `setPreferredSize` will then be a nightmare in that situation). – Guillaume Polet May 27 '13 at 15:47
2

You would have to allocate at least thousands of these objects per second to even begin worrying. HotSpot's GC scheme is specifically optimized towards the low cost of short-lived objects. They get allocated in the Eden space and, if there are no survivors when Eden fills up, all are released by just updating a pointer. So the best advice is to keep an eye on other performance issues first; this one should have a very low priority.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • This one is called in a render loop, so one per ten millisecond is almost as big as thousands per second, sorry I should have clarified this is for some *kind of* rendering loop. – Bigger May 28 '13 at 03:24
  • 1
    That's a mere hundred objects per second. – Marko Topolnik May 28 '13 at 03:55
  • 1
    Well, then you at least have got a case to consider. Did you monitor allocation with `visualgc`, or at least with `jvisualvm`? You should see a rapid pace of heap growth and shrinkage---that's memory churn. If you see that, then you *may* have an allocation-related issue, still probably not accounting for more than 10-20% of performance. – Marko Topolnik May 28 '13 at 07:17
  • 1+ Nice tools, I'll check them out when I get my hands on the code again. – Bigger May 28 '13 at 07:23
1

"I'm concerned about the all the Dimension objects laying around."

Don't worry about a few thousand micro objects that die quickly. The GC can handle that. If you run into any performance problems they won't be fixed by avoiding a few Dimension creations. You should be aware that calls like setPreferredSize() have a much higher cost internally than that puny Dimension you pass as a parameter (Check out the JRE source, its doing a lot more internally).

Durandal
  • 19,919
  • 4
  • 36
  • 70
  • Indeed, checking the code I see it fires a PropertyChanged event which seems costly and redundant the way I'm using it. This, however, is something I can work around and I would like to focus on the Dimension object, where it comes from and where it goes. – Bigger May 28 '13 at 03:32
0

I'm interested in JComponent's get/setPreferredSize mechanism and allocation behavior

Note: I'm not interested in: how the default layout managers work. Let's think for a moment I'm using preferredSize values for an X purpose not related to default layout managers.

The official documentation fails to give a solution, so let's look at the JRE source code:

This one calls the super...

//JComponent.java (1642 ~ 1663)
public Dimension getPreferredSize() {
   if (isPreferredSizeSet()) {
       return super.getPreferredSize();
   }

   Dimension size = null;
   if (ui != null) {
       size = ui.getPreferredSize(this);
   }
   return (size != null) ? size : super.getPreferredSize();
}

This one also calls the super...

//JComponent.java (1628 ~ 1639)
public void setPreferredSize(Dimension preferredSize) {
   super.setPreferredSize(preferredSize);
}

JComponent extends Container, let's take a look:

//Container.java (1751 ~ 1800)
public Dimension getPreferredSize() {
   return preferredSize();
}

@Deprecated
public Dimension preferredSize() {
   Dimension dim = prefSize;
   if (dim == null || !(isPreferredSizeSet() || isValid())) {
       synchronized (getTreeLock()) {
           prefSize = (layoutMgr != null) ?
               layoutMgr.preferredLayoutSize(this) :
               super.preferredSize();
           dim = prefSize;
       }
   }

   if (dim != null){
       return new Dimension(dim);
   }
   else{
       return dim;
   }
}

as I thought, it's returning a new instance. But let's see what happens with the setter, which is in Component.java (since Container extends Component but doesn't override the method):

//Component.java (2560 ~ 2585)
public void setPreferredSize(Dimension preferredSize) {
   Dimension old;
   if (prefSizeSet) {
       old = this.prefSize;
   }
   else {
       old = null;
   }

   this.prefSize = preferredSize;
   prefSizeSet = (preferredSize != null);
   firePropertyChange("preferredSize", old, preferredSize);
}

This is just referencing to the object I passed!, perfect! (dangerous indeed, but it suits my current needs!)

I conclude I'm setting the actual object instance that my components hold and use, so (at least as for this JRE release) there is no need to repeatedly call setPreferredSize with the same object again (but failing to do so would indeed be dangerous as this source code can change in different implementations), and even so setPreferredSize won't generate garbage if we call it with the same instance of Dimension. Calling getPreferredSize, on the other hand, clearly will allocate a new Dimension object to return the value.

The answer is: keep track of your preferred sizes somewhere to minimize the use of getPreferredSize, and to be polite to the API, call setPreferredSize, but using the same Dimension instance for avoiding garbage.

Dimension pref = null;

void getNewPrefOnlyWhenNeeded() {
    pref = component.getPreferredSize();
}

void calledEveryMillisecond() {
    //Here or on the Constructor, MouseListener or wherever
    if (pref == null || someCondition) getNewPrefOnlyWhenNeeded();

    // do stuff with pref...
    ...

    //setPreferredSize can actually be skipped if we call our layout manager after all is resized. It would be dirty tho...   
    component.setPreferredSize(pref);
}

After further analysis, Maximum and Minimum size methods share this mechanics, so everything solved.

Bigger
  • 1,807
  • 3
  • 18
  • 28
  • foot note 1: First of all, thank you all for your help. But I have to say I hate when stackoverflow becomes a "trauma support group", with answers like "Don't worry", "You don't need to do it", even when the OP explains to no avail he/she just *wants to* do it. It reminds me of the medical forum members who tell the OPs to go see a doctor. I know we are all trying to help but... that just doesn't help. No offense intended to anyone. – Bigger May 28 '13 at 03:16
  • foot note 2: Isn't it wonderful how the open-sourcedness of a project can make for it's poor documentation? :) – Bigger May 28 '13 at 03:17