• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&copy->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