blob: b7f83b34b5c4112cafb7314854edf10f87f1fb73 [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
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080028#define GC_LOG_TAG LOG_TAG "-gc"
29
30#if LOG_NDEBUG
31#define LOGV_GC(...) ((void)0)
32#define LOGD_GC(...) ((void)0)
33#else
34#define LOGV_GC(...) LOG(LOG_VERBOSE, GC_LOG_TAG, __VA_ARGS__)
35#define LOGD_GC(...) LOG(LOG_DEBUG, GC_LOG_TAG, __VA_ARGS__)
36#endif
37
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080038#define LOGI_GC(...) LOG(LOG_INFO, GC_LOG_TAG, __VA_ARGS__)
39#define LOGW_GC(...) LOG(LOG_WARN, GC_LOG_TAG, __VA_ARGS__)
40#define LOGE_GC(...) LOG(LOG_ERROR, GC_LOG_TAG, __VA_ARGS__)
41
42#define LOG_SCAN(...) LOGV_GC("SCAN: " __VA_ARGS__)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080043
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080044#define ALIGN_UP_TO_PAGE_SIZE(p) \
Andy McFadden96516932009-10-28 17:39:02 -070045 (((size_t)(p) + (SYSTEM_PAGE_SIZE - 1)) & ~(SYSTEM_PAGE_SIZE - 1))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080046
47/* Do not cast the result of this to a boolean; the only set bit
48 * may be > 1<<8.
49 */
Carl Shapiro6343bd02010-02-16 17:40:19 -080050static inline long isMarked(const void *obj, const GcMarkContext *ctx)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080051{
Carl Shapirof373efd2010-02-19 00:46:33 -080052 return dvmHeapBitmapIsObjectBitSet(ctx->bitmap, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080053}
54
55static bool
56createMarkStack(GcMarkStack *stack)
57{
58 const Object **limit;
59 size_t size;
The Android Open Source Project99409882009-03-18 22:20:24 -070060 int fd, err;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080061
62 /* Create a stack big enough for the worst possible case,
63 * where the heap is perfectly full of the smallest object.
64 * TODO: be better about memory usage; use a smaller stack with
65 * overflow detection and recovery.
66 */
67 size = dvmHeapSourceGetIdealFootprint() * sizeof(Object*) /
68 (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD);
69 size = ALIGN_UP_TO_PAGE_SIZE(size);
70 fd = ashmem_create_region("dalvik-heap-markstack", size);
71 if (fd < 0) {
The Android Open Source Project99409882009-03-18 22:20:24 -070072 LOGE_GC("Could not create %d-byte ashmem mark stack: %s\n",
73 size, strerror(errno));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080074 return false;
75 }
76 limit = (const Object **)mmap(NULL, size, PROT_READ | PROT_WRITE,
77 MAP_PRIVATE, fd, 0);
The Android Open Source Project99409882009-03-18 22:20:24 -070078 err = errno;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080079 close(fd);
80 if (limit == MAP_FAILED) {
The Android Open Source Project99409882009-03-18 22:20:24 -070081 LOGE_GC("Could not mmap %d-byte ashmem mark stack: %s\n",
82 size, strerror(err));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080083 return false;
84 }
85
86 memset(stack, 0, sizeof(*stack));
87 stack->limit = limit;
88 stack->base = (const Object **)((uintptr_t)limit + size);
89 stack->top = stack->base;
90
91 return true;
92}
93
94static void
95destroyMarkStack(GcMarkStack *stack)
96{
97 munmap((char *)stack->limit,
98 (uintptr_t)stack->base - (uintptr_t)stack->limit);
99 memset(stack, 0, sizeof(*stack));
100}
101
102#define MARK_STACK_PUSH(stack, obj) \
103 do { \
104 *--(stack).top = (obj); \
105 } while (false)
106
107bool
Carl Shapirod25566d2010-03-11 20:39:47 -0800108dvmHeapBeginMarkStep(GcMode mode)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800109{
110 GcMarkContext *mc = &gDvm.gcHeap->markContext;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800111
112 if (!createMarkStack(&mc->stack)) {
113 return false;
114 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800115 mc->finger = NULL;
Carl Shapirod25566d2010-03-11 20:39:47 -0800116 mc->immuneLimit = dvmHeapSourceGetImmuneLimit(mode);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800117 return true;
118}
119
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800120static long
Carl Shapiro6343bd02010-02-16 17:40:19 -0800121setAndReturnMarkBit(GcMarkContext *ctx, const void *obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800122{
Carl Shapirof373efd2010-02-19 00:46:33 -0800123 return dvmHeapBitmapSetAndReturnObjectBit(ctx->bitmap, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800124}
125
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800126static void
Barry Hayese1bccb92010-05-18 09:48:37 -0700127markObjectNonNull(const Object *obj, GcMarkContext *ctx,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800128 bool checkFinger, bool forceStack)
129{
Barry Hayese1bccb92010-05-18 09:48:37 -0700130 assert(ctx != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800131 assert(obj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800132 assert(dvmIsValidObject(obj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800133
Carl Shapirob31b3012010-05-25 18:35:37 -0700134 if (obj < (Object *)ctx->immuneLimit) {
Carl Shapirod25566d2010-03-11 20:39:47 -0800135 assert(isMarked(obj, ctx));
136 return;
137 }
Carl Shapiro6343bd02010-02-16 17:40:19 -0800138 if (!setAndReturnMarkBit(ctx, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800139 /* This object was not previously marked.
140 */
Carl Shapiro6343bd02010-02-16 17:40:19 -0800141 if (forceStack || (checkFinger && (void *)obj < ctx->finger)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800142 /* This object will need to go on the mark stack.
143 */
144 MARK_STACK_PUSH(ctx->stack, obj);
145 }
146
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800147#if WITH_HPROF
148 if (gDvm.gcHeap->hprofContext != NULL) {
149 hprofMarkRootObject(gDvm.gcHeap->hprofContext, obj, 0);
150 }
151#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800152 }
153}
154
155/* Used to mark objects when recursing. Recursion is done by moving
156 * the finger across the bitmaps in address order and marking child
157 * objects. Any newly-marked objects whose addresses are lower than
158 * the finger won't be visited by the bitmap scan, so those objects
159 * need to be added to the mark stack.
160 */
Barry Hayese1bccb92010-05-18 09:48:37 -0700161static void markObject(const Object *obj, GcMarkContext *ctx)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800162{
Barry Hayese1bccb92010-05-18 09:48:37 -0700163 if (obj != NULL) {
164 markObjectNonNull(obj, ctx, true, false);
165 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800166}
167
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800168/* If the object hasn't already been marked, mark it and
169 * schedule it to be scanned for references.
170 *
171 * obj may not be NULL. The macro dvmMarkObject() should
172 * be used in situations where a reference may be NULL.
173 *
174 * This function may only be called when marking the root
Barry Hayese1bccb92010-05-18 09:48:37 -0700175 * set. When recursing, use the internal markObject().
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800176 */
177void
178dvmMarkObjectNonNull(const Object *obj)
179{
Barry Hayese1bccb92010-05-18 09:48:37 -0700180 assert(obj != NULL);
181 markObjectNonNull(obj, &gDvm.gcHeap->markContext, false, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800182}
183
184/* Mark the set of root objects.
185 *
186 * Things we need to scan:
187 * - System classes defined by root classloader
188 * - For each thread:
189 * - Interpreted stack, from top to "curFrame"
190 * - Dalvik registers (args + local vars)
191 * - JNI local references
192 * - Automatic VM local references (TrackedAlloc)
193 * - Associated Thread/VMThread object
194 * - ThreadGroups (could track & start with these instead of working
195 * upward from Threads)
196 * - Exception currently being thrown, if present
197 * - JNI global references
198 * - Interned string table
199 * - Primitive classes
200 * - Special objects
201 * - gDvm.outOfMemoryObj
202 * - Objects allocated with ALLOC_NO_GC
203 * - Objects pending finalization (but not yet finalized)
204 * - Objects in debugger object registry
205 *
206 * Don't need:
207 * - Native stack (for in-progress stuff in the VM)
208 * - The TrackedAlloc stuff watches all native VM references.
209 */
210void dvmHeapMarkRootSet()
211{
212 HeapRefTable *refs;
213 GcHeap *gcHeap;
214 Object **op;
215
216 gcHeap = gDvm.gcHeap;
217
218 HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_STICKY_CLASS, 0);
219
Carl Shapirod25566d2010-03-11 20:39:47 -0800220 LOG_SCAN("immune objects");
Barry Hayes425848f2010-05-04 13:32:12 -0700221 dvmMarkImmuneObjects(gcHeap->markContext.immuneLimit);
Carl Shapirod25566d2010-03-11 20:39:47 -0800222
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800223 LOG_SCAN("root class loader\n");
224 dvmGcScanRootClassLoader();
225 LOG_SCAN("primitive classes\n");
226 dvmGcScanPrimitiveClasses();
227
228 /* dvmGcScanRootThreadGroups() sets a bunch of
229 * different scan states internally.
230 */
231 HPROF_CLEAR_GC_SCAN_STATE();
232
233 LOG_SCAN("root thread groups\n");
234 dvmGcScanRootThreadGroups();
235
236 HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_INTERNED_STRING, 0);
237
238 LOG_SCAN("interned strings\n");
239 dvmGcScanInternedStrings();
240
241 HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_GLOBAL, 0);
242
243 LOG_SCAN("JNI global refs\n");
244 dvmGcMarkJniGlobalRefs();
245
246 HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_REFERENCE_CLEANUP, 0);
247
248 LOG_SCAN("pending reference operations\n");
249 dvmHeapMarkLargeTableRefs(gcHeap->referenceOperations, true);
250
251 HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0);
252
253 LOG_SCAN("pending finalizations\n");
254 dvmHeapMarkLargeTableRefs(gcHeap->pendingFinalizationRefs, false);
255
256 HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_DEBUGGER, 0);
257
258 LOG_SCAN("debugger refs\n");
259 dvmGcMarkDebuggerRefs();
260
261 HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_VM_INTERNAL, 0);
262
263 /* Mark all ALLOC_NO_GC objects.
264 */
265 LOG_SCAN("ALLOC_NO_GC objects\n");
266 refs = &gcHeap->nonCollectableRefs;
267 op = refs->table;
268 while ((uintptr_t)op < (uintptr_t)refs->nextEntry) {
269 dvmMarkObjectNonNull(*(op++));
270 }
271
272 /* Mark any special objects we have sitting around.
273 */
274 LOG_SCAN("special objects\n");
275 dvmMarkObjectNonNull(gDvm.outOfMemoryObj);
276 dvmMarkObjectNonNull(gDvm.internalErrorObj);
Andy McFadden7fc3ce82009-07-14 15:57:23 -0700277 dvmMarkObjectNonNull(gDvm.noClassDefFoundErrorObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800278//TODO: scan object references sitting in gDvm; use pointer begin & end
279
280 HPROF_CLEAR_GC_SCAN_STATE();
281}
282
283/*
Barry Hayese1bccb92010-05-18 09:48:37 -0700284 * Nothing past this point is allowed to use dvmMarkObject() or
285 * dvmMarkObjectNonNull(), which are for root-marking only.
286 * Scanning/recursion must use markObject(), which takes the finger
287 * into account.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800288 */
Barry Hayese1bccb92010-05-18 09:48:37 -0700289#undef dvmMarkObject
290#define dvmMarkObject __dont_use_dvmMarkObject__
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800291#define dvmMarkObjectNonNull __dont_use_dvmMarkObjectNonNull__
292
Barry Hayese1bccb92010-05-18 09:48:37 -0700293/*
294 * Scans instance fields.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800295 */
Barry Hayese1bccb92010-05-18 09:48:37 -0700296static void scanInstanceFields(const Object *obj, GcMarkContext *ctx)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800297{
Barry Hayese1bccb92010-05-18 09:48:37 -0700298 assert(obj != NULL);
299 assert(obj->clazz != NULL);
300 assert(ctx != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800301
Barry Hayese1bccb92010-05-18 09:48:37 -0700302 if (obj->clazz->refOffsets != CLASS_WALK_SUPER) {
303 unsigned int refOffsets = obj->clazz->refOffsets;
Barry Hayeseac47ed2009-06-22 11:45:20 -0700304 while (refOffsets != 0) {
305 const int rshift = CLZ(refOffsets);
306 refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
307 markObject(dvmGetFieldObject((Object*)obj,
Barry Hayese1bccb92010-05-18 09:48:37 -0700308 CLASS_OFFSET_FROM_CLZ(rshift)), ctx);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800309 }
Barry Hayeseac47ed2009-06-22 11:45:20 -0700310 } else {
Barry Hayese1bccb92010-05-18 09:48:37 -0700311 ClassObject *clazz;
312 int i;
313 for (clazz = obj->clazz; clazz != NULL; clazz = clazz->super) {
314 InstField *field = clazz->ifields;
315 for (i = 0; i < clazz->ifieldRefCount; ++i, ++field) {
316 void *addr = BYTE_OFFSET((Object *)obj, field->byteOffset);
317 markObject(((JValue *)addr)->l, ctx);
Barry Hayeseac47ed2009-06-22 11:45:20 -0700318 }
Barry Hayeseac47ed2009-06-22 11:45:20 -0700319 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800320 }
321}
322
Barry Hayese1bccb92010-05-18 09:48:37 -0700323/*
324 * Scans the header, static field references, and interface
325 * pointers of a class object.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800326 */
Barry Hayese1bccb92010-05-18 09:48:37 -0700327static void scanClassObject(const ClassObject *obj, GcMarkContext *ctx)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800328{
Barry Hayese1bccb92010-05-18 09:48:37 -0700329 int i;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800330
Barry Hayese1bccb92010-05-18 09:48:37 -0700331 assert(obj != NULL);
332 assert(obj->obj.clazz == gDvm.classJavaLangClass);
333 assert(ctx != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800334
Barry Hayese1bccb92010-05-18 09:48:37 -0700335 markObject((Object *)obj->obj.clazz, ctx);
336 if (IS_CLASS_FLAG_SET(obj, CLASS_ISARRAY)) {
337 markObject((Object *)obj->elementClass, ctx);
338 }
Barry Hayesc49db852010-05-14 13:43:34 -0700339 /* Do super and the interfaces contain Objects and not dex idx values? */
340 if (obj->status > CLASS_IDX) {
341 markObject((Object *)obj->super, ctx);
342 }
Barry Hayese1bccb92010-05-18 09:48:37 -0700343 markObject(obj->classLoader, ctx);
344 /* Scan static field references. */
345 for (i = 0; i < obj->sfieldCount; ++i) {
346 char ch = obj->sfields[i].field.signature[0];
347 if (ch == '[' || ch == 'L') {
348 markObject(obj->sfields[i].value.l, ctx);
349 }
350 }
351 /* Scan the instance fields. */
352 scanInstanceFields((const Object *)obj, ctx);
353 /* Scan interface references. */
Barry Hayesc49db852010-05-14 13:43:34 -0700354 if (obj->status > CLASS_IDX) {
355 for (i = 0; i < obj->interfaceCount; ++i) {
356 markObject((Object *)obj->interfaces[i], ctx);
357 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800358 }
359}
360
Barry Hayese1bccb92010-05-18 09:48:37 -0700361/*
362 * Scans the header of all array objects. If the array object is
363 * specialized to a reference type, scans the array data as well.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800364 */
Barry Hayese1bccb92010-05-18 09:48:37 -0700365static void scanArrayObject(const ArrayObject *obj, GcMarkContext *ctx)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800366{
Barry Hayese1bccb92010-05-18 09:48:37 -0700367 size_t i;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800368
Barry Hayese1bccb92010-05-18 09:48:37 -0700369 assert(obj != NULL);
370 assert(obj->obj.clazz != NULL);
371 assert(ctx != NULL);
372 /* Scan the class object reference. */
373 markObject((Object *)obj->obj.clazz, ctx);
374 if (IS_CLASS_FLAG_SET(obj->obj.clazz, CLASS_ISOBJECTARRAY)) {
375 /* Scan the array contents. */
376 Object **contents = (Object **)obj->contents;
377 for (i = 0; i < obj->length; ++i) {
378 markObject(contents[i], ctx);
379 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800380 }
Barry Hayese1bccb92010-05-18 09:48:37 -0700381}
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800382
Barry Hayese1bccb92010-05-18 09:48:37 -0700383/*
384 * Process the "referent" field in a java.lang.ref.Reference. If the
385 * referent has not yet been marked, put it on the appropriate list in
386 * the gcHeap for later processing.
387 */
388static void delayReferenceReferent(const DataObject *obj,
389 GcMarkContext *ctx)
390{
391 assert(obj != NULL);
392 assert(obj->obj.clazz != NULL);
393 assert(ctx != NULL);
394
395 GcHeap *gcHeap = gDvm.gcHeap;
396 Object *referent;
397
398 /* It's a subclass of java/lang/ref/Reference.
399 * The fields in this class have been arranged
400 * such that scanInstanceFields() did not actually
401 * mark the "referent" field; we need to handle
402 * it specially.
403 *
404 * If the referent already has a strong mark (isMarked(referent)),
405 * we don't care about its reference status.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800406 */
Barry Hayese1bccb92010-05-18 09:48:37 -0700407 referent = dvmGetFieldObject((Object *)obj,
408 gDvm.offJavaLangRefReference_referent);
409 if (referent != NULL && !isMarked(referent, ctx))
410 {
411 u4 refFlags;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800412
Barry Hayese1bccb92010-05-18 09:48:37 -0700413 /* Find out what kind of reference is pointing
414 * to referent.
415 */
416 refFlags = GET_CLASS_FLAG_GROUP(obj->obj.clazz,
417 CLASS_ISREFERENCE |
418 CLASS_ISWEAKREFERENCE |
419 CLASS_ISPHANTOMREFERENCE);
420
421 /* We use the vmData field of Reference objects
422 * as a next pointer in a singly-linked list.
423 * That way, we don't need to allocate any memory
424 * while we're doing a GC.
425 */
426#define ADD_REF_TO_LIST(list, ref) \
427 do { \
428 Object *ARTL_ref_ = (/*de-const*/Object *)(ref); \
429 dvmSetFieldObject(ARTL_ref_, \
430 gDvm.offJavaLangRefReference_vmData, list); \
431 list = ARTL_ref_; \
432 } while (false)
433
434 /* At this stage, we just keep track of all of
435 * the live references that we've seen. Later,
436 * we'll walk through each of these lists and
437 * deal with the referents.
438 */
439 if (refFlags == CLASS_ISREFERENCE) {
440 /* It's a soft reference. Depending on the state,
441 * we'll attempt to collect all of them, some of
442 * them, or none of them.
443 */
444 ADD_REF_TO_LIST(gcHeap->softReferences, obj);
445 } else {
446 /* It's a weak or phantom reference.
447 * Clearing CLASS_ISREFERENCE will reveal which.
448 */
449 refFlags &= ~CLASS_ISREFERENCE;
450 if (refFlags == CLASS_ISWEAKREFERENCE) {
451 ADD_REF_TO_LIST(gcHeap->weakReferences, obj);
452 } else if (refFlags == CLASS_ISPHANTOMREFERENCE) {
453 ADD_REF_TO_LIST(gcHeap->phantomReferences, obj);
454 } else {
455 assert(!"Unknown reference type");
456 }
457 }
458#undef ADD_REF_TO_LIST
459 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800460}
461
Barry Hayese1bccb92010-05-18 09:48:37 -0700462/*
463 * Scans the header and field references of a data object.
464 */
465static void scanDataObject(const DataObject *obj, GcMarkContext *ctx)
466{
467 assert(obj != NULL);
468 assert(obj->obj.clazz != NULL);
469 assert(ctx != NULL);
470 /* Scan the class object. */
471 markObject((Object *)obj->obj.clazz, ctx);
472 /* Scan the instance fields. */
473 scanInstanceFields((const Object *)obj, ctx);
474
475 if (IS_CLASS_FLAG_SET(obj->obj.clazz, CLASS_ISREFERENCE)) {
476 delayReferenceReferent(obj, ctx);
477 }
478}
479
480/*
481 * Scans an object reference. Determines the type of the reference
482 * and dispatches to a specialized scanning routine.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800483 */
484static void scanObject(const Object *obj, GcMarkContext *ctx)
485{
Barry Hayese1bccb92010-05-18 09:48:37 -0700486 assert(obj != NULL);
487 assert(ctx != NULL);
Barry Hayes899cdb72010-06-08 09:59:12 -0700488 assert(obj->clazz != NULL);
Carl Shapiro1a8e21a2010-06-08 13:19:57 -0700489#if WITH_HPROF
490 if (gDvm.gcHeap->hprofContext != NULL) {
491 hprofDumpHeapObject(gDvm.gcHeap->hprofContext, obj);
492 }
493#endif
Barry Hayese1bccb92010-05-18 09:48:37 -0700494 /* Dispatch a type-specific scan routine. */
Carl Shapiro1a8e21a2010-06-08 13:19:57 -0700495 if (obj->clazz == gDvm.classJavaLangClass) {
Barry Hayese1bccb92010-05-18 09:48:37 -0700496 scanClassObject((ClassObject *)obj, ctx);
Carl Shapiro1a8e21a2010-06-08 13:19:57 -0700497 } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
Barry Hayes899cdb72010-06-08 09:59:12 -0700498 scanArrayObject((ArrayObject *)obj, ctx);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800499 } else {
Barry Hayes899cdb72010-06-08 09:59:12 -0700500 scanDataObject((DataObject *)obj, ctx);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800501 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800502}
503
504static void
505processMarkStack(GcMarkContext *ctx)
506{
507 const Object **const base = ctx->stack.base;
508
509 /* Scan anything that's on the mark stack.
510 * We can't use the bitmaps anymore, so use
511 * a finger that points past the end of them.
512 */
513 ctx->finger = (void *)ULONG_MAX;
514 while (ctx->stack.top != base) {
515 scanObject(*ctx->stack.top++, ctx);
516 }
517}
518
519#ifndef NDEBUG
520static uintptr_t gLastFinger = 0;
521#endif
522
523static bool
524scanBitmapCallback(size_t numPtrs, void **ptrs, const void *finger, void *arg)
525{
526 GcMarkContext *ctx = (GcMarkContext *)arg;
527 size_t i;
528
529#ifndef NDEBUG
530 assert((uintptr_t)finger >= gLastFinger);
531 gLastFinger = (uintptr_t)finger;
532#endif
533
534 ctx->finger = finger;
535 for (i = 0; i < numPtrs; i++) {
Carl Shapiro6343bd02010-02-16 17:40:19 -0800536 scanObject(*ptrs++, ctx);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800537 }
538
539 return true;
540}
541
542/* Given bitmaps with the root set marked, find and mark all
543 * reachable objects. When this returns, the entire set of
544 * live objects will be marked and the mark stack will be empty.
545 */
Carl Shapiro29540742010-03-26 15:34:39 -0700546void dvmHeapScanMarkedObjects(void)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800547{
548 GcMarkContext *ctx = &gDvm.gcHeap->markContext;
549
550 assert(ctx->finger == NULL);
551
552 /* The bitmaps currently have bits set for the root set.
553 * Walk across the bitmaps and scan each object.
554 */
555#ifndef NDEBUG
556 gLastFinger = 0;
557#endif
Carl Shapirof373efd2010-02-19 00:46:33 -0800558 dvmHeapBitmapWalk(ctx->bitmap, scanBitmapCallback, ctx);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800559
560 /* We've walked the mark bitmaps. Scan anything that's
561 * left on the mark stack.
562 */
563 processMarkStack(ctx);
564
565 LOG_SCAN("done with marked objects\n");
566}
567
Barry Hayes6930a112009-12-22 11:01:38 -0800568/** Clear the referent field.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800569 */
Barry Hayes6930a112009-12-22 11:01:38 -0800570static void clearReference(Object *reference)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800571{
572 /* This is what the default implementation of Reference.clear()
573 * does. We're required to clear all references to a given
574 * referent atomically, so we can't pop in and out of interp
575 * code each time.
576 *
Barry Hayes6930a112009-12-22 11:01:38 -0800577 * We don't ever actaully call overriding implementations of
578 * Reference.clear().
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800579 */
580 dvmSetFieldObject(reference,
581 gDvm.offJavaLangRefReference_referent, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800582}
583
Carl Shapiro29540742010-03-26 15:34:39 -0700584/*
585 * Returns true if the reference was registered with a reference queue
586 * and has not yet been enqueued.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800587 */
Carl Shapiro29540742010-03-26 15:34:39 -0700588static bool isEnqueuable(const Object *reference)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800589{
Barry Hayes6930a112009-12-22 11:01:38 -0800590 Object *queue = dvmGetFieldObject(reference,
591 gDvm.offJavaLangRefReference_queue);
592 Object *queueNext = dvmGetFieldObject(reference,
593 gDvm.offJavaLangRefReference_queueNext);
594 if (queue == NULL || queueNext != NULL) {
595 /* There is no queue, or the reference has already
596 * been enqueued. The Reference.enqueue() method
597 * will do nothing even if we call it.
598 */
599 return false;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800600 }
601
602 /* We need to call enqueue(), but if we called it from
603 * here we'd probably deadlock. Schedule a call.
604 */
605 return true;
606}
607
Carl Shapiro29540742010-03-26 15:34:39 -0700608/*
609 * Schedules a reference to be appended to its reference queue.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800610 */
Carl Shapiro29540742010-03-26 15:34:39 -0700611static void enqueueReference(Object *ref)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800612{
Carl Shapiro29540742010-03-26 15:34:39 -0700613 LargeHeapRefTable **table;
614 Object *op;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800615
Carl Shapiro29540742010-03-26 15:34:39 -0700616 assert(((uintptr_t)ref & 3) == 0);
617 assert((WORKER_ENQUEUE & ~3) == 0);
618 assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue) != NULL);
619 assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext) == NULL);
620 /* Stuff the enqueue bit in the bottom of the pointer.
621 * Assumes that objects are 8-byte aligned.
Andy McFaddenb18992f2009-09-25 10:42:15 -0700622 *
Carl Shapiro29540742010-03-26 15:34:39 -0700623 * Note that we are adding the *Reference* (which
624 * is by definition already marked at this point) to
625 * this list; we're not adding the referent (which
626 * has already been cleared).
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800627 */
Carl Shapiro29540742010-03-26 15:34:39 -0700628 table = &gDvm.gcHeap->referenceOperations;
629 op = (Object *)((uintptr_t)ref | WORKER_ENQUEUE);
630 if (!dvmHeapAddRefToLargeTable(table, op)) {
631 LOGE_HEAP("enqueueReference(): no room for any more "
632 "reference operations\n");
633 dvmAbort();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800634 }
635}
636
Carl Shapiro29540742010-03-26 15:34:39 -0700637/*
638 * Walks the reference list marking any references subject to the
639 * reference clearing policy. References with a black referent are
640 * removed from the list. References with white referents biased
641 * toward saving are blackened and also removed from the list.
642 */
643void dvmHandleSoftRefs(Object **list)
644{
645 GcMarkContext *markContext;
646 Object *ref, *referent;
647 Object *prev, *next;
648 size_t referentOffset, vmDataOffset;
649 unsigned counter;
650 bool marked;
651
652 markContext = &gDvm.gcHeap->markContext;
653 vmDataOffset = gDvm.offJavaLangRefReference_vmData;
654 referentOffset = gDvm.offJavaLangRefReference_referent;
655 counter = 0;
656 prev = next = NULL;
657 ref = *list;
658 while (ref != NULL) {
659 referent = dvmGetFieldObject(ref, referentOffset);
660 next = dvmGetFieldObject(ref, vmDataOffset);
661 assert(referent != NULL);
662 marked = isMarked(referent, markContext);
663 if (!marked && ((++counter) & 1)) {
664 /* Referent is white and biased toward saving, mark it. */
Barry Hayese1bccb92010-05-18 09:48:37 -0700665 assert(referent != NULL);
666 markObject(referent, markContext);
Carl Shapiro29540742010-03-26 15:34:39 -0700667 marked = true;
668 }
669 if (marked) {
670 /* Referent is black, unlink it. */
671 if (prev != NULL) {
672 dvmSetFieldObject(ref, vmDataOffset, NULL);
673 dvmSetFieldObject(prev, vmDataOffset, next);
674 }
675 } else {
676 /* Referent is white, skip over it. */
677 prev = ref;
678 }
679 ref = next;
680 }
681 /*
682 * Restart the mark with the newly black references added to the
683 * root set.
684 */
685 processMarkStack(markContext);
686}
687
688/*
689 * Walks the reference list and clears references with an unmarked
690 * (white) referents. Cleared references registered to a reference
691 * queue are scheduled for appending by the heap worker thread.
692 */
693void dvmClearWhiteRefs(Object **list)
694{
695 GcMarkContext *markContext;
696 Object *ref, *referent;
697 size_t referentOffset, vmDataOffset;
698 bool doSignal;
699
700 markContext = &gDvm.gcHeap->markContext;
701 vmDataOffset = gDvm.offJavaLangRefReference_vmData;
702 referentOffset = gDvm.offJavaLangRefReference_referent;
703 doSignal = false;
704 while (*list != NULL) {
705 ref = *list;
706 referent = dvmGetFieldObject(ref, referentOffset);
707 *list = dvmGetFieldObject(ref, vmDataOffset);
708 assert(referent != NULL);
709 if (!isMarked(referent, markContext)) {
710 /* Referent is "white", clear it. */
711 clearReference(ref);
712 if (isEnqueuable(ref)) {
713 enqueueReference(ref);
714 doSignal = true;
715 }
716 }
717 }
718 /*
719 * If we cleared a reference with a reference queue we must notify
720 * the heap worker to append the reference.
721 */
722 if (doSignal) {
723 dvmSignalHeapWorker(false);
724 }
725 assert(*list == NULL);
726}
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800727
728/* Find unreachable objects that need to be finalized,
729 * and schedule them for finalization.
730 */
731void dvmHeapScheduleFinalizations()
732{
733 HeapRefTable newPendingRefs;
734 LargeHeapRefTable *finRefs = gDvm.gcHeap->finalizableRefs;
735 Object **ref;
736 Object **lastRef;
737 size_t totalPendCount;
738 GcMarkContext *markContext = &gDvm.gcHeap->markContext;
739
740 /*
741 * All reachable objects have been marked.
742 * Any unmarked finalizable objects need to be finalized.
743 */
744
745 /* Create a table that the new pending refs will
746 * be added to.
747 */
748 if (!dvmHeapInitHeapRefTable(&newPendingRefs, 128)) {
749 //TODO: mark all finalizable refs and hope that
750 // we can schedule them next time. Watch out,
751 // because we may be expecting to free up space
752 // by calling finalizers.
753 LOGE_GC("dvmHeapScheduleFinalizations(): no room for "
754 "pending finalizations\n");
755 dvmAbort();
756 }
757
758 /* Walk through finalizableRefs and move any unmarked references
759 * to the list of new pending refs.
760 */
761 totalPendCount = 0;
762 while (finRefs != NULL) {
763 Object **gapRef;
764 size_t newPendCount = 0;
765
766 gapRef = ref = finRefs->refs.table;
767 lastRef = finRefs->refs.nextEntry;
768 while (ref < lastRef) {
Carl Shapiro6343bd02010-02-16 17:40:19 -0800769 if (!isMarked(*ref, markContext)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800770 if (!dvmHeapAddToHeapRefTable(&newPendingRefs, *ref)) {
771 //TODO: add the current table and allocate
772 // a new, smaller one.
773 LOGE_GC("dvmHeapScheduleFinalizations(): "
774 "no room for any more pending finalizations: %zd\n",
775 dvmHeapNumHeapRefTableEntries(&newPendingRefs));
776 dvmAbort();
777 }
778 newPendCount++;
779 } else {
780 /* This ref is marked, so will remain on finalizableRefs.
781 */
782 if (newPendCount > 0) {
783 /* Copy it up to fill the holes.
784 */
785 *gapRef++ = *ref;
786 } else {
787 /* No holes yet; don't bother copying.
788 */
789 gapRef++;
790 }
791 }
792 ref++;
793 }
794 finRefs->refs.nextEntry = gapRef;
795 //TODO: if the table is empty when we're done, free it.
796 totalPendCount += newPendCount;
797 finRefs = finRefs->next;
798 }
799 LOGD_GC("dvmHeapScheduleFinalizations(): %zd finalizers triggered.\n",
800 totalPendCount);
801 if (totalPendCount == 0) {
802 /* No objects required finalization.
803 * Free the empty temporary table.
804 */
805 dvmClearReferenceTable(&newPendingRefs);
806 return;
807 }
808
809 /* Add the new pending refs to the main list.
810 */
811 if (!dvmHeapAddTableToLargeTable(&gDvm.gcHeap->pendingFinalizationRefs,
812 &newPendingRefs))
813 {
814 LOGE_GC("dvmHeapScheduleFinalizations(): can't insert new "
815 "pending finalizations\n");
816 dvmAbort();
817 }
818
819 //TODO: try compacting the main list with a memcpy loop
820
821 /* Mark the refs we just moved; we don't want them or their
822 * children to get swept yet.
823 */
824 ref = newPendingRefs.table;
825 lastRef = newPendingRefs.nextEntry;
826 assert(ref < lastRef);
827 HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0);
828 while (ref < lastRef) {
Barry Hayese1bccb92010-05-18 09:48:37 -0700829 assert(*ref != NULL);
830 markObject(*ref, markContext);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800831 ref++;
832 }
833 HPROF_CLEAR_GC_SCAN_STATE();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800834 processMarkStack(markContext);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800835 dvmSignalHeapWorker(false);
836}
837
838void dvmHeapFinishMarkStep()
839{
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800840 GcMarkContext *markContext;
841
842 markContext = &gDvm.gcHeap->markContext;
843
844 /* The sweep step freed every object that appeared in the
845 * HeapSource bitmaps that didn't appear in the mark bitmaps.
846 * The new state of the HeapSource is exactly the final
847 * mark bitmaps, so swap them in.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800848 */
Carl Shapirof373efd2010-02-19 00:46:33 -0800849 dvmHeapSourceSwapBitmaps();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800850
Carl Shapirof373efd2010-02-19 00:46:33 -0800851 /* Clean up everything else associated with the marking process.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800852 */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800853 destroyMarkStack(&markContext->stack);
854
Carl Shapirof373efd2010-02-19 00:46:33 -0800855 markContext->finger = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800856}
857
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800858static bool
859sweepBitmapCallback(size_t numPtrs, void **ptrs, const void *finger, void *arg)
860{
861 const ClassObject *const classJavaLangClass = gDvm.classJavaLangClass;
Barry Hayes5cbb2302010-02-02 14:07:37 -0800862 const bool overwriteFree = gDvm.overwriteFree;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800863 size_t i;
Barry Hayesdde8ab02009-05-20 12:10:36 -0700864 void **origPtrs = ptrs;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800865
866 for (i = 0; i < numPtrs; i++) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800867 Object *obj;
868
Carl Shapiro6343bd02010-02-16 17:40:19 -0800869 obj = (Object *)*ptrs++;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800870
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800871 /* This assumes that java.lang.Class will never go away.
872 * If it can, and we were the last reference to it, it
873 * could have already been swept. However, even in that case,
874 * gDvm.classJavaLangClass should still have a useful
875 * value.
876 */
877 if (obj->clazz == classJavaLangClass) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800878 /* dvmFreeClassInnards() may have already been called,
879 * but it's safe to call on the same ClassObject twice.
880 */
881 dvmFreeClassInnards((ClassObject *)obj);
882 }
883
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800884 /* Overwrite the to-be-freed object to make stale references
885 * more obvious.
886 */
Barry Hayes5cbb2302010-02-02 14:07:37 -0800887 if (overwriteFree) {
Barry Hayes2e3c3e12010-02-22 09:39:10 -0800888 int objlen;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800889 ClassObject *clazz = obj->clazz;
Barry Hayes2e3c3e12010-02-22 09:39:10 -0800890 objlen = dvmHeapSourceChunkSize(obj);
891 memset(obj, 0xa5, objlen);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800892 obj->clazz = (ClassObject *)((uintptr_t)clazz ^ 0xffffffff);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800893 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800894 }
Barry Hayesdde8ab02009-05-20 12:10:36 -0700895 // TODO: dvmHeapSourceFreeList has a loop, just like the above
896 // does. Consider collapsing the two loops to save overhead.
897 dvmHeapSourceFreeList(numPtrs, origPtrs);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800898
899 return true;
900}
901
Carl Shapiro5a6071b2010-01-07 21:35:50 -0800902/* Returns true if the given object is unmarked. Ignores the low bits
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800903 * of the pointer because the intern table may set them.
904 */
905static int isUnmarkedObject(void *object)
906{
Carl Shapiro6343bd02010-02-16 17:40:19 -0800907 return !isMarked((void *)((uintptr_t)object & ~(HB_OBJECT_ALIGNMENT-1)),
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800908 &gDvm.gcHeap->markContext);
909}
910
911/* Walk through the list of objects that haven't been
912 * marked and free them.
913 */
914void
Carl Shapirod25566d2010-03-11 20:39:47 -0800915dvmHeapSweepUnmarkedObjects(GcMode mode, int *numFreed, size_t *sizeFreed)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800916{
Carl Shapirof373efd2010-02-19 00:46:33 -0800917 HeapBitmap markBits[HEAP_SOURCE_MAX_HEAP_COUNT];
Carl Shapirod77f7fd2010-04-05 19:23:31 -0700918 HeapBitmap liveBits[HEAP_SOURCE_MAX_HEAP_COUNT];
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800919 size_t origObjectsAllocated;
920 size_t origBytesAllocated;
Carl Shapirod25566d2010-03-11 20:39:47 -0800921 size_t numBitmaps, numSweepBitmaps;
Barry Hayese168ebd2010-05-07 09:19:46 -0700922 size_t i;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800923
924 /* All reachable objects have been marked.
925 * Detach any unreachable interned strings before
926 * we sweep.
927 */
928 dvmGcDetachDeadInternedStrings(isUnmarkedObject);
929
930 /* Free any known objects that are not marked.
931 */
932 origObjectsAllocated = dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED, NULL, 0);
933 origBytesAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
934
Carl Shapiro5a6071b2010-01-07 21:35:50 -0800935 dvmSweepMonitorList(&gDvm.monitorList, isUnmarkedObject);
936
Carl Shapirof373efd2010-02-19 00:46:33 -0800937 numBitmaps = dvmHeapSourceGetNumHeaps();
Carl Shapirod77f7fd2010-04-05 19:23:31 -0700938 dvmHeapSourceGetObjectBitmaps(liveBits, markBits, numBitmaps);
Carl Shapirod25566d2010-03-11 20:39:47 -0800939 if (mode == GC_PARTIAL) {
940 numSweepBitmaps = 1;
Carl Shapirod77f7fd2010-04-05 19:23:31 -0700941 assert((uintptr_t)gDvm.gcHeap->markContext.immuneLimit == liveBits[0].base);
Carl Shapirod25566d2010-03-11 20:39:47 -0800942 } else {
943 numSweepBitmaps = numBitmaps;
944 }
Barry Hayese168ebd2010-05-07 09:19:46 -0700945 for (i = 0; i < numSweepBitmaps; i++) {
946 dvmHeapBitmapXorWalk(&markBits[i], &liveBits[i],
947 sweepBitmapCallback, NULL);
948 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800949
950 *numFreed = origObjectsAllocated -
951 dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED, NULL, 0);
952 *sizeFreed = origBytesAllocated -
953 dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
954
955#ifdef WITH_PROFILER
956 if (gDvm.allocProf.enabled) {
957 gDvm.allocProf.freeCount += *numFreed;
958 gDvm.allocProf.freeSize += *sizeFreed;
959 }
960#endif
961}