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 @@
+
+ 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
+
+
+ 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
+ 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
+ * 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){