SF 1701995 another attempt at memory managment <class_name>.PutInROT system property added.

This commit is contained in:
clay_shooter
2007-05-17 03:40:22 +00:00
parent 90eb8609a3
commit 117799dd86
7 changed files with 168 additions and 31 deletions

View File

@@ -5,8 +5,10 @@
<h3>What's New</h3>
<ul>
<li>
<b>Now compiles with with Visual Studio 2005 (M1)</b>
<b>Changed milestone release naming convetion from "pre..." to "M..."</b>
Now compiles with with Visual Studio 2005 (M1)
</li>
<li>
Changed milestone release naming convetion from "pre..." to "M..."
</li>
</ul>
@@ -26,6 +28,13 @@
<td width="13%" valign="top">1709841 </td>
<td width="87%" valign="top">(M1) Compiles with Visual Studio 2005</td>
</tr>
<tr>
<td width="13%" valign="top">1701995 </td>
<td width="87%" valign="top">(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.
</td>
</tr>
<tr>
<tr>
<td width="13%" valign="top">&nbsp;</td>

View File

@@ -49,15 +49,45 @@ This library supports several different :
even though the <a href="JacobComLifetime.html">JacobComLifetime.html</a> document
says this is a bad idea.
<p>
<p>
This value is cached at startup and cannot be changed on-the-fly via <code>System.setProperty();</code>
<p>
The default value is <strong>false</strong>
<p>
Example: -Dcom.jacob.autogc=false
Example: <code>-Dcom.jacob.autogc=false</code>
<h3>&ltclass_name&gt.PutInROT</h3>
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.
<p>
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. <em>This function is still experimental</em>.
The functionality of this overlaps the experimental <code>com.jacob.autogc</code> introduced
in 1.9.
See the ROT.java test program for an example of the effects of this option.
<p>
This value is checked every time and can be changed on-the-fly via <code>System.setProperty();</code>
<p>
Example: <code>System.setProperty("com.jacob.com.VariantViaVariant.PutInROT","false");</code>
<BR>
Example: <code>-Dcom.jacob.com.VariantViaVariant.PutInROT=false</code>
<h3>com.jacob.debug</h3>
Determines if debug output is enabled to standard out.
Determines if debug output is enabled to standard out.
<p>
This value is cached at startup and cannot be changed on-the-fly via <code>System.setProperty();</code>
<p>
The default value is <strong>false</strong>
<p>
Example: -Dcom.jacob.debug=false
Example: <code>-Dcom.jacob.debug=false</code>
<h3>-XCheck:jni</h3>
This turns on additional JVM checking for JNI issues. This is

View File

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

View File

@@ -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.
* <p>
* 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 );
}
}
}
/**

View File

@@ -26,6 +26,8 @@ import java.util.Date;
* between Java and COM. It provides a single class that can handle all data
* types.
* <p>
* Just loading this class creates 3 variants that get added to the ROT
* <p>
* 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();

View File

@@ -4,13 +4,26 @@ package com.jacob.com;
* This will eventually be changed to a unit test.
* <p>
* 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
* <pre>
* -Djava.library.path=d:/jacob/release/x86
* -Dcom.jacob.autogc=false
* -Dcom.jacob.debug=false
* -Xcheck:jni
* </pre>
*/
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());
}
}

View File

@@ -6,33 +6,92 @@ import com.jacob.com.ROT;
* This will eventually be changed to a unit test.
* <p>
* 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
* <pre>
* -Djava.library.path=d:/jacob/release/x86
* -Dcom.jacob.autogc=false
* -Dcom.jacob.debug=false
* -Xcheck:jni
* </pre>
*/
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){