View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.beanutils2.memoryleaktests;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertNotNull;
21  import static org.junit.jupiter.api.Assertions.assertNotSame;
22  import static org.junit.jupiter.api.Assertions.assertNull;
23  import static org.junit.jupiter.api.Assertions.assertSame;
24  import static org.junit.jupiter.api.Assumptions.assumeFalse;
25  
26  import java.lang.ref.SoftReference;
27  import java.lang.ref.WeakReference;
28  import java.net.MalformedURLException;
29  import java.net.URL;
30  import java.net.URLClassLoader;
31  import java.util.Locale;
32  
33  import org.apache.commons.beanutils2.BeanUtilsBean;
34  import org.apache.commons.beanutils2.ConvertUtils;
35  import org.apache.commons.beanutils2.MappedPropertyDescriptor;
36  import org.apache.commons.beanutils2.MethodUtils;
37  import org.apache.commons.beanutils2.PropertyUtils;
38  import org.apache.commons.beanutils2.WrapDynaBean;
39  import org.apache.commons.beanutils2.WrapDynaClass;
40  import org.apache.commons.beanutils2.converters.IntegerConverter;
41  import org.apache.commons.beanutils2.locale.LocaleBeanUtilsBean;
42  import org.apache.commons.beanutils2.locale.LocaleConvertUtils;
43  import org.apache.commons.beanutils2.locale.converters.IntegerLocaleConverter;
44  import org.junit.jupiter.api.Test;
45  
46  /**
47   * Tests BeanUtils memory leaks.
48   *
49   * See https://issues.apache.org/jira/browse/BEANUTILS-291
50   */
51  public class MemoryLeakTest {
52  
53      /**
54       * Creates a new class loader instance.
55       */
56      private static URLClassLoader newClassLoader() throws MalformedURLException {
57  
58          final String dataFilePath = MemoryLeakTest.class.getResource("pojotests").getFile();
59          // System.out.println("dataFilePath: " + dataFilePath);
60          final String location = "file://"
61                  + dataFilePath.substring(0, dataFilePath.length() - "org.apache.commons.beanutils2.memoryleaktests.pojotests".length());
62          // System.out.println("location: " + location);
63  
64          final StringBuilder newString = new StringBuilder();
65          for (int i = 0; i < location.length(); i++) {
66              if (location.charAt(i) == '\\') {
67                  newString.append("/");
68              } else {
69                  newString.append(location.charAt(i));
70              }
71          }
72          final String classLocation = newString.toString();
73          // System.out.println("classlocation: " + classLocation);
74  
75          final URLClassLoader theLoader = URLClassLoader.newInstance(new URL[] { new URL(classLocation) }, null);
76          return theLoader;
77      }
78  
79      /**
80       * Clears all the BeanUtils Caches manually.
81       *
82       * This is probably overkill, but since we're dealing with static caches it seems sensible to ensure that all test cases start with a clean sheet.
83       */
84      private void clearAllBeanUtilsCaches() {
85  
86          // Clear BeanUtilsBean's PropertyUtilsBean descriptor caches
87          BeanUtilsBean.getInstance().getPropertyUtils().clearDescriptors();
88  
89          // Clear LocaleBeanUtilsBean's PropertyUtilsBean descriptor caches
90          LocaleBeanUtilsBean.getLocaleBeanUtilsInstance().getPropertyUtils().clearDescriptors();
91  
92          // Clear MethodUtils's method cache
93          MethodUtils.clearCache();
94  
95          // Clear WrapDynaClass cache
96          WrapDynaClass.clear();
97  
98          // replace the existing BeanUtilsBean instance for the current class loader with a new, clean instance
99          BeanUtilsBean.setInstance(new BeanUtilsBean());
100 
101         // replace the existing LocaleBeanUtilsBean instance for the current class loader with a new, clean instance
102         LocaleBeanUtilsBean.setInstance(new LocaleBeanUtilsBean());
103     }
104 
105     /**
106      * Tries to force the garbage collector to run by filling up memory and calling System.gc().
107      */
108     private void forceGarbageCollection() throws Exception {
109         // Fill up memory
110         final SoftReference<Object> ref = new SoftReference<>(new Object());
111         int count = 0;
112         while (ref.get() != null && count++ < 5) {
113             java.util.ArrayList<String> list = new java.util.ArrayList<>();
114             try {
115                 long i = 0;
116                 while (ref.get() != null) {
117                     list.add(
118                             "A Big String A Big String A Big String A Big String A Big String A Big String A Big String A Big String A Big String A Big String "
119                                     + i++);
120                 }
121                 System.out.println("Count(1) " + count + " : " + getMemoryStats());
122             } catch (final OutOfMemoryError ignored) {
123                 // Cannot do anything here
124             }
125             // Trying to debug Continuum test fail: try calling GC before releasing the memory
126             System.gc();
127             list.clear();
128             list = null;
129             System.gc();
130             System.out.println("After GC2: " + getMemoryStats() + " Count " + count);
131             Thread.sleep(1000);
132         }
133 
134         final boolean isNotNull = ref.get() != null;
135         System.out.println("Count " + count + " " + isNotNull); // debug for Continuum failure
136         final String message = "Your JVM is not releasing SoftReference, try running the test with less memory (-Xmx)";
137         assumeFalse(isNotNull, message);
138     }
139 
140     /**
141      * Gets the total, free, used memory stats.
142      *
143      * @return the total, free, used memory stats
144      */
145     private String getMemoryStats() {
146         final java.text.DecimalFormat fmt = new java.text.DecimalFormat("#,##0");
147         final Runtime runtime = Runtime.getRuntime();
148         final long free = runtime.freeMemory() / 1024;
149         final long total = runtime.totalMemory() / 1024;
150         final long used = total - free;
151         return "MEMORY - Total: " + fmt.format(total) + "k " + "Used: " + fmt.format(used) + "k " + "Free: " + fmt.format(free) + "k";
152     }
153 
154     /**
155      * Produces a profiler report about where the leaks are.
156      *
157      * This requires JBoss's profiler be installed, see: https://labs.jboss.com/jbossprofiler/
158      *
159      * @param className The name of the class to profile
160      */
161     private void profilerLeakReport(final String test, final String className) {
162         /*
163          * If you want a report about where the leaks are... uncomment this, add jboss-profiler.jvmti.jar and jboss-commons.jar (for org.jboss.loggin). You will
164          * then have a report for where the references are. System.out.println(" ----------------" + test + " START ----------------");
165          * org.jboss.profiler.jvmti.JVMTIInterface jvmti = new org.jboss.profiler.jvmti.JVMTIInterface();
166          * System.out.println(jvmti.exploreClassReferences(className, 8, true, true, true, false, false)); System.out.println(" ----------------" + test +
167          * " END ------------------");
168          */
169     }
170 
171     /**
172      * Tests that ConvertUtilsBean's converters doesn't cause a memory leak.
173      */
174     @Test
175     public void testConvertUtilsBean_converters_memoryLeak() throws Exception {
176 
177         // Clear All BeanUtils caches before the test
178         clearAllBeanUtilsCaches();
179 
180         final String className = "org.apache.commons.beanutils2.memoryleaktests.pojotests.CustomInteger";
181 
182         // The classLoader will go away only when these following variables are released
183         ClassLoader loader = newClassLoader();
184         Class<?> beanClass = loader.loadClass(className);
185         Object bean = beanClass.newInstance();
186 
187         final WeakReference<ClassLoader> someRef = new WeakReference<>(loader);
188 
189         // Sanity checks only
190         assertNotNull(loader, "ClassLoader is null");
191         assertNotNull(beanClass, "BeanClass is null");
192         assertNotSame(getClass().getClassLoader(), beanClass.getClassLoader(), "ClassLoaders should be different..");
193         assertSame(beanClass.getClassLoader(), loader, "BeanClass ClassLoader incorrect");
194 
195         // if you comment the following two lines, the test will work, and the ClassLoader will be released.
196         // That proves that nothing is wrong with the test, and ConvertUtilsBean is holding a reference
197         ConvertUtils.register(new IntegerConverter(), (Class<Integer>) beanClass);
198         assertEquals("12345", ConvertUtils.convert(bean, String.class));
199 
200         // this should make the reference go away.
201         loader = null;
202         beanClass = null;
203         bean = null;
204 
205         forceGarbageCollection(); /* Try to force the garbage collector to run by filling up memory */
206 
207         if (someRef.get() != null) {
208             profilerLeakReport("ConvertUtilsBean converters", className);
209         }
210 
211         // if everything is fine, this will be null
212         assertNull(someRef.get(), "ConvertUtilsBean is holding a reference to the classLoader");
213 
214         // Clear All BeanUtils caches after the test
215         clearAllBeanUtilsCaches();
216     }
217 
218     /**
219      * Tests that LocaleConvertUtilsBean's converters doesn't cause a memory leak.
220      */
221     @Test
222     public void testLocaleConvertUtilsBean_converters_memoryLeak() throws Exception {
223 
224         // Clear All BeanUtils caches before the test
225         clearAllBeanUtilsCaches();
226 
227         final String className = "org.apache.commons.beanutils2.memoryleaktests.pojotests.CustomInteger";
228 
229         // The classLoader will go away only when these following variables are released
230         ClassLoader loader = newClassLoader();
231         Class<?> beanClass = loader.loadClass(className);
232         Object bean = beanClass.newInstance();
233 
234         final WeakReference<ClassLoader> someRef = new WeakReference<>(loader);
235 
236         // Sanity checks only
237         assertNotNull(loader, "ClassLoader is null");
238         assertNotNull(beanClass, "BeanClass is null");
239         assertNotSame(getClass().getClassLoader(), beanClass.getClassLoader(), "ClassLoaders should be different..");
240         assertSame(beanClass.getClassLoader(), loader, "BeanClass ClassLoader incorrect");
241 
242         // if you comment the following two lines, the test will work, and the ClassLoader will be released.
243         // That proves that nothing is wrong with the test, and LocaleConvertUtilsBean is holding a reference
244         LocaleConvertUtils.register(IntegerLocaleConverter.builder().setLocale(Locale.US).setLocalizedPattern(false).get(), (Class<Integer>) beanClass,
245                 Locale.US);
246         assertEquals(new Integer(12345), LocaleConvertUtils.convert(bean.toString(), Integer.class, Locale.US, "#,###"));
247 
248         // this should make the reference go away.
249         loader = null;
250         beanClass = null;
251         bean = null;
252 
253         forceGarbageCollection(); /* Try to force the garbage collector to run by filling up memory */
254 
255         if (someRef.get() != null) {
256             profilerLeakReport("LocaleConvertUtilsBean converters", className);
257         }
258 
259         // if everything is fine, this will be null
260         assertNull(someRef.get(), "LocaleConvertUtilsBean is holding a reference to the classLoader");
261 
262         // Clear All BeanUtils caches after the test
263         clearAllBeanUtilsCaches();
264     }
265 
266     /**
267      * Tests that MappedPropertyDescriptor can re-create the Method reference after it has been garbage collected.
268      */
269     @Test
270     public void testMappedPropertyDescriptor_MappedMethodReference1() throws Exception {
271 
272         // Clear All BeanUtils caches before the test
273         clearAllBeanUtilsCaches();
274 
275         final String className = "org.apache.commons.beanutils2.memoryleaktests.pojotests.SomeMappedPojo";
276         final ClassLoader loader = newClassLoader();
277         final Class<?> beanClass = loader.loadClass(className);
278         final Object bean = beanClass.newInstance();
279 
280         // Sanity checks only
281         assertNotNull(loader, "ClassLoader is null");
282         assertNotNull(beanClass, "BeanClass is null");
283         assertNotNull(bean, "Bean is null");
284         assertNotSame(getClass().getClassLoader(), beanClass.getClassLoader(), "ClassLoaders should be different..");
285         assertSame(beanClass.getClassLoader(), loader, "BeanClass ClassLoader incorrect");
286 
287         final MappedPropertyDescriptor descriptor = new MappedPropertyDescriptor("mappedProperty", beanClass);
288         assertNotNull(descriptor.getMappedReadMethod(), "1-Read Method null");
289         assertNotNull(descriptor.getMappedWriteMethod(), "1-Write Method null");
290         assertEquals("getMappedProperty", descriptor.getMappedReadMethod().getName(), "1-Read Method name");
291         assertEquals("setMappedProperty", descriptor.getMappedWriteMethod().getName(), "1-Read Write name");
292 
293         forceGarbageCollection(); /* Try to force the garbage collector to run by filling up memory */
294 
295         // The aim of this test is to check the functinality in MappedPropertyDescriptor which
296         // re-creates the Method references after they have been garbage collected. However theres no
297         // way of knowing the method references were garbage collected and that code was run, except by
298         // un-commeting the System.out statement in MappedPropertyDescriptor's MappedMethodReference's
299         // get() method.
300 
301         assertNotNull(descriptor.getMappedReadMethod(), "1-Read Method null");
302         assertNotNull(descriptor.getMappedWriteMethod(), "1-Write Method null");
303         assertEquals("getMappedProperty", descriptor.getMappedReadMethod().getName(), "1-Read Method name");
304         assertEquals("setMappedProperty", descriptor.getMappedWriteMethod().getName(), "1-Read Write name");
305 
306         // Clear All BeanUtils caches after the test
307         clearAllBeanUtilsCaches();
308     }
309 
310     /**
311      * Tests that MappedPropertyDescriptor can re-create the Method reference after it has been garbage collected.
312      */
313     @Test
314     public void testMappedPropertyDescriptor_MappedMethodReference2() throws Exception {
315 
316         // Clear All BeanUtils caches before the test
317         clearAllBeanUtilsCaches();
318 
319         final String className = "org.apache.commons.beanutils2.memoryleaktests.pojotests.SomeMappedPojo";
320         ClassLoader loader = newClassLoader();
321         Class<?> beanClass = loader.loadClass(className);
322         Object bean = beanClass.newInstance();
323 
324         // Sanity checks only
325         assertNotNull(loader, "ClassLoader is null");
326         assertNotNull(beanClass, "BeanClass is null");
327         assertNotNull(bean, "Bean is null");
328         assertNotSame(getClass().getClassLoader(), beanClass.getClassLoader(), "ClassLoaders should be different..");
329         assertSame(beanClass.getClassLoader(), loader, "BeanClass ClassLoader incorrect");
330 
331         final MappedPropertyDescriptor descriptor = new MappedPropertyDescriptor("mappedProperty", beanClass);
332         assertNotNull(descriptor.getMappedReadMethod(), "1-Read Method null");
333         assertNotNull(descriptor.getMappedWriteMethod(), "1-Write Method null");
334         assertEquals("getMappedProperty", descriptor.getMappedReadMethod().getName(), "1-Read Method name");
335         assertEquals("setMappedProperty", descriptor.getMappedWriteMethod().getName(), "1-Read Write name");
336 
337         // this should make the reference go away.
338         loader = null;
339         beanClass = null;
340         bean = null;
341 
342         forceGarbageCollection(); /* Try to force the garbage collector to run by filling up memory */
343 
344         // The aim of this test is to check the functinality in MappedPropertyDescriptor which
345         // re-creates the Method references after they have been garbage collected. However theres no
346         // way of knowing the method references were garbage collected and that code was run, except by
347         // un-commeting the System.out statement in MappedPropertyDescriptor's MappedMethodReference's
348         // get() method.
349 
350         assertNotNull(descriptor.getMappedReadMethod(), "1-Read Method null");
351         assertNotNull(descriptor.getMappedWriteMethod(), "1-Write Method null");
352         assertEquals("getMappedProperty", descriptor.getMappedReadMethod().getName(), "1-Read Method name");
353         assertEquals("setMappedProperty", descriptor.getMappedWriteMethod().getName(), "1-Read Write name");
354 
355         // Clear All BeanUtils caches after the test
356         clearAllBeanUtilsCaches();
357     }
358 
359     /**
360      * Tests that MethodUtils's cache doesn't cause a memory leak.
361      */
362     @Test
363     public void testMethodUtils_cache_memoryLeak() throws Exception {
364 
365         // Clear All BeanUtils caches before the test
366         clearAllBeanUtilsCaches();
367 
368         final String className = "org.apache.commons.beanutils2.memoryleaktests.pojotests.SomePojo";
369 
370         // The classLoader will go away only when these following variables are released
371         ClassLoader loader = newClassLoader();
372         Class<?> beanClass = loader.loadClass(className);
373         Object bean = beanClass.newInstance();
374 
375         final WeakReference<ClassLoader> someRef = new WeakReference<>(loader);
376 
377         // Sanity checks only
378         assertNotNull(loader, "ClassLoader is null");
379         assertNotNull(beanClass, "BeanClass is null");
380         assertNotSame(getClass().getClassLoader(), beanClass.getClassLoader(), "ClassLoaders should be different..");
381         assertSame(beanClass.getClassLoader(), loader, "BeanClass ClassLoader incorrect");
382 
383         // if you comment the following line, the test will work, and the ClassLoader will be released.
384         // That proves that nothing is wrong with the test, and MethodUtils is holding a reference
385         assertEquals("initialValue", MethodUtils.invokeExactMethod(bean, "getName", new Object[0]));
386 
387         // this should make the reference go away.
388         loader = null;
389         beanClass = null;
390         bean = null;
391 
392         forceGarbageCollection(); /* Try to force the garbage collector to run by filling up memory */
393 
394         if (someRef.get() != null) {
395             profilerLeakReport("MethodUtils cache", className);
396         }
397 
398         // if everything is fine, this will be null
399         assertNull(someRef.get(), "MethodUtils is holding a reference to the classLoader");
400 
401         // Clear All BeanUtils caches after the test
402         clearAllBeanUtilsCaches();
403     }
404 
405     /**
406      * Tests that PropertyUtilsBean's descriptorsCache doesn't cause a memory leak.
407      */
408     @Test
409     public void testPropertyUtilsBean_descriptorsCache_memoryLeak() throws Exception {
410 
411         // Clear All BeanUtils caches before the test
412         clearAllBeanUtilsCaches();
413 
414         final String className = "org.apache.commons.beanutils2.memoryleaktests.pojotests.SomePojo";
415 
416         // The classLoader will go away only when these following variables are released
417         ClassLoader loader = newClassLoader();
418         Class<?> beanClass = loader.loadClass(className);
419         Object bean = beanClass.newInstance();
420 
421         final WeakReference<ClassLoader> someRef = new WeakReference<>(loader);
422 
423         // Sanity checks only
424         assertNotNull(loader, "ClassLoader is null");
425         assertNotNull(beanClass, "BeanClass is null");
426         assertNotSame(getClass().getClassLoader(), beanClass.getClassLoader(), "ClassLoaders should be different..");
427         assertSame(beanClass.getClassLoader(), loader, "BeanClass ClassLoader incorrect");
428 
429         // if you comment the following line, the test will work, and the ClassLoader will be released.
430         // That proves that nothing is wrong with the test, and PropertyUtils is holding a reference
431         assertEquals("initialValue", PropertyUtils.getProperty(bean, "name"));
432 
433         // this should make the reference go away.
434         loader = null;
435         beanClass = null;
436         bean = null;
437 
438         forceGarbageCollection(); /* Try to force the garbage collector to run by filling up memory */
439 
440         if (someRef.get() != null) {
441             profilerLeakReport("PropertyUtilsBean descriptorsCache", className);
442         }
443 
444         // if everything is fine, this will be null
445         assertNull(someRef.get(), "PropertyUtilsBean is holding a reference to the classLoader");
446 
447         // Clear All BeanUtils caches after the test
448         clearAllBeanUtilsCaches();
449     }
450 
451     /**
452      * Tests that PropertyUtilsBean's mappedDescriptorsCache doesn't cause a memory leak.
453      */
454     @Test
455     public void testPropertyUtilsBean_mappedDescriptorsCache_memoryLeak() throws Exception {
456 
457         // Clear All BeanUtils caches before the test
458         clearAllBeanUtilsCaches();
459 
460         final String className = "org.apache.commons.beanutils2.memoryleaktests.pojotests.SomeMappedPojo";
461 
462         // The classLoader will go away only when these following variables are released
463         ClassLoader loader = newClassLoader();
464         Class<?> beanClass = loader.loadClass(className);
465         Object bean = beanClass.newInstance();
466 
467         final WeakReference<ClassLoader> someRef = new WeakReference<>(loader);
468 
469         // Sanity checks only
470         assertNotNull(loader, "ClassLoader is null");
471         assertNotNull(beanClass, "BeanClass is null");
472         assertNotSame(getClass().getClassLoader(), beanClass.getClassLoader(), "ClassLoaders should be different..");
473         assertSame(beanClass.getClassLoader(), loader, "BeanClass ClassLoader incorrect");
474 
475         // if you comment the following three lines, the test will work, and the ClassLoader will be released.
476         // That proves that nothing is wrong with the test, and PropertyUtils is holding a reference
477         assertEquals("Second Value", PropertyUtils.getProperty(bean, "mappedProperty(Second Key)"));
478         PropertyUtils.setProperty(bean, "mappedProperty(Second Key)", "New Second Value");
479         assertEquals("New Second Value", PropertyUtils.getProperty(bean, "mappedProperty(Second Key)"));
480 
481         // this should make the reference go away.
482         loader = null;
483         beanClass = null;
484         bean = null;
485 
486         // PropertyUtilsBean uses the MethodUtils's method cache for mapped properties.
487         // Uncomment the following line to check this is not just a repeat of that memory leak.
488         // MethodUtils.clearCache();
489 
490         forceGarbageCollection(); /* Try to force the garbage collector to run by filling up memory */
491 
492         if (someRef.get() != null) {
493             profilerLeakReport("PropertyUtilsBean mappedDescriptorsCache", className);
494         }
495 
496         // if everything is fine, this will be null
497         assertNull(someRef.get(), "PropertyUtilsBean is holding a reference to the classLoader");
498 
499         // Clear All BeanUtils caches after the test
500         clearAllBeanUtilsCaches();
501     }
502 
503     /**
504      * Tests that WrapDynaClass's dynaClasses doesn't cause a memory leak.
505      */
506     @Test
507     public void testWrapDynaClass_dynaClasses_memoryLeak() throws Exception {
508 
509         // Clear All BeanUtils caches before the test
510         clearAllBeanUtilsCaches();
511 
512         final String className = "org.apache.commons.beanutils2.memoryleaktests.pojotests.SomePojo";
513 
514         // The classLoader will go away only when these following variables are released
515         ClassLoader loader = newClassLoader();
516         Class<?> beanClass = loader.loadClass(className);
517         Object bean = beanClass.newInstance();
518         WrapDynaBean wrapDynaBean = new WrapDynaBean(bean);
519 
520         final WeakReference<ClassLoader> someRef = new WeakReference<>(loader);
521 
522         // Sanity checks only
523         assertNotNull(loader, "ClassLoader is null");
524         assertNotNull(beanClass, "BeanClass is null");
525         assertNotSame(getClass().getClassLoader(), beanClass.getClassLoader(), "ClassLoaders should be different..");
526         assertSame(beanClass.getClassLoader(), loader, "BeanClass ClassLoader incorrect");
527 
528         // if you comment the following line, the test will work, and the ClassLoader will be released.
529         // That proves that nothing is wrong with the test, and WrapDynaClass is holding a reference
530         assertEquals("initialValue", wrapDynaBean.get("name"));
531 
532         // this should make the reference go away.
533         loader = null;
534         beanClass = null;
535         bean = null;
536         wrapDynaBean = null;
537 
538         // Wrap Dyna Class uses the PropertyUtilsBean's decriptor caches.
539         // Uncomment the following line to check this is not just a repeat of that memory leak.
540         // BeanUtilsBean.getInstance().getPropertyUtils().clearDescriptors();
541 
542         forceGarbageCollection(); /* Try to force the garbage collector to run by filling up memory */
543 
544         if (someRef.get() != null) {
545             profilerLeakReport("WrapDynaClass dynaClasses", className);
546         }
547 
548         // if everything is fine, this will be null
549         assertNull(someRef.get(), "WrapDynaClass is holding a reference to the classLoader");
550 
551         // Clear All BeanUtils caches after the test
552         clearAllBeanUtilsCaches();
553     }
554 }