1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.pool2.impl;
19
20
21 import static org.junit.jupiter.api.Assertions.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertTrue;
23 import static org.junit.jupiter.api.Assertions.fail;
24
25 import java.util.Arrays;
26 import java.util.LinkedList;
27 import java.util.List;
28
29 import org.apache.commons.pool2.BasePooledObjectFactory;
30 import org.apache.commons.pool2.PooledObject;
31 import org.junit.jupiter.api.AfterEach;
32 import org.junit.jupiter.api.Test;
33
34
35
36 public class TestSoftRefOutOfMemory {
37 public static class LargePoolableObjectFactory extends BasePooledObjectFactory<String> {
38 private final String buffer;
39 private int counter;
40
41 public LargePoolableObjectFactory(final int size) {
42 final char[] data = new char[size];
43 Arrays.fill(data, '.');
44 buffer = new String(data);
45 }
46
47 @Override
48 public String create() {
49 counter++;
50 return String.valueOf(counter) + buffer;
51 }
52
53 @Override
54 public PooledObject<String> wrap(final String value) {
55 return new DefaultPooledObject<>(value);
56 }
57 }
58
59 private static class OomeFactory extends BasePooledObjectFactory<String> {
60
61 private final OomeTrigger trigger;
62
63 public OomeFactory(final OomeTrigger trigger) {
64 this.trigger = trigger;
65 }
66
67 @Override
68 public String create() {
69 if (trigger.equals(OomeTrigger.CREATE)) {
70 throw new OutOfMemoryError();
71 }
72
73
74
75
76
77 return new String();
78 }
79
80 @Override
81 public void destroyObject(final PooledObject<String> p) throws Exception {
82 if (trigger.equals(OomeTrigger.DESTROY)) {
83 throw new OutOfMemoryError();
84 }
85 super.destroyObject(p);
86 }
87
88 @Override
89 public boolean validateObject(final PooledObject<String> p) {
90 if (trigger.equals(OomeTrigger.VALIDATE)) {
91 throw new OutOfMemoryError();
92 }
93 return !trigger.equals(OomeTrigger.DESTROY);
94 }
95
96 @Override
97 public PooledObject<String> wrap(final String value) {
98 return new DefaultPooledObject<>(value);
99 }
100 }
101
102 private enum OomeTrigger {
103 CREATE,
104 VALIDATE,
105 DESTROY
106 }
107
108 public static class SmallPoolableObjectFactory extends BasePooledObjectFactory<String> {
109 private int counter;
110
111 @Override
112 public String create() {
113 counter++;
114
115
116
117
118
119 return new String(String.valueOf(counter));
120 }
121 @Override
122 public PooledObject<String> wrap(final String value) {
123 return new DefaultPooledObject<>(value);
124 }
125 }
126
127 private SoftReferenceObjectPool<String> pool;
128
129 @AfterEach
130 public void tearDown() {
131 if (pool != null) {
132 pool.close();
133 pool = null;
134 }
135 System.gc();
136 }
137
138 @Test
139 public void testOutOfMemory() throws Exception {
140 pool = new SoftReferenceObjectPool<>(new SmallPoolableObjectFactory());
141
142 String obj = pool.borrowObject();
143 assertEquals("1", obj);
144 pool.returnObject(obj);
145 obj = null;
146
147 assertEquals(1, pool.getNumIdle());
148
149 final List<byte[]> garbage = new LinkedList<>();
150 final Runtime runtime = Runtime.getRuntime();
151 while (pool.getNumIdle() > 0) {
152 try {
153 long freeMemory = runtime.freeMemory();
154 if (freeMemory > Integer.MAX_VALUE) {
155 freeMemory = Integer.MAX_VALUE;
156 }
157 garbage.add(new byte[Math.min(1024 * 1024, (int) freeMemory / 2)]);
158 } catch (final OutOfMemoryError oome) {
159 System.gc();
160 }
161 System.gc();
162 }
163 garbage.clear();
164 System.gc();
165
166 obj = pool.borrowObject();
167 assertEquals("2", obj);
168 pool.returnObject(obj);
169 obj = null;
170
171 assertEquals(1, pool.getNumIdle());
172 }
173
174 @Test
175 public void testOutOfMemory1000() throws Exception {
176 pool = new SoftReferenceObjectPool<>(new SmallPoolableObjectFactory());
177
178 for (int i = 0 ; i < 1000 ; i++) {
179 pool.addObject();
180 }
181
182 String obj = pool.borrowObject();
183 assertEquals("1000", obj);
184 pool.returnObject(obj);
185 obj = null;
186
187 assertEquals(1000, pool.getNumIdle());
188
189 final List<byte[]> garbage = new LinkedList<>();
190 final Runtime runtime = Runtime.getRuntime();
191 while (pool.getNumIdle() > 0) {
192 try {
193 long freeMemory = runtime.freeMemory();
194 if (freeMemory > Integer.MAX_VALUE) {
195 freeMemory = Integer.MAX_VALUE;
196 }
197 garbage.add(new byte[Math.min(1024 * 1024, (int) freeMemory / 2)]);
198 } catch (final OutOfMemoryError oome) {
199 System.gc();
200 }
201 System.gc();
202 }
203 garbage.clear();
204 System.gc();
205
206 obj = pool.borrowObject();
207 assertEquals("1001", obj);
208 pool.returnObject(obj);
209 obj = null;
210
211 assertEquals(1, pool.getNumIdle());
212 }
213
214
215
216
217
218
219 @Test
220 public void testOutOfMemoryError() throws Exception {
221 pool = new SoftReferenceObjectPool<>(
222 new OomeFactory(OomeTrigger.CREATE));
223
224 try {
225 pool.borrowObject();
226 fail("Expected out of memory.");
227 }
228 catch (final OutOfMemoryError ex) {
229
230 }
231 pool.close();
232
233 pool = new SoftReferenceObjectPool<>(
234 new OomeFactory(OomeTrigger.VALIDATE));
235
236 try {
237 pool.borrowObject();
238 fail("Expected out of memory.");
239 }
240 catch (final OutOfMemoryError ex) {
241
242 }
243 pool.close();
244
245 pool = new SoftReferenceObjectPool<>(
246 new OomeFactory(OomeTrigger.DESTROY));
247
248 try {
249 pool.borrowObject();
250 fail("Expected out of memory.");
251 }
252 catch (final OutOfMemoryError ex) {
253
254 }
255 pool.close();
256 }
257
258 @Test
259 public void testOutOfMemoryLarge() throws Exception {
260 pool = new SoftReferenceObjectPool<>(new LargePoolableObjectFactory(1000000));
261
262 String obj = pool.borrowObject();
263 assertTrue(obj.startsWith("1."));
264 pool.returnObject(obj);
265 obj = null;
266
267 assertEquals(1, pool.getNumIdle());
268
269 final List<byte[]> garbage = new LinkedList<>();
270 final Runtime runtime = Runtime.getRuntime();
271 while (pool.getNumIdle() > 0) {
272 try {
273 long freeMemory = runtime.freeMemory();
274 if (freeMemory > Integer.MAX_VALUE) {
275 freeMemory = Integer.MAX_VALUE;
276 }
277 garbage.add(new byte[Math.min(1024 * 1024, (int) freeMemory / 2)]);
278 } catch (final OutOfMemoryError oome) {
279 System.gc();
280 }
281 System.gc();
282 }
283 garbage.clear();
284 System.gc();
285
286 obj = pool.borrowObject();
287 assertTrue(obj.startsWith("2."));
288 pool.returnObject(obj);
289 obj = null;
290
291 assertEquals(1, pool.getNumIdle());
292 }
293 }