Memory leak hunting - Rhino script woes leading to stale metadata not being garbage collected

Brian Koehmstedt bkoehmstedt at ucmerced.edu
Thu Sep 18 00:00:36 EDT 2014


I was able to duplicate this issue in test with RedHat/OpenJDK by 
utilizing the following trick:

- Wrote a quick and dirty script to every minute update the 
<ds:Reference URI="..."> in a metadata file copied from the Incommon file.
(May not be necessary to randomize URI.  Changing the last modified time 
stamp may be sufficient.  Not sure.)
This was so that the test Shib IdP will recognize a new metadata file.
- Run a web server that serves up this metadata file
- Configure the FileBackedHTTPMetadataProvider in relying-party.xml to 
download this changed file every minute.
- Create a dummy attribute (or just use an existing one) in LDAP
- Edit attribute-filter.xml and add 5 to 10 
<afp:AttributeFilterPolicy><afp:PolicyRequirementRule/><afp:AttributeRule/></afp:AttributeFilterPolicy> 
sections that reference the dummy attribute and for each one, provide a 
different matching value.  (Example below.)
- Then test by setting the dummy attribute value to, say, 1 and clearing 
all cookies, and logging into a test SP.  (Also, of course, release the 
attribute to the SP that you have in afp:AttributeRule.)
- Allow the FileBackedHTTPMetadataProvider to download a new version of 
the metadata (it should do this every minute.)
- Grab the PID of your IDP JVM
- Do jmap -histo:live <PID> | grep EntitiesDescriptorImpl and note the 
count.
- Then repeat the above 4 steps, except increment your dummy value to be 
something different now so that a new <afp:AttributeFilterPolicy> is 
triggered (matched) on your next login.

As you repeat those steps, it accumulates cached 
ShibbolethFilteringContexts and EntitiesDescriptorImpl objects in the heap.

Once I was able to duplicate the memory leak in test like this, it 
became easy to test with a different JVM.

So I tested with Oracle JDK 1.7.0_67 Linux x64 instead of the OpenJDK 
that ships with RedHat 6.5.

The jmap -histo:live shows that the EntitiesDescriptorImpl count does 
*not* grow with this JVM (and no stale ShibbolethFilteringContexts).

Now I'm ready to blame OpenJDK.  :)

I'll still need to verify by deploying a new Oracle JVM to production 
and confirming the memory leak is gone, but based on the above testing, 
it looks likely that switching to Oracle JDK and avoiding the JVM that 
ships with RedHat will solve the issue.

Too bad because it would be nice to use the package management system on 
RedHat for software updates like the JVM.  That was one of the reasons 
why we migrated to RedHat in the first place.

A couple side notes:

- I found a few things on the web that suggests using
scriptContext.setOptimizationLevel(-1) will prevent Rhino from using the 
ClassCache.  (Perhaps the Oracle JDK has this nonoptimization level by 
default while OpenJDK simply has it set to optimize by default?)
   - One potential fix to try for OpenJDK deployments is add 
scriptContext.setOptimizationLevel(-1) to the Shibboleth code to see if 
it avoids the ClassCaching.  And if so, I wonder if it's worth 
considering adding to the code.  (Is this nonoptimization level the 
default in Oracle JDK?)
- I saw postings on the web that indicate Rhino has been removed from 
JDK 8 in favor of a "Nashorn."  I wonder what the implications of this 
are for anybody who is migrating shibboleth to Java 8 and who has 
existing Javascript scripts in their Shibboleth configs?

Example attribute-filter.xml config using a dummy attribute with 
incrementing values:

     <!-- run the filtering script for givenName if MatchingAttribute==1 -->
     <afp:AttributeFilterPolicy>
         <afp:PolicyRequirementRule xsi:type="basic:AttributeValueString"
                                    attributeID="MatchingAttribute"
                                    value="1"/>
         <afp:AttributeRule attributeID="givenName">
             <afp:DenyValueRule xsi:type="basic:Script">
               <basic:Script>
                 <![CDATA[
                   <SOME JAVASCRIPT HERE, DOESN'T MATTER WHAT IT IS>
                 ]]>
               </basic:Script>
             </afp:DenyValueRule>
         </afp:AttributeRule>
     </afp:AttributeFilterPolicy>

     <!-- run the filtering script for givenName if MatchingAttribute==2 -->
     <afp:AttributeFilterPolicy>
         <afp:PolicyRequirementRule xsi:type="basic:AttributeValueString"
                                    attributeID="MatchingAttribute"
                                    value="2"/>
         <afp:AttributeRule attributeID="givenName">
             <afp:DenyValueRule xsi:type="basic:Script">
               <basic:Script>
                 <![CDATA[
                   <SOME JAVASCRIPT HERE, DOESN'T MATTER WHAT IT IS>
                 ]]>
               </basic:Script>
             </afp:DenyValueRule>
         </afp:AttributeRule>
     </afp:AttributeFilterPolicy>



More information about the users mailing list