get / setPreferredSize,而不是每次都分配Dimension对象

时间:2013-05-27 14:26:27

标签: java performance swing memory allocation

有没有一种很好的方法可以使用set / getPreferredSize,set / getMinimumSize和set / getMaximumSize(或绕过它们的方法),而不是偶尔分配Dimension对象? (除了扩展JComponents)

我的应用程序在鼠标拖动时大量使用此方法来调整大小/移动,我担心所有的Dimension对象。

我知道像set / getBounds这样的方法可以处理传递给它们的已经分配的对象,我似乎无法找到其他方法的等价物。

4 个答案:

答案 0 :(得分:3)

  

是否有使用set/getPreferredSize, set/getMinimumSize, and set/getMaximumSize

的好方法

是的:不要使用它们!请改用相应的LayoutManager。 LayoutManager负责计算这些值,因此最好的方法是将其委托给LayoutManagerBorderLayoutGridBagLayout已经为布局提供了一个非常好的起点。您可能不时使用FlowLayout和/或GridLayout。如果您愿意使用第三方库,MigLayout也是有效的LayoutManager

请考虑阅读answer关于Should I avoid the use of set[Preferred|Maximum|Minimum]Size methods in Java Swing?

的内容

无论如何,Dimension对象的内存占用非常小,并且在实例化后很快就会被垃圾收集,所以你不必担心它们。

如果您认为自己遇到了性能问题,请使用分析器来帮助您找到它们。

答案 1 :(得分:2)

你必须每秒至少分配数千个这些对象,甚至开始担心。 HotSpot的GC方案专门针对短期对象的低成本进行了优化。它们在Eden空间中被分配,如果在Eden填满时没有幸存者,则只需更新指针即可释放所有幸存者。所以最好的建议是先关注其他性能问题;这个应该有一个非常低的优先级。

答案 2 :(得分:1)

“我很担心所有的Dimension对象。”

不要担心几千个快速死亡的微物体。 GC可以解决这个问题。如果遇到任何性能问题,则无法通过避免一些Dimension创建来修复它们。您应该知道,像setPreferredSize()这样的调用内部的成本要比作为参数传递的那么微小的Dimension高得多(查看JRE源代码,它执行更多内部)。

答案 3 :(得分:0)

我对JComponent的get / setPreferredSize机制和分配行为感兴趣

注意:我对以下内容感兴趣:默认布局管理器如何工作。让我们想一下,我将preferredSize值用于与默认布局管理器无关的X目的。

官方文档未能给出解决方案,所以让我们看一下JRE源代码:

这个叫超级......

//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();
}

这个也叫超级......

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

JComponent扩展Container,让我们来看看:

//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;
   }
}

正如我想的那样,它正在返回一个新实例。但是让我们看一下使用set.java中的setter会发生什么(因为Container扩展了Component但是没有覆盖该方法):

//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);
}

这只是引用我传递的对象!,完美! (确实很危险,但它符合我目前的需求!)

我总结说我正在设置我的组件保存和使用的实际对象实例,所以(至少对于这个JRE版本),不需要再次使用相同的对象重复调用setPreferredSize(但是没有这样做会确实很危险,因为这个源代码可以在不同的实现中改变),即使如此,如果我们使用相同的Dimension实例调用它, setPreferredSize也不会生成垃圾。另一方面,调用getPreferredSize显然会分配一个新的Dimension对象来返回值。

答案是:在某处跟踪您的首选大小以最小化getPreferredSize的使用,并且对API有礼貌,调用setPreferredSize,但使用相同的Dimension实例来避免垃圾。< / p>

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);
}

经过进一步分析,最大和最小尺寸方法共享这种机制,所以一切都解决了。