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