Implementing simple mark&sweep GC

This commit is contained in:
Alexey Andreev 2016-09-08 16:36:10 +03:00
parent f7296e0389
commit ae5d701aac
7 changed files with 322 additions and 21 deletions

View File

@ -16,6 +16,7 @@
package org.teavm.runtime;
import org.teavm.interop.Address;
import org.teavm.interop.NoGC;
import org.teavm.interop.StaticInit;
import org.teavm.interop.Structure;
@ -24,24 +25,17 @@ public final class Allocator {
private Allocator() {
}
static Address address = initialize();
private static native Address initialize();
public static Address allocate(RuntimeClass tag) {
Address result = address;
address = result.add(tag.size);
fillZero(result, tag.size);
RuntimeObject object = result.toStructure();
RuntimeObject object = GC.alloc(tag.size);
fillZero(object.toAddress(), tag.size);
object.classReference = tag.toAddress().toInt() >> 3;
return result;
return object.toAddress();
}
public static Address allocateArray(RuntimeClass tag, int size) {
Address result = address;
int sizeInBytes = tag.itemType.size * size + Structure.sizeOf(RuntimeArray.class);
sizeInBytes = Address.align(Address.fromInt(sizeInBytes), 4).toInt();
address = result.add(sizeInBytes);
Address result = GC.alloc(sizeInBytes).toAddress();
fillZero(result, sizeInBytes);
RuntimeArray array = result.toStructure();
@ -51,19 +45,12 @@ public final class Allocator {
return result;
}
@NoGC
public static native void fillZero(Address address, int count);
@NoGC
public static native void moveMemoryBlock(Address source, Address target, int count);
@NoGC
public static native boolean isInitialized(Class<?> cls);
public static native void allocStack(int size);
public static native void registerGcRoot(int index, Object object);
public static native void removeGcRoot(int index);
public static native void releaseStack(int size);
public static native Address getStaticGcRoots();
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2016 Alexey Andreev.
*
* 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.
*/
package org.teavm.runtime;
class FreeChunk extends RuntimeObject {
int size;
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2016 Alexey Andreev.
*
* 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.
*/
package org.teavm.runtime;
import org.teavm.interop.Structure;
class FreeChunkHolder extends Structure {
FreeChunk value;
}

View File

@ -0,0 +1,169 @@
/*
* Copyright 2016 Alexey Andreev.
*
* 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.
*/
package org.teavm.runtime;
import org.teavm.interop.Address;
import org.teavm.interop.NoGC;
import org.teavm.interop.StaticInit;
import org.teavm.interop.Structure;
@NoGC
@StaticInit
public final class GC {
private GC() {
}
static Address currentChunkLimit;
static FreeChunk currentChunk;
static FreeChunkHolder currentChunkPointer;
static int freeChunks;
private static native Address gcStorageAddress();
private static native Address heapAddress();
private static native Region regionsAddress();
private static native int regionMaxCount();
private static native int availableBytes();
private static native int regionSize();
static {
currentChunk = heapAddress().toStructure();
currentChunk.classReference = 0;
currentChunk.size = availableBytes();
currentChunkLimit = currentChunk.toAddress().add(currentChunk.size);
currentChunkPointer = gcStorageAddress().toStructure();
currentChunkPointer.value = currentChunk;
freeChunks = 1;
getAvailableChunkIfPossible(0);
}
public static RuntimeObject alloc(int size) {
FreeChunk current = currentChunk;
Address next = currentChunk.toAddress().add(size);
if (!next.add(Structure.sizeOf(FreeChunk.class) + 1).isLessThan(currentChunkLimit)) {
getAvailableChunk(size);
}
int oldSize = current.size;
Address result = current.toAddress();
currentChunk = next.toStructure();
currentChunk.classReference = 0;
currentChunk.size = oldSize - size;
return result.toStructure();
}
private static void getAvailableChunk(int size) {
if (getAvailableChunkIfPossible(size)) {
return;
}
collectGarbage(size);
getAvailableChunkIfPossible(size);
}
private static boolean getAvailableChunkIfPossible(int size) {
while (!currentChunk.toAddress().add(size).isLessThan(currentChunkLimit)) {
if (--size == 0) {
return false;
}
currentChunkPointer = currentChunkPointer.toAddress().add(FreeChunkHolder.class, 1).toStructure();
currentChunk = currentChunkPointer.value;
currentChunkLimit = currentChunk.toAddress().add(currentChunk.size);
}
return false;
}
private static boolean collectGarbage(int size) {
mark();
return false;
}
private static void mark() {
MarkQueue.init();
Allocator.fillZero(regionsAddress().toAddress(), regionMaxCount() * Structure.sizeOf(Region.class));
Address staticRoots = Mutator.getStaticGcRoots();
int staticCount = staticRoots.getInt();
staticRoots.add(8);
while (staticCount-- > 0) {
RuntimeObject object = staticRoots.getAddress().toStructure();
if (object != null) {
mark(object);
}
}
for (Address stackRoots = Mutator.getStackGcRoots(); stackRoots != null;
stackRoots = Mutator.getNextStackRoots(stackRoots)) {
int count = Mutator.getStackRootCount(stackRoots);
Address stackRootsPtr = Mutator.getStackRootPointer(stackRoots);
while (count-- > 0) {
RuntimeObject obj = stackRootsPtr.getAddress().toStructure();
mark(obj);
stackRootsPtr = stackRootsPtr.add(Address.sizeOf());
}
}
}
private static void mark(RuntimeObject object) {
if (object == null || isMarked(object)) {
return;
}
MarkQueue.enqueue(object);
while (!MarkQueue.isEmpty()) {
object = MarkQueue.dequeue();
if (isMarked(object)) {
continue;
}
object.classReference |= RuntimeObject.GC_MARKED;
long offset = object.toAddress().toLong() - heapAddress().toLong();
Region region = regionsAddress().toAddress().add(Region.class, (int) (offset / regionSize()))
.toStructure();
short relativeOffset = (short) (offset % regionSize() + 1);
if (region.start == 0 || region.start > relativeOffset) {
region.start = relativeOffset;
}
RuntimeClass cls = RuntimeClass.getClass(object);
while (cls != null) {
Address layout = cls.layout;
if (layout != null) {
short fieldCount = layout.getShort();
while (fieldCount-- > 0) {
layout = layout.add(2);
int fieldOffset = layout.getShort();
RuntimeObject reference = object.toAddress().add(fieldOffset).toStructure();
if (reference != null && !isMarked(reference)) {
MarkQueue.enqueue(reference);
}
}
}
cls = cls.parent;
}
}
}
private static boolean isMarked(RuntimeObject object) {
return (object.classReference & RuntimeObject.GC_MARKED) != 0;
}
static class Region extends Structure {
short start;
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2016 Alexey Andreev.
*
* 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.
*/
package org.teavm.runtime;
import org.teavm.interop.Address;
final class MarkQueue {
private MarkQueue() {
}
private static int head;
private static int tail;
static void init() {
head = 0;
tail = 0;
}
private static native Address getBase();
private static native int getSize();
static void enqueue(RuntimeObject object) {
getBase().add(Address.sizeOf() * tail).putAddress(object.toAddress());
if (++tail >= getSize()) {
tail = 0;
}
}
static RuntimeObject dequeue() {
Address result = getBase().add(Address.sizeOf() * head).getAddress();
if (++head >= getSize()) {
head = 0;
}
return result.toStructure();
}
static boolean isEmpty() {
return head == tail;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2016 Alexey Andreev.
*
* 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.
*/
package org.teavm.runtime;
import org.teavm.interop.Address;
import org.teavm.interop.NoGC;
@NoGC
public final class Mutator {
private Mutator() {
}
public static native void allocStack(int size);
public static native void registerGcRoot(int index, Object object);
public static native void removeGcRoot(int index);
public static native void releaseStack(int size);
public static native Address getStaticGcRoots();
public static native Address getStackGcRoots();
public static native Address getNextStackRoots(Address address);
public static native int getStackRootCount(Address address);
public static native Address getStackRootPointer(Address address);
}

View File

@ -22,6 +22,8 @@ public final class Address {
public native Address add(long offset);
public native boolean isLessThan(Address other);
public native int toInt();
public native long toLong();
@ -56,6 +58,10 @@ public final class Address {
public native void putDouble(double value);
public native Address getAddress();
public native void putAddress(Address value);
public static native Address fromInt(int value);
public static native Address fromLong(long value);