Soft References In ColdFusion

Posted May 6, 2008 at 8:39 AM by Ben Nadel

Tags: ColdFusion

At cf.Objective(), Mark Mandel held a presentation on the caching mechanisms and algorithms he uses in his Transfer ORM project. In it, he talked about his use of Soft References. A soft reference, as opposed to a hard reference, is a reference to an object that does not get flagged as a "use" of that object. When the JVM cleans up all the garbage in its memory, it checks to see if objects are in use by seeing if there are any hard references to it. If there are no hard references, the garbarge collection may clear that object out of memory. From what I gathered, Mark uses soft references to keep cached items around until the JVM needs to free up memory.

To do this, he uses the java.lang.ref.SoftReference class. I had never heard of this, and I love playing with the underlying Java, so I figured I would take a look and see how this object works. The SoftReference class is really simple. It basically takes an object in its constructor and then provides a Get() method to retreive that object (to which it has a soft reference). If the object has been cleared from memory, the Get() method will return NULL, and in ColdFusion, will delete whatever variable stored the return object.

To test this, I set up a really simple application that stores a single soft reference. I then would refresh the page repeatidly to see if the object in question still existed. To try and speed up the process, I put in some random Garbage Collection calls. I have been told that you can't really force garbage collection, but I threw it in anyway (I might not even be using it properly - I don't know that much about Java). Anyway, here is my very simple Application.cfc:

  • <cfcomponent
  • output="false"
  • hint="I set up the application and define event handlers.">
  •  
  • <!--- Define application settings. --->
  • <cfset THIS.Name = "SoftReferenceDemo" />
  • <cfset THIS.ApplicationTimeout = CreateTimeSpan( 0, 0, 5, 0 ) />
  •  
  • <!--- Define page settings. --->
  • <cfsetting
  • showdebugoutput="false"
  • requesttimeout="10"
  • />
  •  
  •  
  • <cffunction
  • name="OnApplicationStart"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="I run when the application starts up. If I return false, the application will not initialize.">
  •  
  • <!--- Clear the application scope. --->
  • <cfset StructClear( APPLICATION ) />
  •  
  • <!--- Keep a log of messages. --->
  • <cfset APPLICATION.Log = [] />
  •  
  • <!--- Return out. --->
  • <cfreturn true />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="OnRequestStart"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="I run before the requested template gets processed. If I return false, the page will not load.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Page"
  • type="string"
  • required="true"
  • hint="I am the template requested by the user."
  • />
  •  
  • <!--- Check to see if the re-init method was called. --->
  • <cfif StructKeyExists( URL, "reset" )>
  •  
  • <!---
  • The user has requested that the application
  • be reset - execute this function manually.
  • --->
  • <cfset THIS.OnApplicationStart() />
  •  
  • </cfif>
  •  
  • <!--- Return out. --->
  • <cfreturn true />
  • </cffunction>
  •  
  • </cfcomponent>

All we are really doing here is initializing the APPLICATION scope and creating an array of log messages.

Then we have our index.cfm page which creates the soft reference if it doesn't exist and then, for each subsequent page request, sees if our soft reference target still exists:

  • <!---
  • Check to see if our test refernce object has been
  • created. If not, we will need to create and cached it.
  • --->
  • <cfif NOT StructKeyExists( APPLICATION, "Reference" )>
  •  
  • <!--- Let's create a simple struct. --->
  • <cfset objGirl = {
  • FirstName = "Winona",
  • LastName = "Ryder"
  • } />
  •  
  •  
  • <!---
  • Now, let's create a soft reference to that object
  • and cache that in the APPLICATION scope. No other
  • references to it are going to exist, so there will
  • be no hard references that force it to remain in
  • memory.
  • --->
  • <cfset APPLICATION.Reference = CreateObject(
  • "java",
  • "java.lang.ref.SoftReference"
  • ).Init(
  • objGirl
  • )
  • />
  •  
  •  
  • <!--- Log the creation. --->
  • <cfset ArrayAppend(
  • APPLICATION.Log,
  • "#TimeFormat( Now(), 'hh:mm:ss' )# - Reference Created."
  • ) />
  •  
  • </cfif>
  •  
  •  
  •  
  • <!---
  • At this point, we have our soft reference in the APPLICATION
  • scope, but there is not saying whether or not the object
  • will still be there. We need to get the value and see if it
  • returns null. I am scoping the return value so that we can
  • see if the key exists after our Get().
  • --->
  • <cfset REQUEST.Girl = APPLICATION.Reference.Get() />
  •  
  • <!---
  • Check to see if Girl exists. If it does, then we got the
  • object. If it does not, then the object in question has been
  • cleaned up.
  • --->
  • <cfif StructKeyExists( REQUEST, "Girl" )>
  •  
  • <!--- It exists. Log message. --->
  • <cfset ArrayAppend(
  • APPLICATION.Log,
  • (
  • "#TimeFormat( Now(), 'hh:mm:ss' )# - Exists - " &
  • REQUEST.GirL.FirstName
  • )) />
  •  
  • <cfelse>
  •  
  • <!--- It no longer exists. Log message. --->
  • <cfset ArrayAppend(
  • APPLICATION.Log,
  • "#TimeFormat( Now(), 'hh:mm:ss' )# - Does NOT Exist."
  • ) />
  •  
  • </cfif>
  •  
  •  
  •  
  • <!--- Check to see if we want to run the garbage collection. --->
  • <cfif (RandRange( 1, 5 ) EQ 3)>
  •  
  • <!--- Get a reference to the runtime. --->
  • <cfset objRuntime = CreateObject(
  • "java",
  • "java.lang.Runtime"
  • ).GetRuntime()
  • />
  •  
  • <!--- Call garbage collection. --->
  • <cfset objRuntime.GC() />
  •  
  • <!--- Log message. --->
  • <cfset ArrayAppend(
  • APPLICATION.Log,
  • "#TimeFormat( Now(), 'hh:mm:ss' )# - Garbage Collection."
  • ) />
  •  
  • </cfif>
  •  
  •  
  •  
  • <cfoutput>
  •  
  • <h3>
  • Application Log
  • </h3>
  •  
  • <!--- Output application log entries. --->
  • <cfloop
  • index="strMessage"
  • array="#APPLICATION.Log#">
  •  
  • #strMessage#<br />
  •  
  • </cfloop>
  •  
  • </cfoutput>

As you can see, for each page request, I am calling the Get() method on our cached SoftReference object. If that return value voids the "Girl" key in the REQUEST scope, we know that the soft reference target has been cleared by memory. I ran this a bunch of times and here it the log output that I got:

Application Log
07:57:11 - Reference Created.
08:03:28 - Exists - Winona
08:03:30 - Exists - Winona
08:04:36 - Exists - Winona
08:09:33 - Exists - Winona
08:09:33 - Garbage Collection.
08:09:34 - Exists - Winona
08:09:34 - Garbage Collection.
08:09:35 - Exists - Winona
08:09:36 - Exists - Winona
08:09:44 - Exists - Winona
08:09:45 - Exists - Winona
08:11:27 - Exists - Winona
08:11:27 - Garbage Collection.
08:12:06 - Exists - Winona
08:12:08 - Exists - Winona
08:12:08 - Garbage Collection.
08:13:21 - Exists - Winona
08:15:28 - Exists - Winona
08:27:14 - Exists - Winona
08:27:21 - Garbage Collection.
08:27:22 - Exists - Winona
08:37:35 - Exists - Winona

As you can see, over the course of about 30 minutes, I refreshed the page ever few minutes. As easy as this was to set up, apparently, it's not very easy to get the soft reference target to be cleared out of memory. I guess the JVM just didn't see the need - I have almost nothing running on this DEV server at this time of the day. There are no memory management issues that need to taken into account.

Not much to take away from this post as I couldn't demonstrate the very idea that I set out to test. At the very least, the idea of soft references is very interesting. Thanks to Mark Mandel for bringing this to my attention.




Reader Comments

May 6, 2008 at 9:32 AM // reply »
9 Comments

A couple things - I have been able to force garbage collection using the jConsole tool. I have been planning on writing a blog post about it but haven't gotten to it yet.

The other things you could try to do to get that object to be cleaned up would be

1) lower your -Xmx argument in jvm.config so you are running with less memory
2) Run another CF page that repeatedly stuffs something large into a persistant scope, to start using up available memory.

It would be interesting to see it work.


May 6, 2008 at 9:41 AM // reply »
11,246 Comments

@Ryan,

I don't want to start messing with the system settings too much; I don't know very much about that stuff and I don't want to break anything. I have heard of people breaking the CF service and stuff by messing with the configuration. Eeek :)


May 6, 2008 at 9:46 AM // reply »
11,246 Comments

I just realized I totally forgot to Spell Check this blog post. So appologies to anyone who finds blatant spelling errors.


sal
May 6, 2008 at 12:58 PM // reply »
1 Comments

mmh, pretty interesting post man!

cheers


May 6, 2008 at 4:08 PM // reply »
5 Comments

Soft References are indeed great Ben. ColdBox's caching engine is based on soft references too. It also supports caching eternal references that won't get cleaned by the JVM.

As a side note, I don't recommend running garbage collections manually, that is just asking for trouble. What you can do to limit their stay in memory is to be able to track the time they where placed in the cache and have a reaping method that checks how long they have lived. This way you can create timeouts on the objects. Although they are not always guaranteeded, since the JVM can collect them whenever it sees fit.

Also, remember that if you have any references to these objects in such a way that the JVM sees them as hard references, they won't be collected.


May 6, 2008 at 5:42 PM // reply »
11,246 Comments

@Luis,

Agreed. I never use garbage collection. I only did it here because I wanted to see that reference disappear and it just wouldn't!! My first attempt didn't have the GC at all. I only put it in after like 15 minutes when the reference did not disappear.


May 7, 2008 at 3:19 PM // reply »
8 Comments

Ben, I too found this fascinating. I want to use it at work. So I ran some tests on caching CFC instances. I used lots of them and watched the memory monitor in CF8. After GCs, I was able to find missing ones. Check out my experiment.

http://www.cfchris.com/cfchris/index.cfm/2008/5/7/javalangrefSoftReference-in-CFML


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 24, 2013 at 5:39 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Adam Oops! My mistake! I hadn't gotten that far in my testing - I'm still baby stepping my way through the process. ... read »
May 24, 2013 at 5:13 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
Hi Jason, Thanks for checking up on that, but I still stand firm on my position. :) There are actually two listLast()'s in use, and you're right that the one using a space as a delimiter is fine. ... read »
May 24, 2013 at 4:45 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Ben I have been lurking your site for quite some time, and haven't stepped up to comment until today. Thanks for all the great info - keep it up! @Adam I believe you are mistaken... as the commen ... read »
May 24, 2013 at 11:21 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, Ha ha, let's us never speak of justifying "##" notation again :P ... read »
May 24, 2013 at 11:18 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Ah, so it was indeed how I vaguely remembered it to be: A direct assignment value = users.id[ i ] causes value to retain the sticky datatype of the query column. Although unnecessary in ... read »
May 24, 2013 at 9:11 AM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Brandon, Hi, No, I haven't been able to do that. I have just kept it as it is. ... read »
May 23, 2013 at 9:52 PM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Muhmmadibn Did you figure out a solution to launching PDFs? I am running into the same issues myself. There is no way to close the PDF or go back once you launch it. Thanks in advance! ... read »
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools