/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Garbage-collecting memory allocator.
*/
#include "Dalvik.h"
#include "alloc/Heap.h"
#include "alloc/HeapInternal.h"
#include "alloc/HeapSource.h"
/*
* Initialize the GC universe.
*
* We're currently using a memory-mapped arena to keep things off of the
* main heap. This needs to be replaced with something real.
*/
bool dvmGcStartup()
{
dvmInitMutex(&gDvm.gcHeapLock);
pthread_cond_init(&gDvm.gcHeapCond, NULL);
return dvmHeapStartup();
}
/*
* Post-zygote heap initialization, including starting
* the HeapWorker thread.
*/
bool dvmGcStartupAfterZygote()
{
return dvmHeapStartupAfterZygote();
}
/*
* Shutdown the threads internal to the garbage collector.
*/
void dvmGcThreadShutdown()
{
dvmHeapThreadShutdown();
}
/*
* Shut the GC down.
*/
void dvmGcShutdown()
{
//TODO: grab and destroy the lock
dvmHeapShutdown();
}
/*
* Do any last-minute preparation before we call fork() for the first time.
*/
bool dvmGcPreZygoteFork()
{
return dvmHeapSourceStartupBeforeFork();
}
bool dvmGcStartupClasses()
{
ClassObject *klass = dvmFindSystemClass("Ljava/lang/Daemons;");
if (klass == NULL) {
return false;
}
Method *method = dvmFindDirectMethodByDescriptor(klass, "start", "()V");
if (method == NULL) {
return false;
}
Thread *self = dvmThreadSelf();
assert(self != NULL);
JValue unusedResult;
dvmCallMethod(self, method, NULL, &unusedResult);
return true;
}
/*
* Create a "stock instance" of an exception class.
*/
static Object* createStockException(const char* descriptor, const char* msg)
{
Thread* self = dvmThreadSelf();
StringObject* msgStr = NULL;
ClassObject* clazz;
Method* init;
Object* obj;
/* find class, initialize if necessary */
clazz = dvmFindSystemClass(descriptor);
if (clazz == NULL) {
ALOGE("Unable to find %s", descriptor);
return NULL;
}
init = dvmFindDirectMethodByDescriptor(clazz, "<init>",
"(Ljava/lang/String;)V");
if (init == NULL) {
ALOGE("Unable to find String-arg constructor for %s", descriptor);
return NULL;
}
obj = dvmAllocObject(clazz, ALLOC_DEFAULT);
if (obj == NULL)
return NULL;
if (msg == NULL) {
msgStr = NULL;
} else {
msgStr = dvmCreateStringFromCstr(msg);
if (msgStr == NULL) {
ALOGW("Could not allocate message string \"%s\"", msg);
dvmReleaseTrackedAlloc(obj, self);
return NULL;
}
}
JValue unused;
dvmCallMethod(self, init, obj, &unused, msgStr);
if (dvmCheckException(self)) {
dvmReleaseTrackedAlloc((Object*) msgStr, self);
dvmReleaseTrackedAlloc(obj, self);
return NULL;
}
dvmReleaseTrackedAlloc((Object*) msgStr, self); // okay if msgStr NULL
return obj;
}
/*
* Create some "stock" exceptions. These can be thrown when the system is
* too screwed up to allocate and initialize anything, or when we don't
* need a meaningful stack trace.
*
* We can't do this during the initial startup because we need to execute
* the constructors.
*/
bool dvmCreateStockExceptions()
{
/*
* Pre-allocate some throwables. These need to be explicitly added
* to the GC's root set (see dvmHeapMarkRootSet()).
*/
gDvm.outOfMemoryObj = createStockException("Ljava/lang/OutOfMemoryError;",
"[memory exhausted]");
dvmReleaseTrackedAlloc(gDvm.outOfMemoryObj, NULL);
gDvm.internalErrorObj = createStockException("Ljava/lang/InternalError;",
"[pre-allocated]");
dvmReleaseTrackedAlloc(gDvm.internalErrorObj, NULL);
gDvm.noClassDefFoundErrorObj =
createStockException("Ljava/lang/NoClassDefFoundError;",
"[generic]");
dvmReleaseTrackedAlloc(gDvm.noClassDefFoundErrorObj, NULL);
if (gDvm.outOfMemoryObj == NULL || gDvm.internalErrorObj == NULL ||
gDvm.noClassDefFoundErrorObj == NULL)
{
ALOGW("Unable to create stock exceptions");
return false;
}
return true;
}
/*
* Create an instance of the specified class.
*
* Returns NULL and throws an exception on failure.
*/
Object* dvmAllocObject(ClassObject* clazz, int flags)
{
Object* newObj;
assert(clazz != NULL);
assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz));
/* allocate on GC heap; memory is zeroed out */
newObj = (Object*)dvmMalloc(clazz->objectSize, flags);
if (newObj != NULL) {
DVM_OBJECT_INIT(newObj, clazz);
dvmTrackAllocation(clazz, clazz->objectSize); /* notify DDMS */
}
return newObj;
}
/*
* Create a copy of an object, for Object.clone().
*
* We use the size actually allocated, rather than obj->clazz->objectSize,
* because the latter doesn't work for array objects.
*/
Object* dvmCloneObject(Object* obj, int flags)
{
assert(dvmIsValidObject(obj));
ClassObject* clazz = obj->clazz;
/* Class.java shouldn't let us get here (java.lang.Class is final
* and does not implement Clonable), but make extra sure.
* A memcpy() clone will wreak havoc on a ClassObject's "innards".
*/
assert(!dvmIsTheClassClass(clazz));
size_t size;
if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
size = dvmArrayObjectSize((ArrayObject *)obj);
} else {
size = clazz->objectSize;
}
Object* copy = (Object*)dvmMalloc(size, flags);
if (copy == NULL)
return NULL;
DVM_OBJECT_INIT(copy, clazz);
size_t offset = sizeof(Object);
/* Copy instance data. We assume memcpy copies by words. */
memcpy((char*)copy + offset, (char*)obj + offset, size - offset);
/* Mark the clone as finalizable if appropriate. */
if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE)) {
dvmSetFinalizable(copy);
}
dvmTrackAllocation(clazz, size); /* notify DDMS */
return copy;
}
/*
* Track an object that was allocated internally and isn't yet part of the
* VM root set.
*
* We could do this per-thread or globally. If it's global we don't have
* to do the thread lookup but we do have to synchronize access to the list.
*
* "obj" must not be NULL.
*
* NOTE: "obj" is not a fully-formed object; in particular, obj->clazz will
* usually be NULL since we're being called from dvmMalloc().
*/
void dvmAddTrackedAlloc(Object* obj, Thread* self)
{
if (self == NULL)
self = dvmThreadSelf();
assert(obj != NULL);
assert(self != NULL);
if (!dvmAddToReferenceTable(&self->internalLocalRefTable, obj)) {
ALOGE("threadid=%d: unable to add %p to internal ref table",
self->threadId, obj);
dvmDumpThread(self, false);
dvmAbort();
}
}
/*
* Stop tracking an object.
*
* We allow attempts to delete NULL "obj" so that callers don't have to wrap
* calls with "if != NULL".
*/
void dvmReleaseTrackedAlloc(Object* obj, Thread* self)
{
if (obj == NULL)
return;
if (self == NULL)
self = dvmThreadSelf();
assert(self != NULL);
if (!dvmRemoveFromReferenceTable(&self->internalLocalRefTable,
self->internalLocalRefTable.table, obj))
{
ALOGE("threadid=%d: failed to remove %p from internal ref table",
self->threadId, obj);
dvmAbort();
}
}
/*
* Explicitly initiate garbage collection.
*/
void dvmCollectGarbage()
{
if (gDvm.disableExplicitGc) {
return;
}
dvmLockHeap();
dvmWaitForConcurrentGcToComplete();
dvmCollectGarbageInternal(GC_EXPLICIT);
dvmUnlockHeap();
}
struct CountContext {
const ClassObject *clazz;
size_t count;
};
static void countInstancesOfClassCallback(Object *obj, void *arg)
{
CountContext *ctx = (CountContext *)arg;
assert(ctx != NULL);
if (obj->clazz == ctx->clazz) {
ctx->count += 1;
}
}
size_t dvmCountInstancesOfClass(const ClassObject *clazz)
{
CountContext ctx = { clazz, 0 };
dvmLockHeap();
HeapBitmap *bitmap = dvmHeapSourceGetLiveBits();
dvmHeapBitmapWalk(bitmap, countInstancesOfClassCallback, &ctx);
dvmUnlockHeap();
return ctx.count;
}
static void countAssignableInstancesOfClassCallback(Object *obj, void *arg)
{
CountContext *ctx = (CountContext *)arg;
assert(ctx != NULL);
if (obj->clazz != NULL && dvmInstanceof(obj->clazz, ctx->clazz)) {
ctx->count += 1;
}
}
size_t dvmCountAssignableInstancesOfClass(const ClassObject *clazz)
{
CountContext ctx = { clazz, 0 };
dvmLockHeap();
HeapBitmap *bitmap = dvmHeapSourceGetLiveBits();
dvmHeapBitmapWalk(bitmap, countAssignableInstancesOfClassCallback, &ctx);
dvmUnlockHeap();
return ctx.count;
}
bool dvmIsHeapAddress(void *address)
{
return address != NULL && (((uintptr_t) address & (8-1)) == 0);
}
bool dvmIsNonMovingObject(const Object* object)
{
return true;
}