001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.pool.impl;
019
020 import junit.framework.TestCase;
021 import junit.framework.TestSuite;
022 import org.apache.commons.pool.BasePoolableObjectFactory;
023 import org.apache.commons.pool.PoolableObjectFactory;
024
025 import java.util.Arrays;
026 import java.util.LinkedList;
027 import java.util.List;
028
029 /**
030 * @author Dirk Verbeeck
031 * @author Sandy McArthur
032 * @version $Revision: 480413 $ $Date: 2006-11-29 00:16:05 -0500 (Wed, 29 Nov 2006) $
033 */
034 public class TestSoftRefOutOfMemory extends TestCase {
035 private SoftReferenceObjectPool pool;
036
037 public TestSoftRefOutOfMemory(String testName) {
038 super(testName);
039 }
040
041 public static TestSuite suite() {
042 return new TestSuite(TestSoftRefOutOfMemory.class);
043 }
044
045 public void tearDown() throws Exception {
046 if (pool != null) {
047 pool.close();
048 pool = null;
049 }
050 System.gc();
051 }
052
053 public void testOutOfMemory() throws Exception {
054 pool = new SoftReferenceObjectPool(new SmallPoolableObjectFactory());
055
056 Object obj = pool.borrowObject();
057 assertEquals("1", obj);
058 pool.returnObject(obj);
059 obj = null;
060
061 assertEquals(1, pool.getNumIdle());
062
063 final List garbage = new LinkedList();
064 final Runtime runtime = Runtime.getRuntime();
065 while (pool.getNumIdle() > 0) {
066 try {
067 garbage.add(new byte[Math.min(1024 * 1024, (int)runtime.freeMemory()/2)]);
068 } catch (OutOfMemoryError oome) {
069 System.gc();
070 }
071 System.gc();
072 }
073 garbage.clear();
074 System.gc();
075
076 obj = pool.borrowObject();
077 assertEquals("2", obj);
078 pool.returnObject(obj);
079 obj = null;
080
081 assertEquals(1, pool.getNumIdle());
082 }
083
084 public void testOutOfMemory1000() throws Exception {
085 pool = new SoftReferenceObjectPool(new SmallPoolableObjectFactory());
086
087 for (int i = 0 ; i < 1000 ; i++) {
088 pool.addObject();
089 }
090
091 Object obj = pool.borrowObject();
092 assertEquals("1000", obj);
093 pool.returnObject(obj);
094 obj = null;
095
096 assertEquals(1000, pool.getNumIdle());
097
098 final List garbage = new LinkedList();
099 final Runtime runtime = Runtime.getRuntime();
100 while (pool.getNumIdle() > 0) {
101 try {
102 garbage.add(new byte[Math.min(1024 * 1024, (int)runtime.freeMemory()/2)]);
103 } catch (OutOfMemoryError oome) {
104 System.gc();
105 }
106 System.gc();
107 }
108 garbage.clear();
109 System.gc();
110
111 obj = pool.borrowObject();
112 assertEquals("1001", obj);
113 pool.returnObject(obj);
114 obj = null;
115
116 assertEquals(1, pool.getNumIdle());
117 }
118
119 public void testOutOfMemoryLarge() throws Exception {
120 pool = new SoftReferenceObjectPool(new LargePoolableObjectFactory(1000000));
121
122 Object obj = pool.borrowObject();
123 assertTrue(((String)obj).startsWith("1."));
124 pool.returnObject(obj);
125 obj = null;
126
127 assertEquals(1, pool.getNumIdle());
128
129 final List garbage = new LinkedList();
130 final Runtime runtime = Runtime.getRuntime();
131 while (pool.getNumIdle() > 0) {
132 try {
133 garbage.add(new byte[Math.min(1024 * 1024, (int)runtime.freeMemory()/2)]);
134 } catch (OutOfMemoryError oome) {
135 System.gc();
136 }
137 System.gc();
138 }
139 garbage.clear();
140 System.gc();
141
142 obj = pool.borrowObject();
143 assertTrue(((String)obj).startsWith("2."));
144 pool.returnObject(obj);
145 obj = null;
146
147 assertEquals(1, pool.getNumIdle());
148 }
149
150 /**
151 * Makes sure an {@link OutOfMemoryError} isn't swallowed.
152 */
153 public void testOutOfMemoryError() throws Exception {
154 pool = new SoftReferenceObjectPool(new BasePoolableObjectFactory() {
155 public Object makeObject() throws Exception {
156 throw new OutOfMemoryError();
157 }
158 });
159
160 try {
161 pool.borrowObject();
162 fail("Expected out of memory.");
163 }
164 catch (OutOfMemoryError ex) {
165 // expected
166 }
167 pool.close();
168
169 pool = new SoftReferenceObjectPool(new BasePoolableObjectFactory() {
170 public Object makeObject() throws Exception {
171 return new Object();
172 }
173
174 public boolean validateObject(Object obj) {
175 throw new OutOfMemoryError();
176 }
177 });
178
179 pool.returnObject(pool.borrowObject());
180
181 try {
182 pool.borrowObject();
183 fail("Expected out of memory.");
184 }
185 catch (OutOfMemoryError ex) {
186 // expected
187 }
188 pool.close();
189 }
190
191
192 public static class SmallPoolableObjectFactory implements PoolableObjectFactory {
193 private int counter = 0;
194
195 public Object makeObject() {
196 counter++;
197 // It seems that as of Java 1.4 String.valueOf may return an
198 // intern()'ed String this may cause problems when the tests
199 // depend on the returned object to be eventually garbaged
200 // collected. Either way, making sure a new String instance
201 // is returned eliminated false failures.
202 return new String(String.valueOf(counter));
203 }
204 public boolean validateObject(Object obj) {
205 return true;
206 }
207 public void activateObject(Object obj) { }
208 public void passivateObject(Object obj) { }
209 public void destroyObject(Object obj) { }
210 }
211
212 public static class LargePoolableObjectFactory implements PoolableObjectFactory {
213 private String buffer;
214 private int counter = 0;
215
216 public LargePoolableObjectFactory(int size) {
217 char[] data = new char[size];
218 Arrays.fill(data, '.');
219 buffer = new String(data);
220 }
221
222 public Object makeObject() {
223 counter++;
224 return String.valueOf(counter) + buffer;
225 }
226 public boolean validateObject(Object obj) {
227 return true;
228 }
229 public void activateObject(Object obj) { }
230 public void passivateObject(Object obj) { }
231 public void destroyObject(Object obj) { }
232 }
233 }