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