Wednesday, November 28, 2012

Using cfproperty to reduce java class files

I haven't seen much blogged about this, so I thought it would be worth bringing up.

As part of our jump from CF8 to CF10 I've been experimenting with cfproperty and the concept of implicit getters/setters. Due to the way ColdFusion compiles these implicit functions it seems to me you can significantly reduce the number of java class files that ColdFusion creates. Obviously the effect is very noticeable in OO applications with lots of beans and hundreds of getter/setter functions.

To prove this let's take the following CFC with the old school explicit declarations for all of our getters & setters. I'll call it "explicit.cfc"

<cfcomponent output="false">
       
    <cffunction name="init" output="false" returntype="any" hint="our formal constructor block.">
            <cfset variables.instance.id    =1>
            <cfset variables.instance.name="">
            <cfset variables.instance.title="">
    </cffunction>

    <cffunction name="getID" output="false" returntype="string">
        <cfreturn variables.instance.id />
    </cffunction>
    <cffunction name="setID" output="false">
        <cfargument name="ID" type="string" required="true">
        <cfset variables.instance.id=arguments.id>
    </cffunction>

    <cffunction name="getName" output="false" returntype="string">
        <cfreturn variables.instance.name />
    </cffunction>
    <cffunction name="setName" output="false">
        <cfargument name="Name" type="string" required="true">
        <cfset variables.instance.Name=arguments.Name>
    </cffunction>

    <cffunction name="getTitle" output="false" returntype="string">
        <cfreturn variables.instance.title />
    </cffunction>
    <cffunction name="setTitle" output="false">
        <cfargument name="Title" type="string" required="true">
        <cfset variables.instance.Title=arguments.Title>
    </cffunction>

</cfcomponent>


It is a bean-like object with accessors for three variables: id, name, and title.

Now let's write a file that uses our object.

<cfprocessingdirective pageEncoding="utf-8" suppressWhitespace="true">
<cfscript>
    ex = new explicit();
    ex.init();
    ex.setName('Tester');  
    writeOutput("<p>  ID:#ex.getID()#  Name:#ex.getName()#  </p>" );
</cfscript>
</cfprocessingdirective>


Finally, go into the CF administrator and check " " in the Caching menu.

Now call up that page we created in your browser, then check in the .class files directory for CF and see what the server compiled. It will look like this:


 As we can see, CF has created a class file for our CFC itself, and every function inside of it. It also created a class file for our calling page.

Now let's take a CFC with the new style implicit cfproperty declarations for our getters & setters. Let's call it "implicit.cfc"

<cfcomponent output="false" accessors="true">

    <cfproperty name="id" />
    <cfproperty name="name" />
    <cfproperty name="title" />
      
        <cffunction name="init" output="false" returntype="any" hint="our formal constructor block.">
            <cfset setID(1)>
            <cfset setName('Tester')>           
        </cffunction>

</cfcomponent>

Go back and edit our file. We now create an implicit.cfc object.

<cfprocessingdirective pageEncoding="utf-8" suppressWhitespace="true">
<cfscript>
    ex = new implicit();
    ex.init();
    ex.setName('Tester');  
    writeOutput("<p>  ID:#ex.getID()#  Name:#ex.getName()#  </p>" );
</cfscript>
</cfprocessingdirective>


And what gets created now for java class files?


Just three files: one for the CFC itself, one for the Init() function inside of it, and one for the calling page.

Obviously the effect of this in a large application is that you're going to save yourself from creating a lot of 3kb class files for every getter/setter.

You may be asking if all if this matters, and it most cases it probably is not of great concern. In my experience it only really matters if you are seeing java.lang.OutOfMemoryError: PermGen space errors on your server. This is the space in the java heap where your class definitions are stored and it is possible to fill it up if you're running a lot of big applications on the server. Fewer classes should result in a reduced PermGen usage.

I've seen the PermGen error a number of times on a 32 bit machine with CF8. We got around it in the end by increasing the size of our perm-gen space in the JVM to 256mb, but even then we kept bumping up on the limit in some cases. On a 64 bit machine this should be even less of a problem though due to the ability to run with a much larger JVM.

The fact that each ColdFusion function results in a Java class file has always bugged me, and I'm glad this is no longer always the case. In theory fewer classes should also result in a performance gain, although, to be fair, I suspect in most cases the practical effect will be marginal.


1 comment: