1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package libcore.util; 18 19 import dalvik.system.VMRuntime; 20 import sun.misc.Cleaner; 21 22 import java.lang.ref.Reference; 23 24 /** 25 * A NativeAllocationRegistry is used to associate native allocations with 26 * Java objects and register them with the runtime. 27 * There are two primary benefits of registering native allocations associated 28 * with Java objects: 29 * <ol> 30 * <li>The runtime will account for the native allocations when scheduling 31 * garbage collection to run.</li> 32 * <li>The runtime will arrange for the native allocation to be automatically 33 * freed by a user-supplied function when the associated Java object becomes 34 * unreachable.</li> 35 * </ol> 36 * A separate NativeAllocationRegistry should be instantiated for each kind 37 * of native allocation, where the kind of a native allocation consists of the 38 * native function used to free the allocation and the estimated size of the 39 * allocation. Once a NativeAllocationRegistry is instantiated, it can be 40 * used to register any number of native allocations of that kind. 41 * @hide 42 */ 43 @libcore.api.CorePlatformApi 44 @libcore.api.IntraCoreApi 45 public class NativeAllocationRegistry { 46 47 private final ClassLoader classLoader; 48 49 // Pointer to native deallocation function of type void f(void* freeFunction). 50 private final long freeFunction; 51 52 // The size of the registered native objects. This can be, and usually is, approximate. 53 // The least significant bit is one iff the object was allocated primarily with system 54 // malloc(). 55 // This field is examined by ahat and other tools. We chose this encoding of the "is_malloced" 56 // information to (a) allow existing readers to continue to work with minimal confusion, 57 // and (b) to avoid adding a field to NativeAllocationRegistry objects. 58 private final long size; 59 // Bit mask for "is_malloced" information. 60 private static final long IS_MALLOCED = 0x1; 61 62 /** 63 * Return a NativeAllocationRegistry for native memory that is mostly 64 * allocated by means other than the system memory allocator. For example, 65 * the memory may be allocated directly with mmap. 66 * @param classLoader ClassLoader that was used to load the native 67 * library defining freeFunction. 68 * This ensures that the the native library isn't unloaded 69 * before freeFunction is called. 70 * @param freeFunction address of a native function of type 71 * <code>void f(void* nativePtr)</code> used to free this 72 * kind of native allocation 73 * @param size estimated size in bytes of the part of the described 74 * native memory that is not allocated with system malloc. 75 * Approximate values are acceptable. 76 * @throws IllegalArgumentException If <code>size</code> is negative 77 */ 78 @libcore.api.CorePlatformApi createNonmalloced( ClassLoader classLoader, long freeFunction, long size)79 public static NativeAllocationRegistry createNonmalloced( 80 ClassLoader classLoader, long freeFunction, long size) { 81 return new NativeAllocationRegistry(classLoader, freeFunction, size, false); 82 } 83 84 /** 85 * Return a NativeAllocationRegistry for native memory that is mostly 86 * allocated by the system memory allocator. 87 * For example, the memory may be allocated directly with new or malloc. 88 * <p> 89 * The native function should have the type: 90 * <pre> 91 * void f(void* nativePtr); 92 * </pre> 93 * <p> 94 * @param classLoader ClassLoader that was used to load the native 95 * library freeFunction belongs to. 96 * @param freeFunction address of a native function of type 97 * <code>void f(void* nativePtr)</code> used to free this 98 * kind of native allocation 99 * @param size estimated size in bytes of the part of the described 100 * native memory allocated with system malloc. 101 * Approximate values are acceptable. For sizes less than 102 * a few hundered KB, use the simplified overload below. 103 * @throws IllegalArgumentException If <code>size</code> is negative 104 */ 105 @libcore.api.CorePlatformApi createMalloced( ClassLoader classLoader, long freeFunction, long size)106 public static NativeAllocationRegistry createMalloced( 107 ClassLoader classLoader, long freeFunction, long size) { 108 return new NativeAllocationRegistry(classLoader, freeFunction, size, true); 109 } 110 111 /** 112 * Return a NativeAllocationRegistry for native memory that is mostly 113 * allocated by the system memory allocator. This version is preferred 114 * for smaller objects (typically less than a few hundred KB). 115 * @param classLoader ClassLoader that was used to load the native 116 * library freeFunction belongs to. 117 * @param freeFunction address of a native function of type 118 * <code>void f(void* nativePtr)</code> used to free this 119 * kind of native allocation 120 */ 121 @libcore.api.CorePlatformApi 122 @libcore.api.IntraCoreApi createMalloced( ClassLoader classLoader, long freeFunction)123 public static NativeAllocationRegistry createMalloced( 124 ClassLoader classLoader, long freeFunction) { 125 return new NativeAllocationRegistry(classLoader, freeFunction, 0, true); 126 } 127 128 /** 129 * Constructs a NativeAllocationRegistry for a particular kind of native 130 * allocation. 131 * <p> 132 * The <code>size</code> should be an estimate of the total number of 133 * native bytes this kind of native allocation takes up. Different 134 * NativeAllocationRegistrys must be used to register native allocations 135 * with different estimated sizes, even if they use the same 136 * <code>freeFunction</code>. This is used to help inform the garbage 137 * collector about the possible need for collection. Memory allocated with 138 * native malloc is implicitly included, and ideally should not be included in this 139 * argument. 140 * <p> 141 * @param classLoader ClassLoader that was used to load the native 142 * library freeFunction belongs to. 143 * @param freeFunction address of a native function used to free this 144 * kind of native allocation 145 * @param size estimated size in bytes of this kind of native 146 * allocation. If mallocAllocation is false, then this 147 * should ideally exclude memory allocated by system 148 * malloc. However including it will simply double-count it, 149 * typically resulting in slightly increased GC frequency. 150 * If mallocAllocation is true, then this affects only the 151 * frequency with which we sample the malloc heap, and debugging 152 * tools. In this case a value of zero is commonly used to 153 * indicate an unknown non-huge size. 154 * @param mallocAllocation the native object is primarily allocated via malloc. 155 */ NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size, boolean mallocAllocation)156 private NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size, 157 boolean mallocAllocation) { 158 if (size < 0) { 159 throw new IllegalArgumentException("Invalid native allocation size: " + size); 160 } 161 this.classLoader = classLoader; 162 this.freeFunction = freeFunction; 163 this.size = mallocAllocation ? (size | IS_MALLOCED) : (size & ~IS_MALLOCED); 164 } 165 166 /** 167 * Constructs a NativeAllocationRegistry for a particular kind of native 168 * allocation. 169 * <p> 170 * New code should use the preceding factory methods rather than calling this 171 * constructor directly. 172 * <p> 173 * The <code>size</code> should be an estimate of the total number of 174 * native bytes this kind of native allocation takes up excluding bytes allocated 175 * with system malloc. Different 176 * NativeAllocationRegistrys must be used to register native allocations 177 * with different estimated sizes, even if they use the same 178 * <code>freeFunction</code>. This is used to help inform the garbage 179 * collector about the possible need for collection. Memory allocated with 180 * native malloc is implicitly included, and ideally should not be included in this 181 * argument. 182 * <p> 183 * @param classLoader ClassLoader that was used to load the native 184 * library freeFunction belongs to. 185 * @param freeFunction address of a native function used to free this 186 * kind of native allocation 187 * @param size estimated size in bytes of this kind of native 188 * allocation, excluding memory allocated with system malloc. 189 * A value of 0 indicates that the memory was allocated mainly 190 * with malloc. 191 * 192 * @param mallocAllocation the native object is primarily allocated via malloc. 193 */ 194 @libcore.api.CorePlatformApi NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size)195 public NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size) { 196 this(classLoader, freeFunction, size, size == 0); 197 } 198 199 /** 200 * Registers a new native allocation and associated Java object with the 201 * runtime. 202 * This NativeAllocationRegistry's <code>freeFunction</code> will 203 * automatically be called with <code>nativePtr</code> as its sole 204 * argument when <code>referent</code> becomes unreachable. If you 205 * maintain copies of <code>nativePtr</code> outside 206 * <code>referent</code>, you must not access these after 207 * <code>referent</code> becomes unreachable, because they may be dangling 208 * pointers. 209 * <p> 210 * The returned Runnable can be used to free the native allocation before 211 * <code>referent</code> becomes unreachable. The runnable will have no 212 * effect if the native allocation has already been freed by the runtime 213 * or by using the runnable. 214 * <p> 215 * WARNING: This unconditionally takes ownership, i.e. deallocation 216 * responsibility of nativePtr. nativePtr will be DEALLOCATED IMMEDIATELY 217 * if the registration attempt throws an exception (other than one reporting 218 * a programming error). 219 * 220 * @param referent Non-null java object to associate the native allocation with 221 * @param nativePtr Non-zero address of the native allocation 222 * @return runnable to explicitly free native allocation 223 * @throws IllegalArgumentException if either referent or nativePtr is null. 224 * @throws OutOfMemoryError if there is not enough space on the Java heap 225 * in which to register the allocation. In this 226 * case, <code>freeFunction</code> will be 227 * called with <code>nativePtr</code> as its 228 * argument before the OutOfMemoryError is 229 * thrown. 230 */ 231 @libcore.api.CorePlatformApi 232 @libcore.api.IntraCoreApi registerNativeAllocation(Object referent, long nativePtr)233 public Runnable registerNativeAllocation(Object referent, long nativePtr) { 234 if (referent == null) { 235 throw new IllegalArgumentException("referent is null"); 236 } 237 if (nativePtr == 0) { 238 throw new IllegalArgumentException("nativePtr is null"); 239 } 240 241 CleanerThunk thunk; 242 CleanerRunner result; 243 try { 244 thunk = new CleanerThunk(); 245 Cleaner cleaner = Cleaner.create(referent, thunk); 246 result = new CleanerRunner(cleaner); 247 registerNativeAllocation(this.size); 248 } catch (VirtualMachineError vme /* probably OutOfMemoryError */) { 249 applyFreeFunction(freeFunction, nativePtr); 250 throw vme; 251 } // Other exceptions are impossible. 252 // Enable the cleaner only after we can no longer throw anything, including OOME. 253 thunk.setNativePtr(nativePtr); 254 // Ensure that cleaner doesn't get invoked before we enable it. 255 Reference.reachabilityFence(referent); 256 return result; 257 } 258 259 private class CleanerThunk implements Runnable { 260 private long nativePtr; 261 CleanerThunk()262 public CleanerThunk() { 263 this.nativePtr = 0; 264 } 265 run()266 public void run() { 267 if (nativePtr != 0) { 268 applyFreeFunction(freeFunction, nativePtr); 269 registerNativeFree(size); 270 } 271 } 272 setNativePtr(long nativePtr)273 public void setNativePtr(long nativePtr) { 274 this.nativePtr = nativePtr; 275 } 276 } 277 278 private static class CleanerRunner implements Runnable { 279 private final Cleaner cleaner; 280 CleanerRunner(Cleaner cleaner)281 public CleanerRunner(Cleaner cleaner) { 282 this.cleaner = cleaner; 283 } 284 run()285 public void run() { 286 cleaner.clean(); 287 } 288 } 289 290 // Inform the garbage collector of the allocation. We do this differently for 291 // malloc-based allocations. registerNativeAllocation(long size)292 private static void registerNativeAllocation(long size) { 293 VMRuntime runtime = VMRuntime.getRuntime(); 294 if ((size & IS_MALLOCED) != 0) { 295 final long notifyImmediateThreshold = 300000; 296 if (size >= notifyImmediateThreshold) { 297 runtime.notifyNativeAllocationsInternal(); 298 } else { 299 runtime.notifyNativeAllocation(); 300 } 301 } else { 302 runtime.registerNativeAllocation(size); 303 } 304 } 305 306 // Inform the garbage collector of deallocation, if appropriate. registerNativeFree(long size)307 private static void registerNativeFree(long size) { 308 if ((size & IS_MALLOCED) == 0) { 309 VMRuntime.getRuntime().registerNativeFree(size); 310 } 311 } 312 313 /** 314 * Calls <code>freeFunction</code>(<code>nativePtr</code>). 315 * Provided as a convenience in the case where you wish to manually free a 316 * native allocation using a <code>freeFunction</code> without using a 317 * NativeAllocationRegistry. 318 */ 319 @libcore.api.CorePlatformApi applyFreeFunction(long freeFunction, long nativePtr)320 public static native void applyFreeFunction(long freeFunction, long nativePtr); 321 } 322 323