Alloc.cpp revision 60fc806b679a3655c228b4093058c59941a49cfe
1/* 2 * Copyright (C) 2008 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 * Garbage-collecting memory allocator. 18 */ 19#include "Dalvik.h" 20#include "alloc/Heap.h" 21#include "alloc/HeapInternal.h" 22#include "alloc/HeapSource.h" 23 24/* 25 * Initialize the GC universe. 26 * 27 * We're currently using a memory-mapped arena to keep things off of the 28 * main heap. This needs to be replaced with something real. 29 */ 30bool dvmGcStartup() 31{ 32 dvmInitMutex(&gDvm.gcHeapLock); 33 pthread_cond_init(&gDvm.gcHeapCond, NULL); 34 return dvmHeapStartup(); 35} 36 37/* 38 * Post-zygote heap initialization, including starting 39 * the HeapWorker thread. 40 */ 41bool dvmGcStartupAfterZygote() 42{ 43 return dvmHeapStartupAfterZygote(); 44} 45 46/* 47 * Shutdown the threads internal to the garbage collector. 48 */ 49void dvmGcThreadShutdown() 50{ 51 dvmHeapThreadShutdown(); 52} 53 54/* 55 * Shut the GC down. 56 */ 57void dvmGcShutdown() 58{ 59 //TODO: grab and destroy the lock 60 dvmHeapShutdown(); 61} 62 63/* 64 * Do any last-minute preparation before we call fork() for the first time. 65 */ 66bool dvmGcPreZygoteFork() 67{ 68 return dvmHeapSourceStartupBeforeFork(); 69} 70 71static bool startGcClass(const char* klassName, const char* methodName) 72{ 73 ClassObject *klass = dvmFindSystemClass(klassName); 74 if (klass == NULL) { 75 return false; 76 } 77 Method *method = dvmFindDirectMethodByDescriptor(klass, methodName, "()V"); 78 if (method == NULL) { 79 return false; 80 } 81 Thread *self = dvmThreadSelf(); 82 assert(self != NULL); 83 JValue unusedResult; 84 dvmCallMethod(self, method, NULL, &unusedResult); 85 return true; 86} 87 88bool dvmGcStartupClasses() 89{ 90 bool success = 91 startGcClass("Ljava/lang/ref/ReferenceQueueThread;", 92 "startReferenceQueue") && 93 startGcClass("Ljava/lang/FinalizerThread;", 94 "startFinalizer"); 95 return success; 96} 97 98/* 99 * Create a "stock instance" of an exception class. 100 */ 101static Object* createStockException(const char* descriptor, const char* msg) 102{ 103 Thread* self = dvmThreadSelf(); 104 StringObject* msgStr = NULL; 105 ClassObject* clazz; 106 Method* init; 107 Object* obj; 108 109 /* find class, initialize if necessary */ 110 clazz = dvmFindSystemClass(descriptor); 111 if (clazz == NULL) { 112 LOGE("Unable to find %s", descriptor); 113 return NULL; 114 } 115 116 init = dvmFindDirectMethodByDescriptor(clazz, "<init>", 117 "(Ljava/lang/String;)V"); 118 if (init == NULL) { 119 LOGE("Unable to find String-arg constructor for %s", descriptor); 120 return NULL; 121 } 122 123 obj = dvmAllocObject(clazz, ALLOC_DEFAULT); 124 if (obj == NULL) 125 return NULL; 126 127 if (msg == NULL) { 128 msgStr = NULL; 129 } else { 130 msgStr = dvmCreateStringFromCstr(msg); 131 if (msgStr == NULL) { 132 LOGW("Could not allocate message string \"%s\"", msg); 133 dvmReleaseTrackedAlloc(obj, self); 134 return NULL; 135 } 136 } 137 138 JValue unused; 139 dvmCallMethod(self, init, obj, &unused, msgStr); 140 if (dvmCheckException(self)) { 141 dvmReleaseTrackedAlloc((Object*) msgStr, self); 142 dvmReleaseTrackedAlloc(obj, self); 143 return NULL; 144 } 145 146 dvmReleaseTrackedAlloc((Object*) msgStr, self); // okay if msgStr NULL 147 return obj; 148} 149 150/* 151 * Create some "stock" exceptions. These can be thrown when the system is 152 * too screwed up to allocate and initialize anything, or when we don't 153 * need a meaningful stack trace. 154 * 155 * We can't do this during the initial startup because we need to execute 156 * the constructors. 157 */ 158bool dvmCreateStockExceptions() 159{ 160 /* 161 * Pre-allocate some throwables. These need to be explicitly added 162 * to the GC's root set (see dvmHeapMarkRootSet()). 163 */ 164 gDvm.outOfMemoryObj = createStockException("Ljava/lang/OutOfMemoryError;", 165 "[memory exhausted]"); 166 dvmReleaseTrackedAlloc(gDvm.outOfMemoryObj, NULL); 167 gDvm.internalErrorObj = createStockException("Ljava/lang/InternalError;", 168 "[pre-allocated]"); 169 dvmReleaseTrackedAlloc(gDvm.internalErrorObj, NULL); 170 gDvm.noClassDefFoundErrorObj = 171 createStockException("Ljava/lang/NoClassDefFoundError;", 172 "[generic]"); 173 dvmReleaseTrackedAlloc(gDvm.noClassDefFoundErrorObj, NULL); 174 175 if (gDvm.outOfMemoryObj == NULL || gDvm.internalErrorObj == NULL || 176 gDvm.noClassDefFoundErrorObj == NULL) 177 { 178 LOGW("Unable to create stock exceptions"); 179 return false; 180 } 181 182 return true; 183} 184 185 186/* 187 * Create an instance of the specified class. 188 * 189 * Returns NULL and throws an exception on failure. 190 */ 191Object* dvmAllocObject(ClassObject* clazz, int flags) 192{ 193 Object* newObj; 194 195 assert(clazz != NULL); 196 assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz)); 197 198 /* allocate on GC heap; memory is zeroed out */ 199 newObj = (Object*)dvmMalloc(clazz->objectSize, flags); 200 if (newObj != NULL) { 201 DVM_OBJECT_INIT(newObj, clazz); 202 dvmTrackAllocation(clazz, clazz->objectSize); /* notify DDMS */ 203 } 204 205 return newObj; 206} 207 208/* 209 * Create a copy of an object, for Object.clone(). 210 * 211 * We use the size actually allocated, rather than obj->clazz->objectSize, 212 * because the latter doesn't work for array objects. 213 */ 214Object* dvmCloneObject(Object* obj, int flags) 215{ 216 assert(dvmIsValidObject(obj)); 217 ClassObject* clazz = obj->clazz; 218 219 /* Class.java shouldn't let us get here (java.lang.Class is final 220 * and does not implement Clonable), but make extra sure. 221 * A memcpy() clone will wreak havoc on a ClassObject's "innards". 222 */ 223 assert(!dvmIsTheClassClass(clazz)); 224 225 size_t size; 226 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) { 227 size = dvmArrayObjectSize((ArrayObject *)obj); 228 } else { 229 size = clazz->objectSize; 230 } 231 232 Object* copy = (Object*)dvmMalloc(size, flags); 233 if (copy == NULL) 234 return NULL; 235 236 DVM_OBJECT_INIT(copy, clazz); 237 size_t offset = sizeof(Object); 238 /* Copy instance data. We assume memcpy copies by words. */ 239 memcpy((char*)copy + offset, (char*)obj + offset, size - offset); 240 241 /* Mark the clone as finalizable if appropriate. */ 242 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE)) { 243 dvmSetFinalizable(copy); 244 } 245 246 dvmTrackAllocation(clazz, size); /* notify DDMS */ 247 248 return copy; 249} 250 251 252/* 253 * Track an object that was allocated internally and isn't yet part of the 254 * VM root set. 255 * 256 * We could do this per-thread or globally. If it's global we don't have 257 * to do the thread lookup but we do have to synchronize access to the list. 258 * 259 * "obj" must not be NULL. 260 * 261 * NOTE: "obj" is not a fully-formed object; in particular, obj->clazz will 262 * usually be NULL since we're being called from dvmMalloc(). 263 */ 264void dvmAddTrackedAlloc(Object* obj, Thread* self) 265{ 266 if (self == NULL) 267 self = dvmThreadSelf(); 268 269 assert(obj != NULL); 270 assert(self != NULL); 271 if (!dvmAddToReferenceTable(&self->internalLocalRefTable, obj)) { 272 LOGE("threadid=%d: unable to add %p to internal ref table", 273 self->threadId, obj); 274 dvmDumpThread(self, false); 275 dvmAbort(); 276 } 277} 278 279/* 280 * Stop tracking an object. 281 * 282 * We allow attempts to delete NULL "obj" so that callers don't have to wrap 283 * calls with "if != NULL". 284 */ 285void dvmReleaseTrackedAlloc(Object* obj, Thread* self) 286{ 287 if (obj == NULL) 288 return; 289 290 if (self == NULL) 291 self = dvmThreadSelf(); 292 assert(self != NULL); 293 294 if (!dvmRemoveFromReferenceTable(&self->internalLocalRefTable, 295 self->internalLocalRefTable.table, obj)) 296 { 297 LOGE("threadid=%d: failed to remove %p from internal ref table", 298 self->threadId, obj); 299 dvmAbort(); 300 } 301} 302 303 304/* 305 * Explicitly initiate garbage collection. 306 */ 307void dvmCollectGarbage() 308{ 309 if (gDvm.disableExplicitGc) { 310 return; 311 } 312 dvmLockHeap(); 313 dvmWaitForConcurrentGcToComplete(); 314 dvmCollectGarbageInternal(GC_EXPLICIT); 315 dvmUnlockHeap(); 316} 317 318struct CountContext { 319 const ClassObject *clazz; 320 size_t count; 321}; 322 323static void countInstancesOfClassCallback(void *ptr, void *arg) 324{ 325 CountContext *ctx = (CountContext *)arg; 326 const Object *obj = (const Object *)ptr; 327 328 assert(ctx != NULL); 329 if (obj->clazz == ctx->clazz) { 330 ctx->count += 1; 331 } 332} 333 334size_t dvmCountInstancesOfClass(const ClassObject *clazz) 335{ 336 CountContext ctx = { clazz, 0 }; 337 dvmLockHeap(); 338 HeapBitmap *bitmap = dvmHeapSourceGetLiveBits(); 339 dvmHeapBitmapWalk(bitmap, countInstancesOfClassCallback, &ctx); 340 dvmUnlockHeap(); 341 return ctx.count; 342} 343 344static void countAssignableInstancesOfClassCallback(void *ptr, void *arg) 345{ 346 CountContext *ctx = (CountContext *)arg; 347 const Object *obj = (const Object *)ptr; 348 349 assert(ctx != NULL); 350 if (obj->clazz != NULL && dvmInstanceof(obj->clazz, ctx->clazz)) { 351 ctx->count += 1; 352 } 353} 354 355size_t dvmCountAssignableInstancesOfClass(const ClassObject *clazz) 356{ 357 CountContext ctx = { clazz, 0 }; 358 dvmLockHeap(); 359 HeapBitmap *bitmap = dvmHeapSourceGetLiveBits(); 360 dvmHeapBitmapWalk(bitmap, countAssignableInstancesOfClassCallback, &ctx); 361 dvmUnlockHeap(); 362 return ctx.count; 363} 364 365bool dvmIsHeapAddress(void *address) 366{ 367 return dvmHeapSourceContainsAddress(address); 368} 369 370bool dvmIsNonMovingObject(const Object* object) 371{ 372 return true; 373} 374