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