source: josm/trunk/src/org/openstreetmap/josm/tools/MemoryManager.java@ 11073

Last change on this file since 11073 was 10717, checked in by simon04, 8 years ago

see #11390, see #12890 - Lambda can be replaced with method reference

File size: 6.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3import static org.openstreetmap.josm.tools.I18n.tr;
4
5import java.text.MessageFormat;
6import java.util.ArrayList;
7import java.util.List;
8import java.util.function.Supplier;
9
10import org.openstreetmap.josm.Main;
11
12/**
13 * This class allows all components of JOSM to register reclaimable amounts to memory.
14 * <p>
15 * It can be used to hold imagery caches or other data that can be reconstructed form disk/web if required.
16 * <p>
17 * Reclaimable storage implementations may be added in the future.
18 *
19 * @author Michael Zangl
20 * @since 10588
21 */
22public class MemoryManager {
23 /**
24 * assumed minimum JOSM memory footprint
25 */
26 private static final long JOSM_CORE_FOOTPRINT = 50L * 1024L * 1024L;
27
28 private static final MemoryManager INSTANCE = new MemoryManager();
29
30 private final ArrayList<MemoryHandle<?>> activeHandles = new ArrayList<>();
31
32 protected MemoryManager() {
33 }
34
35 /**
36 * Allocates a basic, fixed memory size.
37 * <p>
38 * If there is enough free memory, the factory is used to procude one element which is then returned as memory handle.
39 * <p>
40 * You should invoke {@link MemoryHandle#free()} if you do not need that handle any more.
41 * @param <T> The content type of the memory-
42 * @param name A name for the memory area. Only used for debugging.
43 * @param maxBytes The maximum amount of bytes the content may have
44 * @param factory The factory to use to procude the content if there is sufficient memory.
45 * @return A memory handle to the content.
46 * @throws NotEnoughMemoryException If there is not enough memory to allocate.
47 */
48 public synchronized <T> MemoryHandle<T> allocateMemory(String name, long maxBytes, Supplier<T> factory) throws NotEnoughMemoryException {
49 if (isAvailable(maxBytes)) {
50 T content = factory.get();
51 if (content == null) {
52 throw new IllegalArgumentException("Factory did not return a content element.");
53 }
54 Main.info(MessageFormat.format("Allocate for {0}: {1} MB of memory. Available: {2} MB.",
55 name, maxBytes / 1024 / 1024, getAvailableMemory() / 1024 / 1024));
56 MemoryHandle<T> handle = new ManualFreeMemoryHandle<>(name, content, maxBytes);
57 activeHandles.add(handle);
58 return handle;
59 } else {
60 throw new NotEnoughMemoryException(maxBytes);
61 }
62 }
63
64 /**
65 * Check if that memory is available
66 * @param maxBytes The memory to check for
67 * @return <code>true</code> if that memory is available.
68 */
69 public synchronized boolean isAvailable(long maxBytes) {
70 if (maxBytes < 0) {
71 throw new IllegalArgumentException(MessageFormat.format("Cannot allocate negative number of bytes: {0}", maxBytes));
72 }
73 return getAvailableMemory() >= maxBytes;
74 }
75
76 /**
77 * Gets the maximum amount of memory available for use in this manager.
78 * @return The maximum amount of memory.
79 */
80 public synchronized long getMaxMemory() {
81 return Runtime.getRuntime().maxMemory() - JOSM_CORE_FOOTPRINT;
82 }
83
84 /**
85 * Gets the memory that is considered free.
86 * @return The memory that can be used for new allocations.
87 */
88 public synchronized long getAvailableMemory() {
89 return getMaxMemory() - activeHandles.stream().mapToLong(MemoryHandle::getSize).sum();
90 }
91
92 /**
93 * Get the global memory manager instance.
94 * @return The memory manager.
95 */
96 public static MemoryManager getInstance() {
97 return INSTANCE;
98 }
99
100 /**
101 * Reset the state of this manager to the default state.
102 * @return true if there were entries that have been reset.
103 */
104 protected synchronized List<MemoryHandle<?>> resetState() {
105 ArrayList<MemoryHandle<?>> toFree = new ArrayList<>(activeHandles);
106 toFree.forEach(MemoryHandle::free);
107 return toFree;
108 }
109
110 /**
111 * A memory area managed by the {@link MemoryManager}.
112 * @author Michael Zangl
113 * @param <T> The content type.
114 */
115 public interface MemoryHandle<T> {
116
117 /**
118 * Gets the content of this memory area.
119 * <p>
120 * This method should be the prefered access to the memory since it will do error checking when {@link #free()} was called.
121 * @return The memory area content.
122 */
123 T get();
124
125 /**
126 * Get the size that was requested for this memory area.
127 * @return the size
128 */
129 long getSize();
130
131 /**
132 * Manually release this memory area. There should be no memory consumed by this afterwards.
133 */
134 void free();
135 }
136
137 private class ManualFreeMemoryHandle<T> implements MemoryHandle<T> {
138 private final String name;
139 private T content;
140 private final long size;
141
142 ManualFreeMemoryHandle(String name, T content, long size) {
143 this.name = name;
144 this.content = content;
145 this.size = size;
146 }
147
148 @Override
149 public T get() {
150 if (content == null) {
151 throw new IllegalStateException(MessageFormat.format("Memory area was accessed after free(): {0}", name));
152 }
153 return content;
154 }
155
156 @Override
157 public long getSize() {
158 return size;
159 }
160
161 @Override
162 public void free() {
163 if (content == null) {
164 throw new IllegalStateException(MessageFormat.format("Memory area was already marked as freed: {0}", name));
165 }
166 content = null;
167 synchronized (MemoryManager.this) {
168 activeHandles.remove(this);
169 }
170 }
171
172 @Override
173 public String toString() {
174 return "MemoryHandle [name=" + name + ", size=" + size + ']';
175 }
176 }
177
178 /**
179 * This exception is thrown if there is not enough memory for allocating the given object.
180 * @author Michael Zangl
181 */
182 public static class NotEnoughMemoryException extends Exception {
183 NotEnoughMemoryException(long memoryBytesRequired) {
184 super(tr("To add another layer you need to allocate at least {0,number,#}MB memory to JOSM using -Xmx{0,number,#}M "
185 + "option (see http://forum.openstreetmap.org/viewtopic.php?id=25677).\n"
186 + "Currently you have {1,number,#}MB memory allocated for JOSM",
187 memoryBytesRequired / 1024 / 1024, Runtime.getRuntime().maxMemory() / 1024 / 1024));
188 }
189 }
190}
Note: See TracBrowser for help on using the repository browser.