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. These won't have
72 * useful stack traces in them, but they can be thrown when everything
73 * else is not working in a container class.
74 */
createStockException(const char * descriptor)75 static Object* createStockException(const char* descriptor)
76 {
77 ClassObject* clazz;
78 Method* init;
79 Object* obj;
80
81 clazz = dvmFindSystemClass(descriptor);
82 if (clazz == NULL) {
83 LOGE("Unable to find %s\n", descriptor);
84 return NULL;
85 }
86
87 init = dvmFindDirectMethodByDescriptor(clazz, "<init>", "()V");
88 if (init == NULL) {
89 LOGE("Unable to find nullary constructor for %s\n", descriptor);
90 return NULL;
91 }
92
93 obj = dvmAllocObject(clazz, ALLOC_DEFAULT);
94 if (obj == NULL)
95 return NULL;
96
97 Thread* self = dvmThreadSelf();
98 JValue unused;
99 dvmCallMethod(self, init, obj, &unused);
100 if (dvmCheckException(self))
101 return NULL;
102
103 return obj;
104 }
105
106 /*
107 * "Late" initialization. We had to defer this until we were able to
108 * interpret code.
109 */
dvmGcLateInit(void)110 bool dvmGcLateInit(void)
111 {
112 /*
113 * Pre-allocate some throwables. These need to be explicitly added
114 * to the root set by the GC.
115 */
116 gDvm.outOfMemoryObj = createStockException("Ljava/lang/OutOfMemoryError;");
117 dvmReleaseTrackedAlloc(gDvm.outOfMemoryObj, NULL);
118 gDvm.internalErrorObj = createStockException("Ljava/lang/InternalError;");
119 dvmReleaseTrackedAlloc(gDvm.internalErrorObj, NULL);
120 if (gDvm.outOfMemoryObj == NULL || gDvm.internalErrorObj == NULL) {
121 LOGW("Unable to create stock exceptions\n");
122 return false;
123 }
124
125 return true;
126 }
127
128
129 /*
130 * Create an instance of the specified class.
131 *
132 * Returns NULL and throws an exception on failure.
133 */
dvmAllocObject(ClassObject * clazz,int flags)134 Object* dvmAllocObject(ClassObject* clazz, int flags)
135 {
136 Object* newObj;
137
138 assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz));
139
140 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE)) {
141 flags |= ALLOC_FINALIZABLE;
142 }
143
144 /* allocate on GC heap; memory is zeroed out */
145 newObj = dvmMalloc(clazz->objectSize, flags);
146 if (newObj != NULL) {
147 DVM_OBJECT_INIT(newObj, clazz);
148 LOGVV("AllocObject: %s (%d)\n", clazz->descriptor,
149 (int) clazz->objectSize);
150 #if WITH_HPROF && WITH_HPROF_STACK
151 hprofFillInStackTrace(newObj);
152 #endif
153 dvmTrackAllocation(clazz, clazz->objectSize);
154 }
155
156 return newObj;
157 }
158
159 /*
160 * Create a copy of an object, for Object.clone().
161 *
162 * We use the size actually allocated, rather than obj->clazz->objectSize,
163 * because the latter doesn't work for array objects.
164 */
dvmCloneObject(Object * obj)165 Object* dvmCloneObject(Object* obj)
166 {
167 Object* copy;
168 int size;
169 int flags;
170
171 assert(dvmIsValidObject(obj));
172
173 /* Class.java shouldn't let us get here (java.lang.Class is final
174 * and does not implement Clonable), but make extra sure.
175 * A memcpy() clone will wreak havoc on a ClassObject's "innards".
176 */
177 assert(obj->clazz != gDvm.classJavaLangClass);
178
179 if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE))
180 flags = ALLOC_DEFAULT | ALLOC_FINALIZABLE;
181 else
182 flags = ALLOC_DEFAULT;
183
184 //TODO: use clazz->objectSize for non-arrays
185 size = dvmObjectSizeInHeap(obj);
186
187 copy = dvmMalloc(size, flags);
188 if (copy == NULL)
189 return NULL;
190 #if WITH_HPROF && WITH_HPROF_STACK
191 hprofFillInStackTrace(copy);
192 dvmTrackAllocation(obj->clazz, size);
193 #endif
194
195 memcpy(copy, obj, size);
196 DVM_LOCK_INIT(©->lock);
197
198 //LOGV("CloneObject: %p->%p %s (%d)\n", obj, copy, obj->clazz->name, size);
199
200 // TODO: deal with reference classes
201
202 /* don't call dvmReleaseTrackedAlloc -- the caller must do that */
203
204 return copy;
205 }
206
207
208 /*
209 * Track an object that was allocated internally and isn't yet part of the
210 * VM root set.
211 *
212 * We could do this per-thread or globally. If it's global we don't have
213 * to do the thread lookup but we do have to synchronize access to the list.
214 *
215 * NOTE: "obj" is not a fully-formed object; in particular, obj->clazz will
216 * usually be NULL since we're being called from dvmMalloc().
217 */
dvmAddTrackedAlloc(Object * obj,Thread * self)218 void dvmAddTrackedAlloc(Object* obj, Thread* self)
219 {
220 if (self == NULL)
221 self = dvmThreadSelf();
222
223 //LOGI("TRACK ADD %p\n", obj);
224
225 assert(self != NULL);
226 if (!dvmAddToReferenceTable(&self->internalLocalRefTable, obj)) {
227 LOGE("threadid=%d: unable to add %p to internal ref table\n",
228 self->threadId, obj);
229 dvmDumpThread(self, false);
230 dvmAbort();
231 }
232 }
233
234 /*
235 * Stop tracking an object.
236 *
237 * We allow attempts to delete NULL "obj" so that callers don't have to wrap
238 * calls with "if != NULL".
239 */
dvmReleaseTrackedAlloc(Object * obj,Thread * self)240 void dvmReleaseTrackedAlloc(Object* obj, Thread* self)
241 {
242 if (obj == NULL)
243 return;
244
245 if (self == NULL)
246 self = dvmThreadSelf();
247 assert(self != NULL);
248
249 //LOGI("TRACK REM %p (%s)\n", obj,
250 // (obj->clazz != NULL) ? obj->clazz->name : "");
251
252 if (!dvmRemoveFromReferenceTable(&self->internalLocalRefTable,
253 self->internalLocalRefTable.table, obj))
254 {
255 LOGE("threadid=%d: failed to remove %p from internal ref table\n",
256 self->threadId, obj);
257 dvmAbort();
258 }
259 }
260
261
262 /*
263 * Explicitly initiate garbage collection.
264 */
dvmCollectGarbage(bool collectSoftReferences)265 void dvmCollectGarbage(bool collectSoftReferences)
266 {
267 dvmLockHeap();
268
269 LOGVV("Explicit GC\n");
270 dvmCollectGarbageInternal(collectSoftReferences);
271
272 dvmUnlockHeap();
273 }
274