javacommons/utils/javasrc/com/nokia/mj/impl/rt/support/MemoryManagerCtr.java
changeset 21 2a9601315dfc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javacommons/utils/javasrc/com/nokia/mj/impl/rt/support/MemoryManagerCtr.java	Mon May 03 12:27:20 2010 +0300
@@ -0,0 +1,230 @@
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package com.nokia.mj.impl.rt.support;
+
+import com.nokia.mj.impl.utils.Logger;
+
+/**
+ * MemoryManagerCtr is a class which can be used to try to keep number of
+ * allocated objects in defined limits. This is handy in such objects that
+ * consume very little Java heap, but consumes fair big amount of native
+ * resources. In some cases (TCK, benchmarks) objects are allocated in a tight
+ * loop which can cause out of memory situations.
+ * <p>
+ * The user must define two thresholds of object counts: the lower limit
+ * defines a threshold when a young GC (if supported by the VM) should be
+ * triggered. The upper limit defines a threshold when a full GC should be
+ * triggered. If VM does not support young GC, then full GC is triggered
+ * also when young GC threshold is exceeded. GC triggering happens
+ * upon every instantiation of a new object when the number of objects
+ * exceeds the defined limits.
+ * <p>
+ * A static instance of the memory manager is created per use case - which is
+ * normally one problematic class. There can be more than one instances of
+ * memory managers. When the user wants to increase instance count it should
+ * get a new memory token using {@link #setSize} method and keep the reference
+ * to memory token active as long as the problematic object is active. When a
+ * memory token is created it will increse the object count of the associated
+ * memory manager. If a threshold is exceeded, the memory manager will trigger
+ * either young GC or full GC and asks the JVM to run finalizers.
+ * <p>
+ * When the problematic object is GC'ed also the memory token will be GC'ed.
+ * Once the Memory token is finalized the token will decrease the amount of
+ * allocated objects.
+ * <p>
+ * <pre>
+ *
+ * import com.nokia.mj.impl.rt.support.MemoryManagerCtr;
+ *
+ * class GraphicsObject
+ * {
+ *     private static MemoryManagerCtr mMemoryManager = new
+                                 MemoryManagerCtr(20, 40);
+ *     private Object mMemoryToken = mMemoryManager.getMemoryToken();
+ *
+ *     public GraphicsObject()
+ *     {
+ *         // ...
+ *     }
+ * }
+ * </pre>
+ * <p>
+ * Note: There should be one static memory manager per problematic case/class.
+ * There can be more than one instances of memory managers.
+ */
+
+public class MemoryManagerCtr
+{
+
+    /** A count of created objects.*/
+    private int mInstanceCount = 0;
+
+    /** A Threshold when a young GC should start.*/
+    private int mYoungGcThreshold = 0;
+
+    /** A Threshold when a full GC should start.*/
+    private int mFullGcThreshold = 0;
+
+    /** Tells if collection is going on */
+    private boolean mCollectionOngoing = false;
+
+    /** Should we trace operations.*/
+    private boolean mTraceEnabled = false;
+
+
+    /**
+     * Creates a new counter based manager with given limits.
+     *
+     * @param youngGcThreshold number of objects when the young GC should
+     *        be started to be used.
+     * @param fullGcThreshold number of objects when the full GC should
+     *        be started to be used.
+     */
+    public MemoryManagerCtr(int youngGcThreshold, int fullGcThreshold)
+    {
+        mYoungGcThreshold = youngGcThreshold;
+        mFullGcThreshold = fullGcThreshold;
+    }
+
+    /**
+     * Creates a new counter based memory token and associates it to the
+     * memory manager. It will increase the object count of the memory manager
+     * by one. The user should store the token as long the problematic object
+     * is alive. Normally this is achived by defining the token as a member
+     * variable and ensure that it is always instantiated when the problematic
+     * object is created.
+     * <p>
+     * Once the token is finalized it will decrease the object count of the
+     * memory manager.
+     * @return A memory token.
+     */
+    public Object getMemoryToken()
+    {
+        return new MemoryToken();
+    }
+
+    /**
+     * Informs the manager about the addition of one token.
+     */
+    private void incCtr()
+    {
+        // Info whether to run young GC, full GC or no GC at all.
+        Boolean runYoungGc = null;
+        synchronized (this)
+        {
+            mInstanceCount++;
+
+            // Some other thread is doing the collection.
+            if (mCollectionOngoing)
+            {
+                return;
+            }
+
+            if (mInstanceCount >= mYoungGcThreshold)
+            {
+                // Mark that we will request GC.
+                mCollectionOngoing = true;
+
+                traceString("Threshold exceeded: " + mInstanceCount);
+                if (mInstanceCount < mFullGcThreshold)
+                {
+                    // Mark that we will request young GC.
+                    runYoungGc = new Boolean(true);
+                }
+                else
+                {
+                    // Mark that we will request full GC.
+                    runYoungGc = new Boolean(false);
+                }
+            }
+        }
+
+        // Do we need to run GC at all.
+        if (runYoungGc != null)
+        {
+            if (runYoungGc.booleanValue() == true)
+            {
+                // Run fast collection if supported.
+                traceString("Asking YoungGc");
+                if (!JvmInternal.runYoungGenerationGc())
+                {
+                    // Run full collection.
+                    traceString("Asking fullGc - youngGc not supported");
+                    System.gc();
+                }
+            }
+            else
+            {
+                // Run full collection.
+                traceString("Asking fullGc");
+                System.gc();
+            }
+
+            traceString("Asking finalization");
+            // Ask the JVM to run finalizers.
+            JvmInternal.runFinalization();
+
+            synchronized (this)
+            {
+                // Mark that GC request has been sent.
+                mCollectionOngoing = false;
+            }
+        }
+    }
+
+    /**
+     * Informs the manager about finalization of one token.
+     */
+    private synchronized void decCtr()
+    {
+        mInstanceCount--;
+    }
+
+    private void traceString(String s)
+    {
+        if (mTraceEnabled)
+        {
+            Logger.WLOG(Logger.EUtils, "MemoryManagerCtr: " + s);
+        }
+    }
+
+    /**
+     * A class for increasing an decreasing the count of unfinalized
+     * objects.
+     */
+    private class MemoryToken
+    {
+        /** Enables finalization */
+        private Finalizer finalizer = new Finalizer()
+        {
+            public void finalizeImpl()
+            {
+                decCtr();
+            }
+        };
+
+        /**
+         * Creates a new counter based token.
+         */
+        private MemoryToken()
+        {
+            incCtr();
+        }
+    }
+
+}