1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.beanutils.memoryleaktests;
18
19 import java.lang.ref.SoftReference;
20 import java.lang.ref.WeakReference;
21 import java.net.MalformedURLException;
22 import java.net.URL;
23 import java.net.URLClassLoader;
24 import java.util.Locale;
25
26 import org.apache.commons.beanutils.BeanUtilsBean;
27 import org.apache.commons.beanutils.ConvertUtils;
28 import org.apache.commons.beanutils.MappedPropertyDescriptor;
29 import org.apache.commons.beanutils.MethodUtils;
30 import org.apache.commons.beanutils.PropertyUtils;
31 import org.apache.commons.beanutils.WrapDynaBean;
32 import org.apache.commons.beanutils.WrapDynaClass;
33 import org.apache.commons.beanutils.converters.IntegerConverter;
34 import org.apache.commons.beanutils.locale.LocaleBeanUtilsBean;
35 import org.apache.commons.beanutils.locale.LocaleConvertUtils;
36 import org.apache.commons.beanutils.locale.converters.IntegerLocaleConverter;
37 import org.junit.Assume;
38 import org.junit.Test;
39 import static org.junit.Assert.*;
40
41
42
43
44
45
46
47
48 public class MemoryLeakTestCase {
49
50
51
52
53 @Test
54 public void testPropertyUtilsBean_descriptorsCache_memoryLeak() throws Exception {
55
56
57 clearAllBeanUtilsCaches();
58
59 final String className = "org.apache.commons.beanutils.memoryleaktests.pojotests.SomePojo";
60
61
62 ClassLoader loader = newClassLoader();
63 Class<?> beanClass = loader.loadClass(className);
64 Object bean = beanClass.newInstance();
65
66
67 final WeakReference<ClassLoader> someRef = new WeakReference<ClassLoader>(loader);
68
69
70 assertNotNull("ClassLoader is null", loader);
71 assertNotNull("BeanClass is null", beanClass);
72 assertNotSame("ClassLoaders should be different..", getClass().getClassLoader(), beanClass.getClassLoader());
73 assertSame("BeanClass ClassLoader incorrect", beanClass.getClassLoader(), loader);
74
75
76
77 assertEquals("initialValue", PropertyUtils.getProperty(bean, "name"));
78
79
80 loader = null;
81 beanClass = null;
82 bean = null;
83
84 forceGarbageCollection();
85
86 if (someRef.get() != null) {
87 profilerLeakReport("PropertyUtilsBean descriptorsCache", className);
88 }
89
90
91 assertNull("PropertyUtilsBean is holding a reference to the classLoader", someRef.get());
92
93
94 clearAllBeanUtilsCaches();
95 }
96
97
98
99
100 @Test
101 public void testPropertyUtilsBean_mappedDescriptorsCache_memoryLeak() throws Exception {
102
103
104 clearAllBeanUtilsCaches();
105
106 final String className = "org.apache.commons.beanutils.memoryleaktests.pojotests.SomeMappedPojo";
107
108
109 ClassLoader loader = newClassLoader();
110 Class<?> beanClass = loader.loadClass(className);
111 Object bean = beanClass.newInstance();
112
113
114 final WeakReference<ClassLoader> someRef = new WeakReference<ClassLoader>(loader);
115
116
117 assertNotNull("ClassLoader is null", loader);
118 assertNotNull("BeanClass is null", beanClass);
119 assertNotSame("ClassLoaders should be different..", getClass().getClassLoader(), beanClass.getClassLoader());
120 assertSame("BeanClass ClassLoader incorrect", beanClass.getClassLoader(), loader);
121
122
123
124 assertEquals("Second Value", PropertyUtils.getProperty(bean, "mappedProperty(Second Key)"));
125 PropertyUtils.setProperty(bean, "mappedProperty(Second Key)", "New Second Value");
126 assertEquals("New Second Value", PropertyUtils.getProperty(bean, "mappedProperty(Second Key)"));
127
128
129 loader = null;
130 beanClass = null;
131 bean = null;
132
133
134
135
136
137 forceGarbageCollection();
138
139 if (someRef.get() != null) {
140 profilerLeakReport("PropertyUtilsBean mappedDescriptorsCache", className);
141 }
142
143
144 assertNull("PropertyUtilsBean is holding a reference to the classLoader", someRef.get());
145
146
147 clearAllBeanUtilsCaches();
148 }
149
150
151
152
153
154 @Test
155 public void testMappedPropertyDescriptor_MappedMethodReference1() throws Exception {
156
157
158 clearAllBeanUtilsCaches();
159
160 final String className = "org.apache.commons.beanutils.memoryleaktests.pojotests.SomeMappedPojo";
161 final ClassLoader loader = newClassLoader();
162 final Class<?> beanClass = loader.loadClass(className);
163 final Object bean = beanClass.newInstance();
164
165
166
167 assertNotNull("ClassLoader is null", loader);
168 assertNotNull("BeanClass is null", beanClass);
169 assertNotNull("Bean is null", bean);
170 assertNotSame("ClassLoaders should be different..", getClass().getClassLoader(), beanClass.getClassLoader());
171 assertSame("BeanClass ClassLoader incorrect", beanClass.getClassLoader(), loader);
172
173 final MappedPropertyDescriptor descriptor = new MappedPropertyDescriptor("mappedProperty", beanClass);
174 assertNotNull("1-Read Method null", descriptor.getMappedReadMethod());
175 assertNotNull("1-Write Method null", descriptor.getMappedWriteMethod());
176 assertEquals("1-Read Method name", "getMappedProperty", descriptor.getMappedReadMethod().getName());
177 assertEquals("1-Read Write name", "setMappedProperty", descriptor.getMappedWriteMethod().getName());
178
179 forceGarbageCollection();
180
181
182
183
184
185
186
187 assertNotNull("1-Read Method null", descriptor.getMappedReadMethod());
188 assertNotNull("1-Write Method null", descriptor.getMappedWriteMethod());
189 assertEquals("1-Read Method name", "getMappedProperty", descriptor.getMappedReadMethod().getName());
190 assertEquals("1-Read Write name", "setMappedProperty", descriptor.getMappedWriteMethod().getName());
191
192
193 clearAllBeanUtilsCaches();
194 }
195
196
197
198
199
200 @Test
201 public void testMappedPropertyDescriptor_MappedMethodReference2() throws Exception {
202
203
204 clearAllBeanUtilsCaches();
205
206 final String className = "org.apache.commons.beanutils.memoryleaktests.pojotests.SomeMappedPojo";
207 ClassLoader loader = newClassLoader();
208 Class<?> beanClass = loader.loadClass(className);
209 Object bean = beanClass.newInstance();
210
211
212
213 assertNotNull("ClassLoader is null", loader);
214 assertNotNull("BeanClass is null", beanClass);
215 assertNotNull("Bean is null", bean);
216 assertNotSame("ClassLoaders should be different..", getClass().getClassLoader(), beanClass.getClassLoader());
217 assertSame("BeanClass ClassLoader incorrect", beanClass.getClassLoader(), loader);
218
219 final MappedPropertyDescriptor descriptor = new MappedPropertyDescriptor("mappedProperty", beanClass);
220 assertNotNull("1-Read Method null", descriptor.getMappedReadMethod());
221 assertNotNull("1-Write Method null", descriptor.getMappedWriteMethod());
222 assertEquals("1-Read Method name", "getMappedProperty", descriptor.getMappedReadMethod().getName());
223 assertEquals("1-Read Write name", "setMappedProperty", descriptor.getMappedWriteMethod().getName());
224
225
226 loader = null;
227 beanClass = null;
228 bean = null;
229
230 forceGarbageCollection();
231
232
233
234
235
236
237
238 assertNotNull("1-Read Method null", descriptor.getMappedReadMethod());
239 assertNotNull("1-Write Method null", descriptor.getMappedWriteMethod());
240 assertEquals("1-Read Method name", "getMappedProperty", descriptor.getMappedReadMethod().getName());
241 assertEquals("1-Read Write name", "setMappedProperty", descriptor.getMappedWriteMethod().getName());
242
243
244 clearAllBeanUtilsCaches();
245 }
246
247
248
249
250 @Test
251 public void testMethodUtils_cache_memoryLeak() throws Exception {
252
253
254 clearAllBeanUtilsCaches();
255
256 final String className = "org.apache.commons.beanutils.memoryleaktests.pojotests.SomePojo";
257
258
259 ClassLoader loader = newClassLoader();
260 Class<?> beanClass = loader.loadClass(className);
261 Object bean = beanClass.newInstance();
262
263
264 final WeakReference<ClassLoader> someRef = new WeakReference<ClassLoader>(loader);
265
266
267 assertNotNull("ClassLoader is null", loader);
268 assertNotNull("BeanClass is null", beanClass);
269 assertNotSame("ClassLoaders should be different..", getClass().getClassLoader(), beanClass.getClassLoader());
270 assertSame("BeanClass ClassLoader incorrect", beanClass.getClassLoader(), loader);
271
272
273
274 assertEquals("initialValue", MethodUtils.invokeExactMethod(bean, "getName", new Object[0]));
275
276
277 loader = null;
278 beanClass = null;
279 bean = null;
280
281 forceGarbageCollection();
282
283 if (someRef.get() != null) {
284 profilerLeakReport("MethodUtils cache", className);
285 }
286
287
288 assertNull("MethodUtils is holding a reference to the classLoader", someRef.get());
289
290
291 clearAllBeanUtilsCaches();
292 }
293
294
295
296
297 @Test
298 public void testWrapDynaClass_dynaClasses_memoryLeak() throws Exception {
299
300
301 clearAllBeanUtilsCaches();
302
303 final String className = "org.apache.commons.beanutils.memoryleaktests.pojotests.SomePojo";
304
305
306 ClassLoader loader = newClassLoader();
307 Class<?> beanClass = loader.loadClass(className);
308 Object bean = beanClass.newInstance();
309 WrapDynaBean wrapDynaBean = new WrapDynaBean(bean);
310
311
312 final WeakReference<ClassLoader> someRef = new WeakReference<ClassLoader>(loader);
313
314
315 assertNotNull("ClassLoader is null", loader);
316 assertNotNull("BeanClass is null", beanClass);
317 assertNotSame("ClassLoaders should be different..", getClass().getClassLoader(), beanClass.getClassLoader());
318 assertSame("BeanClass ClassLoader incorrect", beanClass.getClassLoader(), loader);
319
320
321
322 assertEquals("initialValue", wrapDynaBean.get("name"));
323
324
325 loader = null;
326 beanClass = null;
327 bean = null;
328 wrapDynaBean = null;
329
330
331
332
333
334 forceGarbageCollection();
335
336 if (someRef.get() != null) {
337 profilerLeakReport("WrapDynaClass dynaClasses", className);
338 }
339
340
341 assertNull("WrapDynaClass is holding a reference to the classLoader", someRef.get());
342
343
344 clearAllBeanUtilsCaches();
345 }
346
347
348
349
350 @Test
351 public void testConvertUtilsBean_converters_memoryLeak() throws Exception {
352
353
354 clearAllBeanUtilsCaches();
355
356 final String className = "org.apache.commons.beanutils.memoryleaktests.pojotests.CustomInteger";
357
358
359 ClassLoader loader = newClassLoader();
360 Class<?> beanClass = loader.loadClass(className);
361 Object bean = beanClass.newInstance();
362
363
364 final WeakReference<ClassLoader> someRef = new WeakReference<ClassLoader>(loader);
365
366
367 assertNotNull("ClassLoader is null", loader);
368 assertNotNull("BeanClass is null", beanClass);
369 assertNotSame("ClassLoaders should be different..", getClass().getClassLoader(), beanClass.getClassLoader());
370 assertSame("BeanClass ClassLoader incorrect", beanClass.getClassLoader(), loader);
371
372
373
374 ConvertUtils.register(new IntegerConverter(), beanClass);
375 assertEquals("12345", ConvertUtils.convert(bean, String.class));
376
377
378 loader = null;
379 beanClass = null;
380 bean = null;
381
382 forceGarbageCollection();
383
384 if (someRef.get() != null) {
385 profilerLeakReport("ConvertUtilsBean converters", className);
386 }
387
388
389 assertNull("ConvertUtilsBean is holding a reference to the classLoader", someRef.get());
390
391
392 clearAllBeanUtilsCaches();
393 }
394
395
396
397
398 @Test
399 public void testLocaleConvertUtilsBean_converters_memoryLeak() throws Exception {
400
401
402 clearAllBeanUtilsCaches();
403
404 final String className = "org.apache.commons.beanutils.memoryleaktests.pojotests.CustomInteger";
405
406
407 ClassLoader loader = newClassLoader();
408 Class<?> beanClass = loader.loadClass(className);
409 Object bean = beanClass.newInstance();
410
411
412 final WeakReference<ClassLoader> someRef = new WeakReference<ClassLoader>(loader);
413
414
415 assertNotNull("ClassLoader is null", loader);
416 assertNotNull("BeanClass is null", beanClass);
417 assertNotSame("ClassLoaders should be different..", getClass().getClassLoader(), beanClass.getClassLoader());
418 assertSame("BeanClass ClassLoader incorrect", beanClass.getClassLoader(), loader);
419
420
421
422 LocaleConvertUtils.register(new IntegerLocaleConverter(Locale.US, false), beanClass, Locale.US);
423 assertEquals(new Integer(12345), LocaleConvertUtils.convert(bean.toString(), Integer.class, Locale.US, "#,###"));
424
425
426 loader = null;
427 beanClass = null;
428 bean = null;
429
430 forceGarbageCollection();
431
432 if (someRef.get() != null) {
433 profilerLeakReport("LocaleConvertUtilsBean converters", className);
434 }
435
436
437 assertNull("LocaleConvertUtilsBean is holding a reference to the classLoader", someRef.get());
438
439
440 clearAllBeanUtilsCaches();
441 }
442
443
444
445
446
447
448
449 private void clearAllBeanUtilsCaches() {
450
451
452 BeanUtilsBean.getInstance().getPropertyUtils().clearDescriptors();
453
454
455 LocaleBeanUtilsBean.getLocaleBeanUtilsInstance().getPropertyUtils().clearDescriptors();
456
457
458 MethodUtils.clearCache();
459
460
461 WrapDynaClass.clear();
462
463
464 BeanUtilsBean.setInstance(new BeanUtilsBean());
465
466
467 LocaleBeanUtilsBean.setInstance(new LocaleBeanUtilsBean());
468 }
469
470
471
472
473 private void forceGarbageCollection() throws Exception {
474
475 final SoftReference<Object> ref = new SoftReference<Object>(new Object());
476 int count = 0;
477 while(ref.get() != null && count++ < 5) {
478 java.util.ArrayList<String> list = new java.util.ArrayList<String>();
479 try {
480 long i = 0;
481 while (true && ref.get() != null) {
482 list.add("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 " + (i++));
483 }
484 System.out.println("Count(1) " + count + " : " + getMemoryStats());
485 } catch (final OutOfMemoryError ignored) {
486
487 }
488
489 System.gc();
490 list.clear();
491 list = null;
492 System.gc();
493 System.out.println("After GC2: " + getMemoryStats()+ " Count " + count);
494 Thread.sleep(1000);
495 }
496
497 final boolean isNotNull = ref.get() != null;
498 System.out.println("Count " + count+ " " + isNotNull);
499 String message = "Your JVM is not releasing SoftReference, try running the testcase with less memory (-Xmx)";
500 Assume.assumeFalse(message, isNotNull);
501 }
502
503
504
505
506 private static URLClassLoader newClassLoader() throws MalformedURLException {
507
508 final String dataFilePath = MemoryLeakTestCase.class.getResource("pojotests").getFile();
509
510 final String location = "file://" + dataFilePath.substring(0,dataFilePath.length()-"org.apache.commons.beanutils.memoryleaktests.pojotests".length());
511
512
513 final StringBuilder newString = new StringBuilder();
514 for (int i=0;i<location.length();i++) {
515 if (location.charAt(i)=='\\') {
516 newString.append("/");
517 } else {
518 newString.append(location.charAt(i));
519 }
520 }
521 final String classLocation = newString.toString();
522
523
524 final URLClassLoader theLoader = URLClassLoader.newInstance(new URL[]{new URL(classLocation)},null);
525 return theLoader;
526 }
527
528
529
530
531
532
533
534
535
536 private void profilerLeakReport(final String test, final String className) {
537
538
539
540
541
542
543
544
545
546 }
547
548
549
550
551
552 private String getMemoryStats() {
553 final java.text.DecimalFormat fmt = new java.text.DecimalFormat("#,##0");
554 final Runtime runtime = Runtime.getRuntime();
555 final long free = runtime.freeMemory() / 1024;
556 final long total = runtime.totalMemory() / 1024;
557 final long used = total - free;
558 return "MEMORY - Total: " + fmt.format(total) + "k " + "Used: "
559 + fmt.format(used) + "k " + "Free: "
560 + fmt.format(free) + "k";
561 }
562 }