Source code: edu/emory/mathcs/util/allocator/PoolingAllocator.java
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Original Code is the Emory Utilities.
15 *
16 * The Initial Developer of the Original Code is
17 * The Distributed Computing Laboratory, Emory University.
18 * Portions created by the Initial Developer are Copyright (C) 2002
19 * the Initial Developer. All Rights Reserved.
20 *
21 * Alternatively, the contents of this file may be used under the terms of
22 * either the GNU General Public License Version 2 or later (the "GPL"), or
23 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
24 * in which case the provisions of the GPL or the LGPL are applicable instead
25 * of those above. If you wish to allow use of your version of this file only
26 * under the terms of either the GPL or the LGPL, and not to allow others to
27 * use your version of this file under the terms of the MPL, indicate your
28 * decision by deleting the provisions above and replace them with the notice
29 * and other provisions required by the GPL or the LGPL. If you do not delete
30 * the provisions above, a recipient may use your version of this file under
31 * the terms of any one of the MPL, the GPL or the LGPL.
32 *
33 * ***** END LICENSE BLOCK ***** */
34
35 package edu.emory.mathcs.util.allocator;
36
37 /**
38 * Implements the {@link Allocator} using memory buffer pool. Able to enforce
39 * memory usage limits; attempts to avoid OutOfMemoryErrors by postponing the
40 * allocate requests when there is insufficient memory available in the system.
41 * Parameters of the pool are: (1) reserved capacity --
42 * if the current memory usage of the pool is below this value, the allocate
43 * request will be always attempted without blocking; (2) maximum capacity --
44 * if the allocate request would inflate the current memory usage above this
45 * value, the call will block until more memory is available; (3) security
46 * margin -- if the memory available in the system is below this value, the
47 * allocate request will block unless current usage is below reserved capacity.
48 *
49 * @author Dawid Kurzyniec
50 * @version 1.0
51 */
52
53 public class PoolingAllocator implements Allocator {
54 final BufferPool bufpool;
55
56 private final Runtime runtime;
57
58 private final long reserved, max;
59 private final long minLeft;
60
61 private long memOccupied = 0;
62
63 // minimize freeMemory() calls
64 private long lastFreeMem = 0;
65 private long occupiedOnLastFreeMem = 0;
66
67 /**
68 * Constructs pooling allocator with default reserved capacity of
69 * 1 MB, unlimited maximum capacity and default security margin
70 * of 2 MB.
71 */
72 public PoolingAllocator() {
73 this(1*1024*1024, -1);
74 }
75
76 /**
77 * Constructs pooling allocator with specified reserved and maximum capacity
78 * and default security margin of 2 MB.
79 *
80 * @param reserved the reserved capacity.
81 * @param max the maximum capacity.
82 */
83 public PoolingAllocator(long reserved, long max) {
84 this(reserved, max, 2*1024*1024);
85 }
86
87 /**
88 * Constructs pooling allocator with specified reserved and maximum capacity
89 * and specified security margin.
90 *
91 * @param reserved the reserved capacity.
92 * @param max the maximum capacity.
93 * @param minLeft the security margin.
94 */
95 public PoolingAllocator(long reserved, long max, int minLeft) {
96 this(reserved, max, minLeft, new BufferPool());
97 }
98
99 /**
100 * Constructs pooling allocator with specified reserved and maximum capacity
101 * and specified security margin, as well as given buffer pool.
102 *
103 * @param reserved the reserved capacity.
104 * @param max the maximum capacity.
105 * @param minLeft the security margin.
106 * @param pool the buffer pool to use.
107 */
108 public PoolingAllocator(long reserved, long max, int minLeft, BufferPool bufpool) {
109 this.bufpool = bufpool;
110 this.reserved = reserved;
111 this.max = max;
112 this.minLeft = minLeft;
113 this.runtime = Runtime.getRuntime();
114 }
115
116 public Allocator.Buffer allocate(int size, boolean clear, long timeout)
117 throws InterruptedException
118 {
119 long endTime = -1;
120 synchronized (this) {
121 while (!canAlloc(size)) {
122 // need to wait
123 if (timeout < 0) {
124 // wait until notification
125 wait();
126 } else if (timeout == 0) {
127 // do not wait
128 return null;
129 } else {
130 // wait remaining time
131 long remaining;
132 if (endTime < 0) {
133 endTime = System.currentTimeMillis() + timeout;
134 remaining = timeout;
135 } else {
136 remaining = endTime - System.currentTimeMillis();
137 }
138 if (remaining <= 0) return null;
139 wait(remaining);
140 }
141 }
142
143 // green light
144 memOccupied += size;
145
146 // continue unsynchronized
147 }
148
149 byte[] data = bufpool.get(size, clear);
150 Buffer buf = new Buffer(data, size);
151 if (data.length > size) {
152 synchronized (this) {
153 memOccupied += (data.length - size);
154 }
155 }
156 return buf;
157 }
158
159 private boolean canAlloc(int size) {
160 if (max >= 0 && memOccupied + size > max) return false;
161 if (memOccupied + size <= reserved) return true;
162 if (minLeft < 0) return true;
163 long morenew = memOccupied-occupiedOnLastFreeMem;
164 // minimize calls to freeMemory(); do it only if memory allocated
165 // since the last call exceeded 1MB or 1/16 of the remaining
166 if (morenew > 1024*1024 || morenew > lastFreeMem / 16) {
167 lastFreeMem = runtime.freeMemory();
168 occupiedOnLastFreeMem = memOccupied;
169 }
170 if (lastFreeMem >= size + minLeft) return true;
171 return false;
172 }
173
174 private void reclaim(byte[] data) {
175 synchronized (this) {
176 memOccupied -= data.length;
177 notifyAll();
178 }
179 bufpool.reclaim(data);
180 }
181
182 private class Buffer extends Allocator.Buffer {
183 Buffer(byte[] data, int size) {
184 super(data, size);
185 }
186 protected void reclaim() {
187 PoolingAllocator.this.reclaim(this.data);
188 }
189 }
190 }