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