diff --git a/docs/ReleaseNotes.html b/docs/ReleaseNotes.html index 1568d24..028c21c 100644 --- a/docs/ReleaseNotes.html +++ b/docs/ReleaseNotes.html @@ -5,8 +5,10 @@

What's New

@@ -26,6 +28,13 @@ 1709841 (M1) Compiles with Visual Studio 2005 + + 1701995 + (M2) Added option to exclude classes from ROT to + try and manage memory in heavy event callback programs. Feature is 100% + backwards compatible by default. + +   diff --git a/docs/UsingJacob.html b/docs/UsingJacob.html index e7e9fde..98ad477 100644 --- a/docs/UsingJacob.html +++ b/docs/UsingJacob.html @@ -49,15 +49,45 @@ This library supports several different : even though the JacobComLifetime.html document says this is a bad idea.

+

+ This value is cached at startup and cannot be changed on-the-fly via System.setProperty(); +

The default value is false

- Example: -Dcom.jacob.autogc=false + Example: -Dcom.jacob.autogc=false + +

<class_name>.PutInROT

+ Lets a program specify that instances of certain classes are to not be inserted + into the ROT. This experimental (1.13) feature provides a mechanism for freeing + VariantViaEvent objects that are created in Event threads. There is normally no + way to free those objects because the thread terminates outside of any normaly MTA/STA + Startup/Teardown code. Each event occures in a new thread and creates a new ROT entry + so they grow without bounds. +

+ This option may cause VM crashes in certain situations where windows memory is freed + outside of the thread it was created in but emperical evidence shows there are + situations where this great reduces the long running memory footprint of applications + that process a lot of events. This function is still experimental. + The functionality of this overlaps the experimental com.jacob.autogc introduced + in 1.9. + See the ROT.java test program for an example of the effects of this option. +

+ This value is checked every time and can be changed on-the-fly via System.setProperty(); +

+ + Example: System.setProperty("com.jacob.com.VariantViaVariant.PutInROT","false"); +
+ Example: -Dcom.jacob.com.VariantViaVariant.PutInROT=false

com.jacob.debug

- Determines if debug output is enabled to standard out. + Determines if debug output is enabled to standard out. +

+ This value is cached at startup and cannot be changed on-the-fly via System.setProperty(); +

The default value is false

- Example: -Dcom.jacob.debug=false + + Example: -Dcom.jacob.debug=false

-XCheck:jni

This turns on additional JVM checking for JNI issues. This is diff --git a/src/com/jacob/com/JacobObject.java b/src/com/jacob/com/JacobObject.java index 9332c78..c350775 100644 --- a/src/com/jacob/com/JacobObject.java +++ b/src/com/jacob/com/JacobObject.java @@ -52,13 +52,13 @@ public class JacobObject { * @see getBuildDate() */ private static String buildDate = ""; - + /** * Standard constructor that adds this JacobObject * to the memory management pool. */ public JacobObject() { - ROT.addObject(this); + ROT.addObject(this); } diff --git a/src/com/jacob/com/ROT.java b/src/com/jacob/com/ROT.java index 75c9e40..2551579 100644 --- a/src/com/jacob/com/ROT.java +++ b/src/com/jacob/com/ROT.java @@ -43,10 +43,22 @@ import java.util.WeakHashMap; public abstract class ROT { /** * Manual garbage collection was the only option pre 1.9 + * Can staticly cache the results because only one value + * and we don't let it change during a run */ protected static final boolean USE_AUTOMATIC_GARBAGE_COLLECTION = "true".equalsIgnoreCase( System.getProperty( "com.jacob.autogc" ) ); + /** + * Suffix added to class name to make up property name that determines if this + * object should be stored in the ROT. This 1.13 "feature" makes it possible + * to cause VariantViaEvent objects to not be added to the ROT in event callbacks. + *

+ * We don't have a static for the actual property because there is a different property + * for each class that may make use of this feature. + */ + protected static String PUT_IN_ROT_SUFFIX = ".PutInROT"; + /** * A hash table where each element is another * HashMap that represents a thread. @@ -170,21 +182,31 @@ public abstract class ROT { * @param o */ protected synchronized static void addObject( JacobObject o ) { - Map tab = getThreadObjects( false ); - if ( tab == null ) { - // this thread has not been initialized as a COM thread - // so make it part of MTA for backwards compatibility - ComThread.InitMTA( false ); - tab = getThreadObjects( true ); - } - if ( JacobObject.isDebugEnabled() ) { - JacobObject.debug( - "ROT: adding " + o + "->" + o.getClass().getName() + - " table size prior to addition:" + tab.size() ); - } - if ( tab != null ) { - tab.put( o, null ); - } + // check the system property to see if this class is put in the ROT + // the default value is "true" which simulates the old behavior + String shouldIncludeClassInROT = + System.getProperty(o.getClass().getName() + PUT_IN_ROT_SUFFIX,"true"); + if (shouldIncludeClassInROT.equalsIgnoreCase("false")){ + if (JacobObject.isDebugEnabled()){ + JacobObject.debug("JacobObject: New instance of "+o.getClass().getName()+" not added to ROT"); + } + } else { + Map tab = getThreadObjects( false ); + if ( tab == null ) { + // this thread has not been initialized as a COM thread + // so make it part of MTA for backwards compatibility + ComThread.InitMTA( false ); + tab = getThreadObjects( true ); + } + if ( JacobObject.isDebugEnabled() ) { + JacobObject.debug( + "ROT: adding " + o + "->" + o.getClass().getName() + + " table size prior to addition:" + tab.size() ); + } + if ( tab != null ) { + tab.put( o, null ); + } + } } /** diff --git a/src/com/jacob/com/Variant.java b/src/com/jacob/com/Variant.java index 25e5653..f11d97f 100644 --- a/src/com/jacob/com/Variant.java +++ b/src/com/jacob/com/Variant.java @@ -26,6 +26,8 @@ import java.util.Date; * between Java and COM. It provides a single class that can handle all data * types. *

+ * Just loading this class creates 3 variants that get added to the ROT + *

* PROPVARIANT introduces new types so eventually Variant will need to be * upgraded to support PropVariant types. * http://blogs.msdn.com/benkaras/archive/2006/09/13/749962.aspx @@ -59,7 +61,8 @@ public class Variant extends JacobObject { false); /* - * do the run time definition of DEFAULT and MISSING + * do the run time definition of DEFAULT and MISSING + * Have to use static block because of the way the initialization si done */ static { com.jacob.com.Variant vtMissing = new com.jacob.com.Variant(); diff --git a/unittest/com/jacob/com/JacobObjectTest.java b/unittest/com/jacob/com/JacobObjectTest.java index 9cdbe51..443fa7a 100644 --- a/unittest/com/jacob/com/JacobObjectTest.java +++ b/unittest/com/jacob/com/JacobObjectTest.java @@ -4,13 +4,26 @@ package com.jacob.com; * This will eventually be changed to a unit test. *

* May need to run with some command line options (including from inside Eclipse). - * Look in the docs area at the Jacob usage document for command line options. + * If so, then try these + *

+ *      -Djava.library.path=d:/jacob/release/x86 
+ *      -Dcom.jacob.autogc=false 
+ *      -Dcom.jacob.debug=false 
+ *      -Xcheck:jni
+ *  
*/ public class JacobObjectTest { public static void main(String args[]) throws Exception { + JacobObjectTest testJig = new JacobObjectTest(); + testJig.testBuildVersion(); + } + + public void testBuildVersion(){ System.out.println("build version is "+JacobObject.getBuildVersion()); System.out.println("build date is "+JacobObject.getBuildDate()); } + + } diff --git a/unittest/com/jacob/com/ROTTest.java b/unittest/com/jacob/com/ROTTest.java index 8ecb8e4..41694a2 100644 --- a/unittest/com/jacob/com/ROTTest.java +++ b/unittest/com/jacob/com/ROTTest.java @@ -6,33 +6,92 @@ import com.jacob.com.ROT; * This will eventually be changed to a unit test. *

* May need to run with some command line options (including from inside Eclipse). - * Look in the docs area at the Jacob usage document for command line options. + * If so, then try these + *

+ *      -Djava.library.path=d:/jacob/release/x86 
+ *      -Dcom.jacob.autogc=false 
+ *      -Dcom.jacob.debug=false 
+ *      -Xcheck:jni
+ *  
*/ public class ROTTest { public static void main(String args[]) throws Exception { - int sizeAfterBuild = 0; + ROTTest testJig = new ROTTest(); + testJig.testDontFillROTSystemProperty(); + testJig.testGCBehavior(); + } + + /** + * verify the SystemProperty (classname).PutInROT functions as expected. + * A value of false means instances of the class are not put in the ROT + * Any o ther value means they are + */ + public void testDontFillROTSystemProperty(){ + debug("testDontFillROTSystemProperty: started"); + // Make sure the class is loaded before running any of the tests + // class to load and any pre-defined Variants (FALSE and TRUE) to be created immediately + VariantViaEvent.class.getName(); + if (ROT.getThreadObjects(true).entrySet().size() < 1){ + debug("Failure: ROT should have objects in it as soon as Variant class loaded."); + } + + System.setProperty(VariantViaEvent.class.getName()+ROT.PUT_IN_ROT_SUFFIX,"false"); + int countPriorToTest = ROT.getThreadObjects(true).entrySet().size(); + new VariantViaEvent(); + int countAfterAddWithoutROT = ROT.getThreadObjects(true).entrySet().size(); + if (countAfterAddWithoutROT != countPriorToTest){ + debug("Failure: count prior: "+countPriorToTest+ + " and count after without ROT was: "+countAfterAddWithoutROT); + } + + System.setProperty(VariantViaEvent.class.getName()+ROT.PUT_IN_ROT_SUFFIX,"true"); + new VariantViaEvent(); + int countAfterAddWithROT = ROT.getThreadObjects(true).entrySet().size(); + if (countAfterAddWithROT != (countPriorToTest+1)){ + debug("Failure: count prior: "+countPriorToTest+ + " and count after with ROT was: "+countAfterAddWithROT); + } + debug("testDontFillROTSystemProperty: completed"); + } + + /** + * Needs documentation. This test looks broken + * + */ + public void testGCBehavior(){ + int sizeBeforeBuild = 0; + int sizeAfterBuild = 0; int sizeBeforeGC = 0; int sizeAfterGC = 0; + int loopSize = 10000; + int sizeExpectedAfterBuild = 0; + + debug("testGCBehavior: started"); debug("creating 10,000 object sets"); - for ( int i = 0 ; i <= 10000; i++){ + // cause classes to get loaded and any static instances to be created + SafeArray.class.getName(); + Variant.class.getName(); + sizeBeforeBuild = ROT.getThreadObjects(false).size(); + sizeExpectedAfterBuild = ((loopSize*3)+sizeBeforeBuild); + for ( int i = 0 ; i < loopSize; i++){ SafeArray a1 = new SafeArray(Variant.VariantVariant, 2); a1.setVariant(0, new Variant("foo")); a1.setVariant(1, new Variant("bar")); } sizeAfterBuild = ROT.getThreadObjects(false).size(); - if (sizeAfterBuild < 10000){ + if (sizeAfterBuild < sizeExpectedAfterBuild){ debug("Something got GC'd: "+sizeAfterBuild); - } else if (sizeAfterBuild > 10000){ - debug("More than expected: "+sizeAfterBuild); + } else if (sizeAfterBuild > sizeExpectedAfterBuild){ + debug("More: "+sizeAfterBuild+" than expected: "+sizeExpectedAfterBuild); } else { debug("They're all there"); } // add more to the VM debug("Flooding Memory to force GC"); - for ( int i = 0 ; i <= 10000; i++){ + for ( int i = 0 ; i <= loopSize*2; i++){ new String("this is just some text to see if we can force gc "+i); } // storage will hold weak references until the next JacobObject is created @@ -65,6 +124,7 @@ public class ROTTest { ROT.clearObjects(); // now force the gc to go collect them, running safeRelease again System.gc(); + debug("testGCBehavior: finished"); } private static void debug(String message){