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>
|
<h3>What's New</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<b>Now compiles with with Visual Studio 2005 (M1)</b>
|
Now compiles with with Visual Studio 2005 (M1)
|
||||||
<b>Changed milestone release naming convetion from "pre..." to "M..."</b>
|
</li>
|
||||||
|
<li>
|
||||||
|
Changed milestone release naming convetion from "pre..." to "M..."
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@@ -26,6 +28,13 @@
|
|||||||
<td width="13%" valign="top">1709841 </td>
|
<td width="13%" valign="top">1709841 </td>
|
||||||
<td width="87%" valign="top">(M1) Compiles with Visual Studio 2005</td>
|
<td width="87%" valign="top">(M1) Compiles with Visual Studio 2005</td>
|
||||||
</tr>
|
</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>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="13%" valign="top"> </td>
|
<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
|
even though the <a href="JacobComLifetime.html">JacobComLifetime.html</a> document
|
||||||
says this is a bad idea.
|
says this is a bad idea.
|
||||||
<p>
|
<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>
|
The default value is <strong>false</strong>
|
||||||
<p>
|
<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>
|
<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>
|
The default value is <strong>false</strong>
|
||||||
<p>
|
<p>
|
||||||
Example: -Dcom.jacob.debug=false
|
|
||||||
|
Example: <code>-Dcom.jacob.debug=false</code>
|
||||||
|
|
||||||
<h3>-XCheck:jni</h3>
|
<h3>-XCheck:jni</h3>
|
||||||
This turns on additional JVM checking for JNI issues. This is
|
This turns on additional JVM checking for JNI issues. This is
|
||||||
|
|||||||
@@ -43,10 +43,22 @@ import java.util.WeakHashMap;
|
|||||||
public abstract class ROT {
|
public abstract class ROT {
|
||||||
/**
|
/**
|
||||||
* Manual garbage collection was the only option pre 1.9
|
* 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 =
|
protected static final boolean USE_AUTOMATIC_GARBAGE_COLLECTION =
|
||||||
"true".equalsIgnoreCase( System.getProperty( "com.jacob.autogc" ) );
|
"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
|
* A hash table where each element is another
|
||||||
* HashMap that represents a thread.
|
* HashMap that represents a thread.
|
||||||
@@ -170,6 +182,15 @@ public abstract class ROT {
|
|||||||
* @param o
|
* @param o
|
||||||
*/
|
*/
|
||||||
protected synchronized static void addObject( JacobObject o ) {
|
protected synchronized static void addObject( JacobObject o ) {
|
||||||
|
// 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 );
|
Map tab = getThreadObjects( false );
|
||||||
if ( tab == null ) {
|
if ( tab == null ) {
|
||||||
// this thread has not been initialized as a COM thread
|
// this thread has not been initialized as a COM thread
|
||||||
@@ -186,6 +207,7 @@ public abstract class ROT {
|
|||||||
tab.put( o, null );
|
tab.put( o, null );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ROT can't be a subclass of JacobObject because of the way ROT pools are managed
|
* ROT can't be a subclass of JacobObject because of the way ROT pools are managed
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import java.util.Date;
|
|||||||
* between Java and COM. It provides a single class that can handle all data
|
* between Java and COM. It provides a single class that can handle all data
|
||||||
* types.
|
* types.
|
||||||
* <p>
|
* <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
|
* PROPVARIANT introduces new types so eventually Variant will need to be
|
||||||
* upgraded to support PropVariant types.
|
* upgraded to support PropVariant types.
|
||||||
* http://blogs.msdn.com/benkaras/archive/2006/09/13/749962.aspx
|
* http://blogs.msdn.com/benkaras/archive/2006/09/13/749962.aspx
|
||||||
@@ -60,6 +62,7 @@ public class Variant extends JacobObject {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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 {
|
static {
|
||||||
com.jacob.com.Variant vtMissing = new com.jacob.com.Variant();
|
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.
|
* This will eventually be changed to a unit test.
|
||||||
* <p>
|
* <p>
|
||||||
* May need to run with some command line options (including from inside Eclipse).
|
* 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 class JacobObjectTest {
|
||||||
|
|
||||||
public static void main(String args[]) throws Exception
|
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 version is "+JacobObject.getBuildVersion());
|
||||||
System.out.println("build date is "+JacobObject.getBuildDate());
|
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.
|
* This will eventually be changed to a unit test.
|
||||||
* <p>
|
* <p>
|
||||||
* May need to run with some command line options (including from inside Eclipse).
|
* 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 class ROTTest {
|
||||||
|
|
||||||
public static void main(String args[]) throws Exception
|
public static void main(String args[]) throws Exception
|
||||||
{
|
{
|
||||||
|
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 sizeAfterBuild = 0;
|
||||||
int sizeBeforeGC = 0;
|
int sizeBeforeGC = 0;
|
||||||
int sizeAfterGC = 0;
|
int sizeAfterGC = 0;
|
||||||
|
int loopSize = 10000;
|
||||||
|
int sizeExpectedAfterBuild = 0;
|
||||||
|
|
||||||
|
|
||||||
|
debug("testGCBehavior: started");
|
||||||
debug("creating 10,000 object sets");
|
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);
|
SafeArray a1 = new SafeArray(Variant.VariantVariant, 2);
|
||||||
a1.setVariant(0, new Variant("foo"));
|
a1.setVariant(0, new Variant("foo"));
|
||||||
a1.setVariant(1, new Variant("bar"));
|
a1.setVariant(1, new Variant("bar"));
|
||||||
}
|
}
|
||||||
sizeAfterBuild = ROT.getThreadObjects(false).size();
|
sizeAfterBuild = ROT.getThreadObjects(false).size();
|
||||||
if (sizeAfterBuild < 10000){
|
if (sizeAfterBuild < sizeExpectedAfterBuild){
|
||||||
debug("Something got GC'd: "+sizeAfterBuild);
|
debug("Something got GC'd: "+sizeAfterBuild);
|
||||||
} else if (sizeAfterBuild > 10000){
|
} else if (sizeAfterBuild > sizeExpectedAfterBuild){
|
||||||
debug("More than expected: "+sizeAfterBuild);
|
debug("More: "+sizeAfterBuild+" than expected: "+sizeExpectedAfterBuild);
|
||||||
} else {
|
} else {
|
||||||
debug("They're all there");
|
debug("They're all there");
|
||||||
}
|
}
|
||||||
// add more to the VM
|
// add more to the VM
|
||||||
debug("Flooding Memory to force GC");
|
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);
|
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
|
// storage will hold weak references until the next JacobObject is created
|
||||||
@@ -65,6 +124,7 @@ public class ROTTest {
|
|||||||
ROT.clearObjects();
|
ROT.clearObjects();
|
||||||
// now force the gc to go collect them, running safeRelease again
|
// now force the gc to go collect them, running safeRelease again
|
||||||
System.gc();
|
System.gc();
|
||||||
|
debug("testGCBehavior: finished");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void debug(String message){
|
private static void debug(String message){
|
||||||
|
|||||||
Reference in New Issue
Block a user