diff options
author | Sven Gothel <[email protected]> | 2010-10-14 18:39:42 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2010-10-14 18:39:42 +0200 |
commit | 6ced17f0325d5719e992b246ffd156e5b39694b4 (patch) | |
tree | c8618ebe347466e26c5ac8feb818b6844761121e /src | |
parent | 29999cf3b7616c8ab58f4483c672e30076fbb3e4 (diff) |
Fix: Memory consumption
Observing memory consumption showed:
1 - 'traceLock' debug stack traces (GLContextLock)
2 - massive Iterator usage
(1) is fixed, ie only enabled in DEBUG mode, like we have done in RecursiveLock before
(2) Using an Iterator on ArrayLists with a low element count < 100,
as it is usual in our use cases, is observed not to be faster
than accessing the elements via an index (-> TestIteratorIndexCORE.java ).
On the contrary, the index implementation was a bit faster.
Further more, these Iterators were massively used on the fly during animation,
hence their memory managment even impacts fluent processing/animation.
Recoded all animation related (display, surfaceUpdated, ..) loops using an index.
Diffstat (limited to 'src')
6 files changed, 241 insertions, 64 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/impl/GLContextLock.java b/src/jogl/classes/com/jogamp/opengl/impl/GLContextLock.java index 565ec967e..ea78f5209 100644 --- a/src/jogl/classes/com/jogamp/opengl/impl/GLContextLock.java +++ b/src/jogl/classes/com/jogamp/opengl/impl/GLContextLock.java @@ -50,11 +50,13 @@ import javax.media.opengl.*; be raised. */ public class GLContextLock { + protected static final boolean DEBUG = GLContextImpl.DEBUG; + static class SyncData { boolean failFastMode = true; Thread owner = null; int waiters = 0; - Exception lockedStack = null; + Exception lockedStack = null; // only enabled if DEBUG } private SyncData sdata = new SyncData(); // synchronized (flow/mem) mutable access @@ -66,12 +68,16 @@ public class GLContextLock { Thread current = Thread.currentThread(); if (sdata.owner == null) { sdata.owner = current; - sdata.lockedStack = new Exception("Previously made current (1) by "+sdata.owner+", lock: "+this); + if(DEBUG) { + sdata.lockedStack = new Exception("Error: Previously made current (1) by "+sdata.owner+", lock: "+this); + } } else if (sdata.owner != current) { while (sdata.owner != null) { if (sdata.failFastMode) { - sdata.lockedStack.printStackTrace(); - throw new GLException("Attempt to make context current on thread " + current + + if(null!=sdata.lockedStack) { + sdata.lockedStack.printStackTrace(); + } + throw new GLException("Error: Attempt to make context current on thread " + current + " which is already current on thread " + sdata.owner); } else { try { @@ -85,7 +91,9 @@ public class GLContextLock { } } sdata.owner = current; - sdata.lockedStack = new Exception("Previously made current (2) by "+sdata.owner+", lock: "+this); + if(DEBUG) { + sdata.lockedStack = new Exception("Previously made current (2) by "+sdata.owner+", lock: "+this); + } } else { throw new GLException("Attempt to make the same context current twice on thread " + current); } @@ -139,6 +147,7 @@ public class GLContextLock { } } + /** holding the owners stack trace when lock is acquired and DEBUG is true */ public final Exception getLockedStack() { synchronized(sdata) { return sdata.lockedStack; diff --git a/src/jogl/classes/com/jogamp/opengl/impl/GLDrawableHelper.java b/src/jogl/classes/com/jogamp/opengl/impl/GLDrawableHelper.java index 53fc34181..4ad0dd4c3 100644 --- a/src/jogl/classes/com/jogamp/opengl/impl/GLDrawableHelper.java +++ b/src/jogl/classes/com/jogamp/opengl/impl/GLDrawableHelper.java @@ -50,15 +50,29 @@ public class GLDrawableHelper { protected static final boolean DEBUG = GLDrawableImpl.DEBUG; private static final boolean VERBOSE = Debug.verbose(); private Object listenersLock = new Object(); - private List listeners = new ArrayList(); - private volatile boolean listenersIter = false; // avoid java.util.ConcurrentModificationException - private Set listenersToBeInit = new HashSet(); - private boolean autoSwapBufferMode = true; + private List listeners; + private volatile boolean listenersIter; // avoid java.util.ConcurrentModificationException + private Set listenersToBeInit; + private boolean autoSwapBufferMode; private Object glRunnablesLock = new Object(); - private ArrayList glRunnables = new ArrayList(); // one shot GL tasks - private GLAnimatorControl animatorCtrl = null; // default + private ArrayList glRunnables; + private GLAnimatorControl animatorCtrl; public GLDrawableHelper() { + reset(); + } + + public void reset() { + synchronized(listenersLock) { + listeners = new ArrayList(); + listenersIter = false; + listenersToBeInit = new HashSet(); + } + autoSwapBufferMode = true; + synchronized(glRunnablesLock) { + glRunnables = new ArrayList(); + } + animatorCtrl = null; } public String toString() { @@ -67,8 +81,8 @@ public class GLDrawableHelper { synchronized(listenersLock) { sb.append("GLEventListeners num "+listeners.size()+" ["); listenersIter = true; - for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { - Object l = iter.next(); + for (int i=0; i < listeners.size(); i++) { + Object l = listeners.get(i); sb.append(l); sb.append("[init "); sb.append( !listenersToBeInit.contains(l) ); @@ -120,8 +134,8 @@ public class GLDrawableHelper { public void dispose(GLAutoDrawable drawable) { synchronized(listenersLock) { listenersIter = true; - for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { - GLEventListener listener = (GLEventListener) iter.next() ; + for (int i=0; i < listeners.size(); i++) { + GLEventListener listener = (GLEventListener) listeners.get(i) ; listener.dispose(drawable); listenersToBeInit.add(listener); } @@ -143,8 +157,8 @@ public class GLDrawableHelper { public void init(GLAutoDrawable drawable) { synchronized(listenersLock) { listenersIter = true; - for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { - GLEventListener listener = (GLEventListener) iter.next() ; + for (int i=0; i < listeners.size(); i++) { + GLEventListener listener = (GLEventListener) listeners.get(i) ; if ( ! init( listener, drawable, false ) ) { throw new GLException("GLEventListener "+listener+" already initialized: "+drawable); } @@ -156,8 +170,8 @@ public class GLDrawableHelper { public void display(GLAutoDrawable drawable) { synchronized(listenersLock) { listenersIter = true; - for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { - GLEventListener listener = (GLEventListener) iter.next() ; + for (int i=0; i < listeners.size(); i++) { + GLEventListener listener = (GLEventListener) listeners.get(i) ; // GLEventListener may need to be init, // in case this one is added after the realization of the GLAutoDrawable init( listener, drawable, true ) ; @@ -179,9 +193,8 @@ public class GLDrawableHelper { public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { synchronized(listenersLock) { listenersIter = true; - int i=0; - for (Iterator iter = listeners.iterator(); iter.hasNext(); i++) { - reshape((GLEventListener) iter.next(), drawable, x, y, width, height, 0==i); + for (int i=0; i < listeners.size(); i++) { + reshape((GLEventListener) listeners.get(i), drawable, x, y, width, height, 0==i); } listenersIter = false; } @@ -198,8 +211,8 @@ public class GLDrawableHelper { } } if(null!=_glRunnables) { - for (Iterator iter = _glRunnables.iterator(); iter.hasNext(); ) { - ((GLRunnable) iter.next()).run(drawable); + for (int i=0; i < _glRunnables.size(); i++) { + ((GLRunnable) _glRunnables.get(i)).run(drawable); } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java b/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java index e3aff61c6..9dd58bb57 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java +++ b/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java @@ -55,29 +55,36 @@ class AWTAnimatorImpl extends AnimatorImpl { public void display(AnimatorBase animator, boolean ignoreExceptions, boolean printExceptions) { - Iterator iter = animator.drawableIterator(); - while (animator.isAnimating() && !animator.getShouldStop() && !animator.getShouldPause() && iter.hasNext()) { - GLAutoDrawable drawable = (GLAutoDrawable) iter.next(); - if (drawable instanceof JComponent) { - // Lightweight components need a more efficient drawing - // scheme than simply forcing repainting of each one in - // turn since drawing one can force another one to be - // drawn in turn - lightweights.add(drawable); - } else { - try { - drawable.display(); - } catch (RuntimeException e) { - if (ignoreExceptions) { - if (printExceptions) { - e.printStackTrace(); + List drawables = animator.acquireDrawables(); + try { + for (int i=0; + animator.isAnimating() && !animator.getShouldStop() && !animator.getShouldPause() && i<drawables.size(); + i++) { + GLAutoDrawable drawable = (GLAutoDrawable) drawables.get(i); + if (drawable instanceof JComponent) { + // Lightweight components need a more efficient drawing + // scheme than simply forcing repainting of each one in + // turn since drawing one can force another one to be + // drawn in turn + lightweights.add(drawable); + } else { + try { + drawable.display(); + } catch (RuntimeException e) { + if (ignoreExceptions) { + if (printExceptions) { + e.printStackTrace(); + } + } else { + throw(e); } - } else { - throw(e); } } } + } finally { + animator.releaseDrawables(); } + if (lightweights.size() > 0) { try { SwingUtilities.invokeAndWait(drawWithRepaintManagerRunnable); diff --git a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java index a54f6be57..24eee1875 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java +++ b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java @@ -28,9 +28,11 @@ package com.jogamp.opengl.util; +import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.opengl.impl.Debug; import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import javax.media.opengl.GLAnimatorControl; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLProfile; @@ -43,7 +45,8 @@ public abstract class AnimatorBase implements GLAnimatorControl { private static int animatorCount = 0; - protected volatile ArrayList/*<GLAutoDrawable>*/ drawables = new ArrayList(); + protected ArrayList/*<GLAutoDrawable>*/ drawables = new ArrayList(); + protected RecursiveLock drawablesLock = new RecursiveLock(); protected AnimatorImpl impl; protected String baseName; protected Thread thread; @@ -75,18 +78,24 @@ public abstract class AnimatorBase implements GLAnimatorControl { protected abstract String getBaseName(String prefix); public synchronized void add(GLAutoDrawable drawable) { - ArrayList newList = (ArrayList) drawables.clone(); - newList.add(drawable); - drawables = newList; - drawable.setAnimator(this); + drawablesLock.lock(); + try { + drawables.add(drawable); + drawable.setAnimator(this); + } finally { + drawablesLock.unlock(); + } notifyAll(); } public synchronized void remove(GLAutoDrawable drawable) { - ArrayList newList = (ArrayList) drawables.clone(); - newList.remove(drawable); - drawables = newList; - drawable.setAnimator(null); + drawablesLock.lock(); + try { + drawables.remove(drawable); + drawable.setAnimator(null); + } finally { + drawablesLock.unlock(); + } notifyAll(); } @@ -101,8 +110,13 @@ public abstract class AnimatorBase implements GLAnimatorControl { totalFrames++; } - public Iterator drawableIterator() { - return drawables.iterator(); + public List acquireDrawables() { + drawablesLock.lock(); + return drawables; + } + + public void releaseDrawables() { + drawablesLock.unlock(); } public long getCurrentTime() { diff --git a/src/jogl/classes/com/jogamp/opengl/util/AnimatorImpl.java b/src/jogl/classes/com/jogamp/opengl/util/AnimatorImpl.java index e4bf8d711..8f2715e0a 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/AnimatorImpl.java +++ b/src/jogl/classes/com/jogamp/opengl/util/AnimatorImpl.java @@ -44,20 +44,26 @@ class AnimatorImpl { public void display(AnimatorBase animator, boolean ignoreExceptions, boolean printExceptions) { - Iterator iter = animator.drawableIterator(); - while (animator.isAnimating() && !animator.getShouldStop() && !animator.getShouldPause() && iter.hasNext()) { - GLAutoDrawable drawable = (GLAutoDrawable) iter.next(); - try { - drawable.display(); - } catch (RuntimeException e) { - if (ignoreExceptions) { - if (printExceptions) { - e.printStackTrace(); + List drawables = animator.acquireDrawables(); + try { + for (int i=0; + animator.isAnimating() && !animator.getShouldStop() && !animator.getShouldPause() && i<drawables.size(); + i++) { + GLAutoDrawable drawable = (GLAutoDrawable) drawables.get(i); + try { + drawable.display(); + } catch (RuntimeException e) { + if (ignoreExceptions) { + if (printExceptions) { + e.printStackTrace(); + } + } else { + throw(e); } - } else { - throw(e); } } + } finally { + animator.releaseDrawables(); } } diff --git a/src/junit/com/jogamp/test/junit/core/TestIteratorIndexCORE.java b/src/junit/com/jogamp/test/junit/core/TestIteratorIndexCORE.java new file mode 100644 index 000000000..0ed102e40 --- /dev/null +++ b/src/junit/com/jogamp/test/junit/core/TestIteratorIndexCORE.java @@ -0,0 +1,128 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.test.junit.core; + +import com.jogamp.test.junit.util.UITestCase; + +import java.util.*; +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.AfterClass; +import org.junit.Test; + +public class TestIteratorIndexCORE extends UITestCase { + + static int elems = 10; + static int loop = 9999999; + + public void populate(List l, int len) { + while(len>0) { + l.add(new Integer(len--)); + } + } + + @Test + public void test01ArrayListIterator() { + int sum=0; + ArrayList l = new ArrayList(); + populate(l, elems); + + for(int j=loop; j>0; j--) { + for(Iterator iter = l.iterator(); iter.hasNext(); ) { + Integer i = (Integer)iter.next(); + sum+=i.intValue(); + } + } + System.err.println("test01-arraylist-iterator sum: "+sum); + } + + @Test + public void test0ArrayListIndex() { + int sum=0; + ArrayList l = new ArrayList(); + populate(l, elems); + + for(int j=loop; j>0; j--) { + for(int k = 0; k < l.size(); k++) { + Integer i = (Integer)l.get(k); + sum+=i.intValue(); + } + } + System.err.println("test01-arraylist-index sum: "+sum); + } + + @Test + public void test01LinkedListListIterator() { + int sum=0; + LinkedList l = new LinkedList(); + populate(l, elems); + + for(int j=loop; j>0; j--) { + for(Iterator iter = l.iterator(); iter.hasNext(); ) { + Integer i = (Integer)iter.next(); + sum+=i.intValue(); + } + } + System.err.println("test01-linkedlist-iterator sum: "+sum); + } + + @Test + public void test01LinkedListListIndex() { + int sum=0; + LinkedList l = new LinkedList(); + populate(l, elems); + + for(int j=loop; j>0; j--) { + for(int k = 0; k < l.size(); k++) { + Integer i = (Integer)l.get(k); + sum+=i.intValue(); + } + } + System.err.println("test01-linkedlist-index sum: "+sum); + } + + public static void main(String args[]) throws IOException { + String tstname = TestIteratorIndexCORE.class.getName(); + org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main(new String[] { + tstname, + "filtertrace=true", + "haltOnError=false", + "haltOnFailure=false", + "showoutput=true", + "outputtoformatters=true", + "logfailedtests=true", + "logtestlistenerevents=true", + "formatter=org.apache.tools.ant.taskdefs.optional.junit.PlainJUnitResultFormatter", + "formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,TEST-"+tstname+".xml" } ); + } + +} |