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