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