blob: 41fc2ac99b2e5a2cb005b0849537f358694d978d [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
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#include "Dalvik.h"
Barry Hayeseac47ed2009-06-22 11:45:20 -070018#include "alloc/clz.h"
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080019#include "alloc/HeapBitmap.h"
20#include "alloc/HeapInternal.h"
21#include "alloc/HeapSource.h"
22#include "alloc/MarkSweep.h"
23#include <limits.h> // for ULONG_MAX
24#include <sys/mman.h> // for madvise(), mmap()
25#include <cutils/ashmem.h>
The Android Open Source Project99409882009-03-18 22:20:24 -070026#include <errno.h>
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080027
28#define GC_DEBUG_PARANOID 2
29#define GC_DEBUG_BASIC 1
30#define GC_DEBUG_OFF 0
31#define GC_DEBUG(l) (GC_DEBUG_LEVEL >= (l))
32
33#if 1
34#define GC_DEBUG_LEVEL GC_DEBUG_PARANOID
35#else
36#define GC_DEBUG_LEVEL GC_DEBUG_OFF
37#endif
38
39#define VERBOSE_GC 0
40
41#define GC_LOG_TAG LOG_TAG "-gc"
42
43#if LOG_NDEBUG
44#define LOGV_GC(...) ((void)0)
45#define LOGD_GC(...) ((void)0)
46#else
47#define LOGV_GC(...) LOG(LOG_VERBOSE, GC_LOG_TAG, __VA_ARGS__)
48#define LOGD_GC(...) LOG(LOG_DEBUG, GC_LOG_TAG, __VA_ARGS__)
49#endif
50
51#if VERBOSE_GC
52#define LOGVV_GC(...) LOGV_GC(__VA_ARGS__)
53#else
54#define LOGVV_GC(...) ((void)0)
55#endif
56
57#define LOGI_GC(...) LOG(LOG_INFO, GC_LOG_TAG, __VA_ARGS__)
58#define LOGW_GC(...) LOG(LOG_WARN, GC_LOG_TAG, __VA_ARGS__)
59#define LOGE_GC(...) LOG(LOG_ERROR, GC_LOG_TAG, __VA_ARGS__)
60
61#define LOG_SCAN(...) LOGV_GC("SCAN: " __VA_ARGS__)
62#define LOG_MARK(...) LOGV_GC("MARK: " __VA_ARGS__)
63#define LOG_SWEEP(...) LOGV_GC("SWEEP: " __VA_ARGS__)
64#define LOG_REF(...) LOGV_GC("REF: " __VA_ARGS__)
65
66#define LOGV_SCAN(...) LOGVV_GC("SCAN: " __VA_ARGS__)
67#define LOGV_MARK(...) LOGVV_GC("MARK: " __VA_ARGS__)
68#define LOGV_SWEEP(...) LOGVV_GC("SWEEP: " __VA_ARGS__)
69#define LOGV_REF(...) LOGVV_GC("REF: " __VA_ARGS__)
70
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080071#define ALIGN_UP_TO_PAGE_SIZE(p) \
Andy McFadden96516932009-10-28 17:39:02 -070072 (((size_t)(p) + (SYSTEM_PAGE_SIZE - 1)) & ~(SYSTEM_PAGE_SIZE - 1))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080073
74/* Do not cast the result of this to a boolean; the only set bit
75 * may be > 1<<8.
76 */
Carl Shapiro6343bd02010-02-16 17:40:19 -080077static inline long isMarked(const void *obj, const GcMarkContext *ctx)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080078 __attribute__((always_inline));
Carl Shapiro6343bd02010-02-16 17:40:19 -080079static inline long isMarked(const void *obj, const GcMarkContext *ctx)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080080{
Carl Shapirof373efd2010-02-19 00:46:33 -080081 return dvmHeapBitmapIsObjectBitSet(ctx->bitmap, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080082}
83
84static bool
85createMarkStack(GcMarkStack *stack)
86{
87 const Object **limit;
88 size_t size;
The Android Open Source Project99409882009-03-18 22:20:24 -070089 int fd, err;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080090
91 /* Create a stack big enough for the worst possible case,
92 * where the heap is perfectly full of the smallest object.
93 * TODO: be better about memory usage; use a smaller stack with
94 * overflow detection and recovery.
95 */
96 size = dvmHeapSourceGetIdealFootprint() * sizeof(Object*) /
97 (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD);
98 size = ALIGN_UP_TO_PAGE_SIZE(size);
99 fd = ashmem_create_region("dalvik-heap-markstack", size);
100 if (fd < 0) {
The Android Open Source Project99409882009-03-18 22:20:24 -0700101 LOGE_GC("Could not create %d-byte ashmem mark stack: %s\n",
102 size, strerror(errno));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800103 return false;
104 }
105 limit = (const Object **)mmap(NULL, size, PROT_READ | PROT_WRITE,
106 MAP_PRIVATE, fd, 0);
The Android Open Source Project99409882009-03-18 22:20:24 -0700107 err = errno;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800108 close(fd);
109 if (limit == MAP_FAILED) {
The Android Open Source Project99409882009-03-18 22:20:24 -0700110 LOGE_GC("Could not mmap %d-byte ashmem mark stack: %s\n",
111 size, strerror(err));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800112 return false;
113 }
114
115 memset(stack, 0, sizeof(*stack));
116 stack->limit = limit;
117 stack->base = (const Object **)((uintptr_t)limit + size);
118 stack->top = stack->base;
119
120 return true;
121}
122
123static void
124destroyMarkStack(GcMarkStack *stack)
125{
126 munmap((char *)stack->limit,
127 (uintptr_t)stack->base - (uintptr_t)stack->limit);
128 memset(stack, 0, sizeof(*stack));
129}
130
131#define MARK_STACK_PUSH(stack, obj) \
132 do { \
133 *--(stack).top = (obj); \
134 } while (false)
135
136bool
Carl Shapirod25566d2010-03-11 20:39:47 -0800137dvmHeapBeginMarkStep(GcMode mode)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800138{
139 GcMarkContext *mc = &gDvm.gcHeap->markContext;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800140
141 if (!createMarkStack(&mc->stack)) {
142 return false;
143 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800144 mc->finger = NULL;
Carl Shapirod25566d2010-03-11 20:39:47 -0800145 mc->immuneLimit = dvmHeapSourceGetImmuneLimit(mode);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800146 return true;
147}
148
Carl Shapiro6343bd02010-02-16 17:40:19 -0800149static long setAndReturnMarkBit(GcMarkContext *ctx, const void *obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800150 __attribute__((always_inline));
151static long
Carl Shapiro6343bd02010-02-16 17:40:19 -0800152setAndReturnMarkBit(GcMarkContext *ctx, const void *obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800153{
Carl Shapirof373efd2010-02-19 00:46:33 -0800154 return dvmHeapBitmapSetAndReturnObjectBit(ctx->bitmap, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800155}
156
157static void _markObjectNonNullCommon(const Object *obj, GcMarkContext *ctx,
158 bool checkFinger, bool forceStack)
159 __attribute__((always_inline));
160static void
161_markObjectNonNullCommon(const Object *obj, GcMarkContext *ctx,
162 bool checkFinger, bool forceStack)
163{
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800164 assert(obj != NULL);
165
166#if GC_DEBUG(GC_DEBUG_PARANOID)
167//TODO: make sure we're locked
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800168 assert(dvmIsValidObject(obj));
169#endif
170
Carl Shapiroa0f1d132010-04-04 01:17:33 -0700171 if (obj < ctx->immuneLimit) {
Carl Shapirod25566d2010-03-11 20:39:47 -0800172 assert(isMarked(obj, ctx));
173 return;
174 }
Carl Shapiro6343bd02010-02-16 17:40:19 -0800175 if (!setAndReturnMarkBit(ctx, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800176 /* This object was not previously marked.
177 */
Carl Shapiro6343bd02010-02-16 17:40:19 -0800178 if (forceStack || (checkFinger && (void *)obj < ctx->finger)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800179 /* This object will need to go on the mark stack.
180 */
181 MARK_STACK_PUSH(ctx->stack, obj);
182 }
183
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800184#if WITH_HPROF
185 if (gDvm.gcHeap->hprofContext != NULL) {
186 hprofMarkRootObject(gDvm.gcHeap->hprofContext, obj, 0);
187 }
188#endif
189#if DVM_TRACK_HEAP_MARKING
190 gDvm.gcHeap->markCount++;
Carl Shapiro6343bd02010-02-16 17:40:19 -0800191 gDvm.gcHeap->markSize += dvmHeapSourceChunkSize((void *)obj) +
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800192 HEAP_SOURCE_CHUNK_OVERHEAD;
193#endif
194
195 /* obj->clazz can be NULL if we catch an object between
196 * dvmMalloc() and DVM_OBJECT_INIT(). This is ok.
197 */
198 LOGV_MARK("0x%08x %s\n", (uint)obj,
199 obj->clazz == NULL ? "<null class>" : obj->clazz->name);
200 }
201}
202
203/* Used to mark objects when recursing. Recursion is done by moving
204 * the finger across the bitmaps in address order and marking child
205 * objects. Any newly-marked objects whose addresses are lower than
206 * the finger won't be visited by the bitmap scan, so those objects
207 * need to be added to the mark stack.
208 */
209static void
210markObjectNonNull(const Object *obj, GcMarkContext *ctx)
211{
212 _markObjectNonNullCommon(obj, ctx, true, false);
213}
214
215#define markObject(obj, ctx) \
216 do { \
217 Object *MO_obj_ = (Object *)(obj); \
218 if (MO_obj_ != NULL) { \
219 markObjectNonNull(MO_obj_, (ctx)); \
220 } \
221 } while (false)
222
223/* If the object hasn't already been marked, mark it and
224 * schedule it to be scanned for references.
225 *
226 * obj may not be NULL. The macro dvmMarkObject() should
227 * be used in situations where a reference may be NULL.
228 *
229 * This function may only be called when marking the root
230 * set. When recursing, use the internal markObject[NonNull]().
231 */
232void
233dvmMarkObjectNonNull(const Object *obj)
234{
235 _markObjectNonNullCommon(obj, &gDvm.gcHeap->markContext, false, false);
236}
237
238/* Mark the set of root objects.
239 *
240 * Things we need to scan:
241 * - System classes defined by root classloader
242 * - For each thread:
243 * - Interpreted stack, from top to "curFrame"
244 * - Dalvik registers (args + local vars)
245 * - JNI local references
246 * - Automatic VM local references (TrackedAlloc)
247 * - Associated Thread/VMThread object
248 * - ThreadGroups (could track & start with these instead of working
249 * upward from Threads)
250 * - Exception currently being thrown, if present
251 * - JNI global references
252 * - Interned string table
253 * - Primitive classes
254 * - Special objects
255 * - gDvm.outOfMemoryObj
256 * - Objects allocated with ALLOC_NO_GC
257 * - Objects pending finalization (but not yet finalized)
258 * - Objects in debugger object registry
259 *
260 * Don't need:
261 * - Native stack (for in-progress stuff in the VM)
262 * - The TrackedAlloc stuff watches all native VM references.
263 */
264void dvmHeapMarkRootSet()
265{
266 HeapRefTable *refs;
267 GcHeap *gcHeap;
268 Object **op;
269
270 gcHeap = gDvm.gcHeap;
271
272 HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_STICKY_CLASS, 0);
273
Carl Shapirod25566d2010-03-11 20:39:47 -0800274 LOG_SCAN("immune objects");
Barry Hayes425848f2010-05-04 13:32:12 -0700275 dvmMarkImmuneObjects(gcHeap->markContext.immuneLimit);
Carl Shapirod25566d2010-03-11 20:39:47 -0800276
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800277 LOG_SCAN("root class loader\n");
278 dvmGcScanRootClassLoader();
279 LOG_SCAN("primitive classes\n");
280 dvmGcScanPrimitiveClasses();
281
282 /* dvmGcScanRootThreadGroups() sets a bunch of
283 * different scan states internally.
284 */
285 HPROF_CLEAR_GC_SCAN_STATE();
286
287 LOG_SCAN("root thread groups\n");
288 dvmGcScanRootThreadGroups();
289
290 HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_INTERNED_STRING, 0);
291
292 LOG_SCAN("interned strings\n");
293 dvmGcScanInternedStrings();
294
295 HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_GLOBAL, 0);
296
297 LOG_SCAN("JNI global refs\n");
298 dvmGcMarkJniGlobalRefs();
299
300 HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_REFERENCE_CLEANUP, 0);
301
302 LOG_SCAN("pending reference operations\n");
303 dvmHeapMarkLargeTableRefs(gcHeap->referenceOperations, true);
304
305 HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0);
306
307 LOG_SCAN("pending finalizations\n");
308 dvmHeapMarkLargeTableRefs(gcHeap->pendingFinalizationRefs, false);
309
310 HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_DEBUGGER, 0);
311
312 LOG_SCAN("debugger refs\n");
313 dvmGcMarkDebuggerRefs();
314
315 HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_VM_INTERNAL, 0);
316
317 /* Mark all ALLOC_NO_GC objects.
318 */
319 LOG_SCAN("ALLOC_NO_GC objects\n");
320 refs = &gcHeap->nonCollectableRefs;
321 op = refs->table;
322 while ((uintptr_t)op < (uintptr_t)refs->nextEntry) {
323 dvmMarkObjectNonNull(*(op++));
324 }
325
326 /* Mark any special objects we have sitting around.
327 */
328 LOG_SCAN("special objects\n");
329 dvmMarkObjectNonNull(gDvm.outOfMemoryObj);
330 dvmMarkObjectNonNull(gDvm.internalErrorObj);
Andy McFadden7fc3ce82009-07-14 15:57:23 -0700331 dvmMarkObjectNonNull(gDvm.noClassDefFoundErrorObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800332//TODO: scan object references sitting in gDvm; use pointer begin & end
333
334 HPROF_CLEAR_GC_SCAN_STATE();
335}
336
337/*
338 * Nothing past this point is allowed to use dvmMarkObject*().
339 * Scanning/recursion must use markObject*(), which takes the
340 * finger into account.
341 */
342#define dvmMarkObjectNonNull __dont_use_dvmMarkObjectNonNull__
343
344
345/* Mark all of a ClassObject's interfaces.
346 */
347static void markInterfaces(const ClassObject *clazz, GcMarkContext *ctx)
348{
349 ClassObject **interfaces;
350 int interfaceCount;
351 int i;
352
353 /* Mark all interfaces.
354 */
355 interfaces = clazz->interfaces;
356 interfaceCount = clazz->interfaceCount;
357 for (i = 0; i < interfaceCount; i++) {
358 markObjectNonNull((Object *)*interfaces, ctx);
359 interfaces++;
360 }
361}
362
363/* Mark all objects referred to by a ClassObject's static fields.
364 */
365static void scanStaticFields(const ClassObject *clazz, GcMarkContext *ctx)
366{
Andy McFadden929ebe22010-03-10 15:21:21 -0800367 const StaticField *f;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800368 int i;
369
370 //TODO: Optimize this with a bit vector or something
Barry Hayes03aa70a2010-03-01 15:49:41 -0800371 f = &clazz->sfields[0];
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800372 for (i = 0; i < clazz->sfieldCount; i++) {
373 char c = f->field.signature[0];
374 if (c == '[' || c == 'L') {
375 /* It's an array or class reference.
376 */
377 markObject((Object *)f->value.l, ctx);
378 }
379 f++;
380 }
381}
382
383/* Mark all objects referred to by a DataObject's instance fields.
384 */
385static void scanInstanceFields(const DataObject *obj, ClassObject *clazz,
386 GcMarkContext *ctx)
387{
Barry Hayesd1497482009-10-28 15:32:19 -0700388 if (clazz->refOffsets != CLASS_WALK_SUPER) {
Barry Hayeseac47ed2009-06-22 11:45:20 -0700389 unsigned int refOffsets = clazz->refOffsets;
390 while (refOffsets != 0) {
391 const int rshift = CLZ(refOffsets);
392 refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
393 markObject(dvmGetFieldObject((Object*)obj,
394 CLASS_OFFSET_FROM_CLZ(rshift)), ctx);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800395 }
Barry Hayeseac47ed2009-06-22 11:45:20 -0700396 } else {
397 while (clazz != NULL) {
398 InstField *f;
399 int i;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800400
Barry Hayeseac47ed2009-06-22 11:45:20 -0700401 /* All of the fields that contain object references
402 * are guaranteed to be at the beginning of the ifields list.
403 */
404 f = clazz->ifields;
405 for (i = 0; i < clazz->ifieldRefCount; i++) {
406 /* Mark the array or object reference.
407 * May be NULL.
408 *
409 * Note that, per the comment on struct InstField,
410 * f->byteOffset is the offset from the beginning of
411 * obj, not the offset into obj->instanceData.
412 */
413 markObject(dvmGetFieldObject((Object*)obj, f->byteOffset), ctx);
414 f++;
415 }
416
417 /* This will be NULL when we hit java.lang.Object
418 */
419 clazz = clazz->super;
420 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800421 }
422}
423
424/* Mark all objects referred to by the array's contents.
425 */
426static void scanObjectArray(const ArrayObject *array, GcMarkContext *ctx)
427{
428 Object **contents;
429 u4 length;
430 u4 i;
431
432 contents = (Object **)array->contents;
433 length = array->length;
434
435 for (i = 0; i < length; i++) {
436 markObject(*contents, ctx); // may be NULL
437 contents++;
438 }
439}
440
441/* Mark all objects referred to by the ClassObject.
442 */
443static void scanClassObject(const ClassObject *clazz, GcMarkContext *ctx)
444{
445 LOGV_SCAN("---------> %s\n", clazz->name);
446
447 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
448 /* We're an array; mark the class object of the contents
449 * of the array.
450 *
451 * Note that we won't necessarily reach the array's element
452 * class by scanning the array contents; the array may be
453 * zero-length, or may only contain null objects.
454 */
455 markObjectNonNull((Object *)clazz->elementClass, ctx);
456 }
457
458 /* We scan these explicitly in case the only remaining
459 * reference to a particular class object is via a data
460 * object; we may not be guaranteed to reach all
461 * live class objects via a classloader.
462 */
463 markObject((Object *)clazz->super, ctx); // may be NULL (java.lang.Object)
464 markObject(clazz->classLoader, ctx); // may be NULL
465
466 scanStaticFields(clazz, ctx);
467 markInterfaces(clazz, ctx);
468}
469
470/* Mark all objects that obj refers to.
471 *
472 * Called on every object in markList.
473 */
474static void scanObject(const Object *obj, GcMarkContext *ctx)
475{
476 ClassObject *clazz;
477
478 assert(dvmIsValidObject(obj));
Barry Hayes03aa70a2010-03-01 15:49:41 -0800479 LOGV_SCAN("0x%08x %s\n", (uint)obj, obj->clazz->descriptor);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800480
481#if WITH_HPROF
482 if (gDvm.gcHeap->hprofContext != NULL) {
483 hprofDumpHeapObject(gDvm.gcHeap->hprofContext, obj);
484 }
485#endif
486
487 /* Get and mark the class object for this particular instance.
488 */
489 clazz = obj->clazz;
490 if (clazz == NULL) {
Barry Hayesb36e6402010-03-05 09:51:12 -0800491 /* This can happen if we catch an object between dvmMalloc()
492 * and DVM_OBJECT_INIT(). The object won't contain any
493 * references yet, so we can just skip it. It can also mean
494 * that this object is unlinkedJavaLangClass, which has its
495 * clazz explicitly set to NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800496 */
497 return;
498 } else if (clazz == gDvm.unlinkedJavaLangClass) {
499 /* This class hasn't been linked yet. We're guaranteed
500 * that the object doesn't contain any references that
501 * aren't already tracked, so we can skip scanning it.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800502 */
503 return;
504 }
Barry Hayes3592d622009-03-16 16:10:35 -0700505
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800506 assert(dvmIsValidObject((Object *)clazz));
507 markObjectNonNull((Object *)clazz, ctx);
508
509 /* Mark any references in this object.
510 */
511 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
512 /* It's an array object.
513 */
514 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
515 /* It's an array of object references.
516 */
517 scanObjectArray((ArrayObject *)obj, ctx);
518 }
519 // else there's nothing else to scan
520 } else {
521 /* It's a DataObject-compatible object.
522 */
523 scanInstanceFields((DataObject *)obj, clazz, ctx);
524
525 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISREFERENCE)) {
526 GcHeap *gcHeap = gDvm.gcHeap;
527 Object *referent;
528
529 /* It's a subclass of java/lang/ref/Reference.
530 * The fields in this class have been arranged
531 * such that scanInstanceFields() did not actually
532 * mark the "referent" field; we need to handle
533 * it specially.
534 *
535 * If the referent already has a strong mark (isMarked(referent)),
536 * we don't care about its reference status.
537 */
538 referent = dvmGetFieldObject(obj,
539 gDvm.offJavaLangRefReference_referent);
Carl Shapiro29540742010-03-26 15:34:39 -0700540 if (referent != NULL && !isMarked(referent, ctx))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800541 {
542 u4 refFlags;
543
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800544 /* Find out what kind of reference is pointing
545 * to referent.
546 */
547 refFlags = GET_CLASS_FLAG_GROUP(clazz,
548 CLASS_ISREFERENCE |
549 CLASS_ISWEAKREFERENCE |
550 CLASS_ISPHANTOMREFERENCE);
551
552 /* We use the vmData field of Reference objects
553 * as a next pointer in a singly-linked list.
554 * That way, we don't need to allocate any memory
555 * while we're doing a GC.
556 */
557#define ADD_REF_TO_LIST(list, ref) \
558 do { \
559 Object *ARTL_ref_ = (/*de-const*/Object *)(ref); \
560 dvmSetFieldObject(ARTL_ref_, \
561 gDvm.offJavaLangRefReference_vmData, list); \
562 list = ARTL_ref_; \
563 } while (false)
564
565 /* At this stage, we just keep track of all of
566 * the live references that we've seen. Later,
567 * we'll walk through each of these lists and
568 * deal with the referents.
569 */
570 if (refFlags == CLASS_ISREFERENCE) {
571 /* It's a soft reference. Depending on the state,
572 * we'll attempt to collect all of them, some of
573 * them, or none of them.
574 */
Carl Shapiro29540742010-03-26 15:34:39 -0700575 ADD_REF_TO_LIST(gcHeap->softReferences, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800576 } else {
577 /* It's a weak or phantom reference.
578 * Clearing CLASS_ISREFERENCE will reveal which.
579 */
580 refFlags &= ~CLASS_ISREFERENCE;
581 if (refFlags == CLASS_ISWEAKREFERENCE) {
582 ADD_REF_TO_LIST(gcHeap->weakReferences, obj);
583 } else if (refFlags == CLASS_ISPHANTOMREFERENCE) {
584 ADD_REF_TO_LIST(gcHeap->phantomReferences, obj);
585 } else {
586 assert(!"Unknown reference type");
587 }
588 }
589#undef ADD_REF_TO_LIST
590 }
591 }
592
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800593 /* If this is a class object, mark various other things that
594 * its internals point to.
595 *
596 * All class objects are instances of java.lang.Class,
597 * including the java.lang.Class class object.
598 */
599 if (clazz == gDvm.classJavaLangClass) {
600 scanClassObject((ClassObject *)obj, ctx);
601 }
602 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800603}
604
605static void
606processMarkStack(GcMarkContext *ctx)
607{
608 const Object **const base = ctx->stack.base;
609
610 /* Scan anything that's on the mark stack.
611 * We can't use the bitmaps anymore, so use
612 * a finger that points past the end of them.
613 */
614 ctx->finger = (void *)ULONG_MAX;
615 while (ctx->stack.top != base) {
616 scanObject(*ctx->stack.top++, ctx);
617 }
618}
619
620#ifndef NDEBUG
621static uintptr_t gLastFinger = 0;
622#endif
623
624static bool
625scanBitmapCallback(size_t numPtrs, void **ptrs, const void *finger, void *arg)
626{
627 GcMarkContext *ctx = (GcMarkContext *)arg;
628 size_t i;
629
630#ifndef NDEBUG
631 assert((uintptr_t)finger >= gLastFinger);
632 gLastFinger = (uintptr_t)finger;
633#endif
634
635 ctx->finger = finger;
636 for (i = 0; i < numPtrs; i++) {
Carl Shapiro6343bd02010-02-16 17:40:19 -0800637 scanObject(*ptrs++, ctx);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800638 }
639
640 return true;
641}
642
643/* Given bitmaps with the root set marked, find and mark all
644 * reachable objects. When this returns, the entire set of
645 * live objects will be marked and the mark stack will be empty.
646 */
Carl Shapiro29540742010-03-26 15:34:39 -0700647void dvmHeapScanMarkedObjects(void)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800648{
649 GcMarkContext *ctx = &gDvm.gcHeap->markContext;
650
651 assert(ctx->finger == NULL);
652
653 /* The bitmaps currently have bits set for the root set.
654 * Walk across the bitmaps and scan each object.
655 */
656#ifndef NDEBUG
657 gLastFinger = 0;
658#endif
Carl Shapirof373efd2010-02-19 00:46:33 -0800659 dvmHeapBitmapWalk(ctx->bitmap, scanBitmapCallback, ctx);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800660
661 /* We've walked the mark bitmaps. Scan anything that's
662 * left on the mark stack.
663 */
664 processMarkStack(ctx);
665
666 LOG_SCAN("done with marked objects\n");
667}
668
Barry Hayes6930a112009-12-22 11:01:38 -0800669/** Clear the referent field.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800670 */
Barry Hayes6930a112009-12-22 11:01:38 -0800671static void clearReference(Object *reference)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800672{
673 /* This is what the default implementation of Reference.clear()
674 * does. We're required to clear all references to a given
675 * referent atomically, so we can't pop in and out of interp
676 * code each time.
677 *
Barry Hayes6930a112009-12-22 11:01:38 -0800678 * We don't ever actaully call overriding implementations of
679 * Reference.clear().
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800680 */
681 dvmSetFieldObject(reference,
682 gDvm.offJavaLangRefReference_referent, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800683}
684
Carl Shapiro29540742010-03-26 15:34:39 -0700685/*
686 * Returns true if the reference was registered with a reference queue
687 * and has not yet been enqueued.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800688 */
Carl Shapiro29540742010-03-26 15:34:39 -0700689static bool isEnqueuable(const Object *reference)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800690{
Barry Hayes6930a112009-12-22 11:01:38 -0800691 Object *queue = dvmGetFieldObject(reference,
692 gDvm.offJavaLangRefReference_queue);
693 Object *queueNext = dvmGetFieldObject(reference,
694 gDvm.offJavaLangRefReference_queueNext);
695 if (queue == NULL || queueNext != NULL) {
696 /* There is no queue, or the reference has already
697 * been enqueued. The Reference.enqueue() method
698 * will do nothing even if we call it.
699 */
700 return false;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800701 }
702
703 /* We need to call enqueue(), but if we called it from
704 * here we'd probably deadlock. Schedule a call.
705 */
706 return true;
707}
708
Carl Shapiro29540742010-03-26 15:34:39 -0700709/*
710 * Schedules a reference to be appended to its reference queue.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800711 */
Carl Shapiro29540742010-03-26 15:34:39 -0700712static void enqueueReference(Object *ref)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800713{
Carl Shapiro29540742010-03-26 15:34:39 -0700714 LargeHeapRefTable **table;
715 Object *op;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800716
Carl Shapiro29540742010-03-26 15:34:39 -0700717 assert(((uintptr_t)ref & 3) == 0);
718 assert((WORKER_ENQUEUE & ~3) == 0);
719 assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue) != NULL);
720 assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext) == NULL);
721 /* Stuff the enqueue bit in the bottom of the pointer.
722 * Assumes that objects are 8-byte aligned.
Andy McFaddenb18992f2009-09-25 10:42:15 -0700723 *
Carl Shapiro29540742010-03-26 15:34:39 -0700724 * Note that we are adding the *Reference* (which
725 * is by definition already marked at this point) to
726 * this list; we're not adding the referent (which
727 * has already been cleared).
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800728 */
Carl Shapiro29540742010-03-26 15:34:39 -0700729 table = &gDvm.gcHeap->referenceOperations;
730 op = (Object *)((uintptr_t)ref | WORKER_ENQUEUE);
731 if (!dvmHeapAddRefToLargeTable(table, op)) {
732 LOGE_HEAP("enqueueReference(): no room for any more "
733 "reference operations\n");
734 dvmAbort();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800735 }
736}
737
Carl Shapiro29540742010-03-26 15:34:39 -0700738/*
739 * Walks the reference list marking any references subject to the
740 * reference clearing policy. References with a black referent are
741 * removed from the list. References with white referents biased
742 * toward saving are blackened and also removed from the list.
743 */
744void dvmHandleSoftRefs(Object **list)
745{
746 GcMarkContext *markContext;
747 Object *ref, *referent;
748 Object *prev, *next;
749 size_t referentOffset, vmDataOffset;
750 unsigned counter;
751 bool marked;
752
753 markContext = &gDvm.gcHeap->markContext;
754 vmDataOffset = gDvm.offJavaLangRefReference_vmData;
755 referentOffset = gDvm.offJavaLangRefReference_referent;
756 counter = 0;
757 prev = next = NULL;
758 ref = *list;
759 while (ref != NULL) {
760 referent = dvmGetFieldObject(ref, referentOffset);
761 next = dvmGetFieldObject(ref, vmDataOffset);
762 assert(referent != NULL);
763 marked = isMarked(referent, markContext);
764 if (!marked && ((++counter) & 1)) {
765 /* Referent is white and biased toward saving, mark it. */
766 markObjectNonNull(referent, markContext);
767 marked = true;
768 }
769 if (marked) {
770 /* Referent is black, unlink it. */
771 if (prev != NULL) {
772 dvmSetFieldObject(ref, vmDataOffset, NULL);
773 dvmSetFieldObject(prev, vmDataOffset, next);
774 }
775 } else {
776 /* Referent is white, skip over it. */
777 prev = ref;
778 }
779 ref = next;
780 }
781 /*
782 * Restart the mark with the newly black references added to the
783 * root set.
784 */
785 processMarkStack(markContext);
786}
787
788/*
789 * Walks the reference list and clears references with an unmarked
790 * (white) referents. Cleared references registered to a reference
791 * queue are scheduled for appending by the heap worker thread.
792 */
793void dvmClearWhiteRefs(Object **list)
794{
795 GcMarkContext *markContext;
796 Object *ref, *referent;
797 size_t referentOffset, vmDataOffset;
798 bool doSignal;
799
800 markContext = &gDvm.gcHeap->markContext;
801 vmDataOffset = gDvm.offJavaLangRefReference_vmData;
802 referentOffset = gDvm.offJavaLangRefReference_referent;
803 doSignal = false;
804 while (*list != NULL) {
805 ref = *list;
806 referent = dvmGetFieldObject(ref, referentOffset);
807 *list = dvmGetFieldObject(ref, vmDataOffset);
808 assert(referent != NULL);
809 if (!isMarked(referent, markContext)) {
810 /* Referent is "white", clear it. */
811 clearReference(ref);
812 if (isEnqueuable(ref)) {
813 enqueueReference(ref);
814 doSignal = true;
815 }
816 }
817 }
818 /*
819 * If we cleared a reference with a reference queue we must notify
820 * the heap worker to append the reference.
821 */
822 if (doSignal) {
823 dvmSignalHeapWorker(false);
824 }
825 assert(*list == NULL);
826}
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800827
828/* Find unreachable objects that need to be finalized,
829 * and schedule them for finalization.
830 */
831void dvmHeapScheduleFinalizations()
832{
833 HeapRefTable newPendingRefs;
834 LargeHeapRefTable *finRefs = gDvm.gcHeap->finalizableRefs;
835 Object **ref;
836 Object **lastRef;
837 size_t totalPendCount;
838 GcMarkContext *markContext = &gDvm.gcHeap->markContext;
839
840 /*
841 * All reachable objects have been marked.
842 * Any unmarked finalizable objects need to be finalized.
843 */
844
845 /* Create a table that the new pending refs will
846 * be added to.
847 */
848 if (!dvmHeapInitHeapRefTable(&newPendingRefs, 128)) {
849 //TODO: mark all finalizable refs and hope that
850 // we can schedule them next time. Watch out,
851 // because we may be expecting to free up space
852 // by calling finalizers.
853 LOGE_GC("dvmHeapScheduleFinalizations(): no room for "
854 "pending finalizations\n");
855 dvmAbort();
856 }
857
858 /* Walk through finalizableRefs and move any unmarked references
859 * to the list of new pending refs.
860 */
861 totalPendCount = 0;
862 while (finRefs != NULL) {
863 Object **gapRef;
864 size_t newPendCount = 0;
865
866 gapRef = ref = finRefs->refs.table;
867 lastRef = finRefs->refs.nextEntry;
868 while (ref < lastRef) {
Carl Shapiro6343bd02010-02-16 17:40:19 -0800869 if (!isMarked(*ref, markContext)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800870 if (!dvmHeapAddToHeapRefTable(&newPendingRefs, *ref)) {
871 //TODO: add the current table and allocate
872 // a new, smaller one.
873 LOGE_GC("dvmHeapScheduleFinalizations(): "
874 "no room for any more pending finalizations: %zd\n",
875 dvmHeapNumHeapRefTableEntries(&newPendingRefs));
876 dvmAbort();
877 }
878 newPendCount++;
879 } else {
880 /* This ref is marked, so will remain on finalizableRefs.
881 */
882 if (newPendCount > 0) {
883 /* Copy it up to fill the holes.
884 */
885 *gapRef++ = *ref;
886 } else {
887 /* No holes yet; don't bother copying.
888 */
889 gapRef++;
890 }
891 }
892 ref++;
893 }
894 finRefs->refs.nextEntry = gapRef;
895 //TODO: if the table is empty when we're done, free it.
896 totalPendCount += newPendCount;
897 finRefs = finRefs->next;
898 }
899 LOGD_GC("dvmHeapScheduleFinalizations(): %zd finalizers triggered.\n",
900 totalPendCount);
901 if (totalPendCount == 0) {
902 /* No objects required finalization.
903 * Free the empty temporary table.
904 */
905 dvmClearReferenceTable(&newPendingRefs);
906 return;
907 }
908
909 /* Add the new pending refs to the main list.
910 */
911 if (!dvmHeapAddTableToLargeTable(&gDvm.gcHeap->pendingFinalizationRefs,
912 &newPendingRefs))
913 {
914 LOGE_GC("dvmHeapScheduleFinalizations(): can't insert new "
915 "pending finalizations\n");
916 dvmAbort();
917 }
918
919 //TODO: try compacting the main list with a memcpy loop
920
921 /* Mark the refs we just moved; we don't want them or their
922 * children to get swept yet.
923 */
924 ref = newPendingRefs.table;
925 lastRef = newPendingRefs.nextEntry;
926 assert(ref < lastRef);
927 HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0);
928 while (ref < lastRef) {
929 markObjectNonNull(*ref, markContext);
930 ref++;
931 }
932 HPROF_CLEAR_GC_SCAN_STATE();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800933 processMarkStack(markContext);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800934 dvmSignalHeapWorker(false);
935}
936
937void dvmHeapFinishMarkStep()
938{
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800939 GcMarkContext *markContext;
940
941 markContext = &gDvm.gcHeap->markContext;
942
943 /* The sweep step freed every object that appeared in the
944 * HeapSource bitmaps that didn't appear in the mark bitmaps.
945 * The new state of the HeapSource is exactly the final
946 * mark bitmaps, so swap them in.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800947 */
Carl Shapirof373efd2010-02-19 00:46:33 -0800948 dvmHeapSourceSwapBitmaps();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800949
Carl Shapirof373efd2010-02-19 00:46:33 -0800950 /* Clean up everything else associated with the marking process.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800951 */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800952 destroyMarkStack(&markContext->stack);
953
Carl Shapirof373efd2010-02-19 00:46:33 -0800954 markContext->finger = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800955}
956
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800957static bool
958sweepBitmapCallback(size_t numPtrs, void **ptrs, const void *finger, void *arg)
959{
960 const ClassObject *const classJavaLangClass = gDvm.classJavaLangClass;
Barry Hayes5cbb2302010-02-02 14:07:37 -0800961 const bool overwriteFree = gDvm.overwriteFree;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800962 size_t i;
Barry Hayesdde8ab02009-05-20 12:10:36 -0700963 void **origPtrs = ptrs;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800964
965 for (i = 0; i < numPtrs; i++) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800966 Object *obj;
967
Carl Shapiro6343bd02010-02-16 17:40:19 -0800968 obj = (Object *)*ptrs++;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800969
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800970 /* NOTE: Dereferencing clazz is dangerous. If obj was the last
971 * one to reference its class object, the class object could be
972 * on the sweep list, and could already have been swept, leaving
973 * us with a stale pointer.
974 */
975 LOGV_SWEEP("FREE: 0x%08x %s\n", (uint)obj, obj->clazz->name);
976
977 /* This assumes that java.lang.Class will never go away.
978 * If it can, and we were the last reference to it, it
979 * could have already been swept. However, even in that case,
980 * gDvm.classJavaLangClass should still have a useful
981 * value.
982 */
983 if (obj->clazz == classJavaLangClass) {
984 LOGV_SWEEP("---------------> %s\n", ((ClassObject *)obj)->name);
985 /* dvmFreeClassInnards() may have already been called,
986 * but it's safe to call on the same ClassObject twice.
987 */
988 dvmFreeClassInnards((ClassObject *)obj);
989 }
990
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800991 /* Overwrite the to-be-freed object to make stale references
992 * more obvious.
993 */
Barry Hayes5cbb2302010-02-02 14:07:37 -0800994 if (overwriteFree) {
Barry Hayes2e3c3e12010-02-22 09:39:10 -0800995 int objlen;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800996 ClassObject *clazz = obj->clazz;
Barry Hayes2e3c3e12010-02-22 09:39:10 -0800997 objlen = dvmHeapSourceChunkSize(obj);
998 memset(obj, 0xa5, objlen);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800999 obj->clazz = (ClassObject *)((uintptr_t)clazz ^ 0xffffffff);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001000 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001001 }
Barry Hayesdde8ab02009-05-20 12:10:36 -07001002 // TODO: dvmHeapSourceFreeList has a loop, just like the above
1003 // does. Consider collapsing the two loops to save overhead.
1004 dvmHeapSourceFreeList(numPtrs, origPtrs);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001005
1006 return true;
1007}
1008
Carl Shapiro5a6071b2010-01-07 21:35:50 -08001009/* Returns true if the given object is unmarked. Ignores the low bits
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001010 * of the pointer because the intern table may set them.
1011 */
1012static int isUnmarkedObject(void *object)
1013{
Carl Shapiro6343bd02010-02-16 17:40:19 -08001014 return !isMarked((void *)((uintptr_t)object & ~(HB_OBJECT_ALIGNMENT-1)),
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001015 &gDvm.gcHeap->markContext);
1016}
1017
1018/* Walk through the list of objects that haven't been
1019 * marked and free them.
1020 */
1021void
Carl Shapirod25566d2010-03-11 20:39:47 -08001022dvmHeapSweepUnmarkedObjects(GcMode mode, int *numFreed, size_t *sizeFreed)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001023{
Carl Shapirof373efd2010-02-19 00:46:33 -08001024 HeapBitmap markBits[HEAP_SOURCE_MAX_HEAP_COUNT];
Carl Shapirod77f7fd2010-04-05 19:23:31 -07001025 HeapBitmap liveBits[HEAP_SOURCE_MAX_HEAP_COUNT];
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001026 size_t origObjectsAllocated;
1027 size_t origBytesAllocated;
Carl Shapirod25566d2010-03-11 20:39:47 -08001028 size_t numBitmaps, numSweepBitmaps;
Barry Hayese168ebd2010-05-07 09:19:46 -07001029 size_t i;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001030
1031 /* All reachable objects have been marked.
1032 * Detach any unreachable interned strings before
1033 * we sweep.
1034 */
1035 dvmGcDetachDeadInternedStrings(isUnmarkedObject);
1036
1037 /* Free any known objects that are not marked.
1038 */
1039 origObjectsAllocated = dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED, NULL, 0);
1040 origBytesAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
1041
Carl Shapiro5a6071b2010-01-07 21:35:50 -08001042 dvmSweepMonitorList(&gDvm.monitorList, isUnmarkedObject);
1043
Carl Shapirof373efd2010-02-19 00:46:33 -08001044 numBitmaps = dvmHeapSourceGetNumHeaps();
Carl Shapirod77f7fd2010-04-05 19:23:31 -07001045 dvmHeapSourceGetObjectBitmaps(liveBits, markBits, numBitmaps);
Carl Shapirod25566d2010-03-11 20:39:47 -08001046 if (mode == GC_PARTIAL) {
1047 numSweepBitmaps = 1;
Carl Shapirod77f7fd2010-04-05 19:23:31 -07001048 assert((uintptr_t)gDvm.gcHeap->markContext.immuneLimit == liveBits[0].base);
Carl Shapirod25566d2010-03-11 20:39:47 -08001049 } else {
1050 numSweepBitmaps = numBitmaps;
1051 }
Barry Hayese168ebd2010-05-07 09:19:46 -07001052 for (i = 0; i < numSweepBitmaps; i++) {
1053 dvmHeapBitmapXorWalk(&markBits[i], &liveBits[i],
1054 sweepBitmapCallback, NULL);
1055 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001056
1057 *numFreed = origObjectsAllocated -
1058 dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED, NULL, 0);
1059 *sizeFreed = origBytesAllocated -
1060 dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
1061
1062#ifdef WITH_PROFILER
1063 if (gDvm.allocProf.enabled) {
1064 gDvm.allocProf.freeCount += *numFreed;
1065 gDvm.allocProf.freeSize += *sizeFreed;
1066 }
1067#endif
1068}