SF 1701995 another attempt at memory managment <class_name>.PutInROT system property added.
This commit is contained in:
@@ -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"> </td>
|
||||
|
||||
@@ -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><class_name>.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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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){
|
||||
|
||||
Reference in New Issue
Block a user