View Javadoc
1   package org.apache.commons.jcs;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.Serializable;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import junit.framework.TestCase;
27  
28  import org.apache.commons.jcs.access.CacheAccess;
29  import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
30  import org.apache.commons.jcs.engine.stats.behavior.IStats;
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  
34  /**
35   * This is based on a test that was posted to the user's list:
36   * <p>
37   * http://www.opensubscriber.com/message/jcs-users@jakarta.apache.org/2435965.html
38   */
39  public class JCSThrashTest
40      extends TestCase
41  {
42      /** The logger. */
43      private static final Log LOG = LogFactory.getLog( JCSThrashTest.class.getName() );
44  
45      /**
46       * the cache instance
47       */
48      protected CacheAccess<String, Serializable> jcs;
49  
50      /**
51       * @param args
52       */
53      public static void main( String[] args )
54      {
55          junit.textui.TestRunner.run( JCSThrashTest.class );
56      }
57  
58      /**
59       * @param arg0
60       */
61      public JCSThrashTest( String arg0 )
62      {
63          super( arg0 );
64      }
65  
66      /**
67       * Sets up the test
68       * @throws Exception
69       */
70      @Override
71      protected void setUp()
72          throws Exception
73      {
74          super.setUp();
75          JCS.setConfigFilename( "/TestThrash.ccf" );
76          jcs = JCS.getInstance( "testcache" );
77      }
78  
79      /**
80       * @throws Exception
81       */
82      @Override
83      protected void tearDown()
84          throws Exception
85      {
86          super.tearDown();
87          jcs.clear();
88          jcs.dispose();
89      }
90  
91      /**
92       * Tests adding an entry.
93       * @throws Exception
94       */
95      public void testPut()
96          throws Exception
97      {
98          final String value = "value";
99          final String key = "key";
100 
101         // Make sure the element is not found
102         assertEquals( 0, getListSize() );
103 
104         assertNull( jcs.get( key ) );
105 
106         jcs.put( key, value );
107 
108         // Get the element
109         LOG.info( "jcs.getStats(): " + jcs.getStatistics() );
110         assertEquals( 1, getListSize() );
111         assertNotNull( jcs.get( key ) );
112         assertEquals( value, jcs.get( key ) );
113     }
114 
115     /**
116      * Test elements can be removed from the store
117      * @throws Exception
118      */
119     public void testRemove()
120         throws Exception
121     {
122         jcs.put( "key1", "value1" );
123         assertEquals( 1, getListSize() );
124 
125         jcs.remove( "key1" );
126         assertEquals( 0, getListSize() );
127 
128         jcs.put( "key2", "value2" );
129         jcs.put( "key3", "value3" );
130         assertEquals( 2, getListSize() );
131 
132         jcs.remove( "key2" );
133         assertEquals( 1, getListSize() );
134 
135         // Try to remove an object that is not there in the store
136         jcs.remove( "key4" );
137         assertEquals( 1, getListSize() );
138     }
139 
140     /**
141      * This does a bunch of work and then verifies that the memory has not grown by much. Most of
142      * the time the amount of memory used after the test is less.
143      * @throws Exception
144      */
145     public void testForMemoryLeaks()
146         throws Exception
147     {
148         long differenceMemoryCache = thrashCache();
149         LOG.info( "Memory Difference is: " + differenceMemoryCache );
150         assertTrue( differenceMemoryCache < 500000 );
151 
152         //LOG.info( "Memory Used is: " + measureMemoryUse() );
153     }
154 
155     /**
156      * @return time
157      * @throws Exception
158      */
159     protected long thrashCache()
160         throws Exception
161     {
162         long startingSize = measureMemoryUse();
163         LOG.info( "Memory Used is: " + startingSize );
164 
165         final String value = "value";
166         final String key = "key";
167 
168         // Add the entry
169         jcs.put( key, value );
170 
171         // Create 15 threads that read the keys;
172         final List<Executable> executables = new ArrayList<Executable>();
173         for ( int i = 0; i < 15; i++ )
174         {
175             final JCSThrashTest.Executable executable = new JCSThrashTest.Executable()
176             {
177                 @Override
178                 public void execute()
179                     throws Exception
180                 {
181                     for ( int j = 0; j < 500; j++ )
182                     {
183                         final String keyj = "key" + j;
184                         jcs.get( keyj );
185                     }
186                     jcs.get( "key" );
187                 }
188             };
189             executables.add( executable );
190         }
191 
192         // Create 15 threads that are insert 500 keys with large byte[] as
193         // values
194         for ( int i = 0; i < 15; i++ )
195         {
196             final JCSThrashTest.Executable executable = new JCSThrashTest.Executable()
197             {
198                 @Override
199                 public void execute()
200                     throws Exception
201                 {
202 
203                     // Add a bunch of entries
204                     for ( int j = 0; j < 500; j++ )
205                     {
206                         // Use a random length value
207                         final String keyj = "key" + j;
208                         byte[] valuej = new byte[10000];
209                         jcs.put( keyj, valuej );
210                     }
211                 }
212             };
213             executables.add( executable );
214         }
215 
216         runThreads( executables );
217         jcs.clear();
218 
219         long finishingSize = measureMemoryUse();
220         LOG.info( "Memory Used is: " + finishingSize );
221         return finishingSize - startingSize;
222     }
223 
224     /**
225      * Runs a set of threads, for a fixed amount of time.
226      * <p>
227      * @param executables
228      * @throws Exception
229      */
230     protected void runThreads( final List<Executable> executables )
231         throws Exception
232     {
233 
234         final long endTime = System.currentTimeMillis() + 10000;
235         final Throwable[] errors = new Throwable[1];
236 
237         // Spin up the threads
238         final Thread[] threads = new Thread[executables.size()];
239         for ( int i = 0; i < threads.length; i++ )
240         {
241             final JCSThrashTest.Executable executable = executables.get( i );
242             threads[i] = new Thread()
243             {
244                 @Override
245                 public void run()
246                 {
247                     try
248                     {
249                         // Run the thread until the given end time
250                         while ( System.currentTimeMillis() < endTime )
251                         {
252                             executable.execute();
253                         }
254                     }
255                     catch ( Throwable t )
256                     {
257                         // Hang on to any errors
258                         errors[0] = t;
259                     }
260                 }
261             };
262             threads[i].start();
263         }
264 
265         // Wait for the threads to finish
266         for ( int i = 0; i < threads.length; i++ )
267         {
268             threads[i].join();
269         }
270 
271         // Throw any error that happened
272         if ( errors[0] != null )
273         {
274             throw new Exception( "Test thread failed.", errors[0] );
275         }
276     }
277 
278     /**
279      * Measure memory used by the VM.
280      * <p>
281      * @return bytes
282      * @throws InterruptedException
283      */
284     protected long measureMemoryUse()
285         throws InterruptedException
286     {
287         System.gc();
288         Thread.sleep( 3000 );
289         System.gc();
290         return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
291     }
292 
293     /**
294      * A runnable, that can throw an exception.
295      */
296     protected interface Executable
297     {
298         /**
299          * Executes this object.
300          * @throws Exception
301          */
302         void execute()
303             throws Exception;
304     }
305 
306     /**
307      * @return size
308      */
309     private int getListSize()
310     {
311         final String listSize = "List Size";
312         final String lruMemoryCache = "LRU Memory Cache";
313         String result = "0";
314         List<IStats> istats = jcs.getStatistics().getAuxiliaryCacheStats();
315         for ( IStats istat : istats )
316         {
317             List<IStatElement<?>> statElements = istat.getStatElements();
318             if ( lruMemoryCache.equals( istat.getTypeName() ) )
319             {
320                 for ( IStatElement<?> statElement : statElements )
321                 {
322                     if ( listSize.equals( statElement.getName() ) )
323                     {
324                         result = statElement.getData().toString();
325                         break;
326                     }
327                 }
328             }
329         }
330         return Integer.parseInt( result );
331     }
332 }