/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) 2001-2008, Martin Schoeberl (martin@jopdesign.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Created on 16.06.2005
*
*/
package com.jopdesign.sys;
/**
* @author Martin Schoeberl (martin@jopdesign.com)
*
*/
public class GCTwoSpaceMarkSweepCompact {
static int mem_start; // read from memory
static final int MEM_SIZE = 30000;
// static final int MEM_SIZE = 256000; // in words (262144)
static int full_heap_size;
/**
* The handle contains following data:
* 0 pointer to the object in the heap
* 1 pointer to the method table or 0 when the handle is free
* 2 size - could be in class info
* + GC mark (could be part of the object pointer)
* 3 flags to mark reference fields - could be in class info
* -1 means more than 31 fields -> look in class info....
* 4 pointer to next handle of same type (used or free)
*/
static final int HANDLE_SIZE = 5;
static final int OFF_PTR = 0;
static final int OFF_MTAB = 1;
static final int OFF_SIZE = 2;
static final int OFF_TYPE = 3;
static final int IS_OBJ = 0;
static final int IS_REFARR = 1;
static final int IS_VALARR = 2;
static final int OFF_NEXT = 4;
static final int TYPICAL_OBJ_SIZE = 10;
static int handle_cnt;
static int heap_size;
static int stackStart;
static int heapStartA, heapStartB;
static boolean useA;
static int heapEnd;
static int heapPtr;
static int freeList;
static int useList;
static int addrStaticRefs;
static Object monitor;
static void init(int addr) {
addrStaticRefs = addr;
mem_start = Native.rdMem(0);
full_heap_size = MEM_SIZE-mem_start;
handle_cnt = full_heap_size/2/(TYPICAL_OBJ_SIZE+HANDLE_SIZE);
// handle_cnt = 200;
heap_size = (full_heap_size-handle_cnt*HANDLE_SIZE)/2;
heapStartA = mem_start+handle_cnt*HANDLE_SIZE;
heapStartB = heapStartA+heap_size;
// we canot call System.out here!
// System.out.println("Heap: "+heapStartA+" "+heapStartB+" "+(heapStartB+heap_size));
// System.out.println("Size: "+heap_size+" words");
useA = true;
heapPtr = heapStartA;
stackStart = heapStartB;
heapEnd = heapPtr+heap_size;
freeList = 0;
useList = 0;
sp = 0;
for (int i=0; i<handle_cnt; ++i) {
addToFreeList(mem_start+i*HANDLE_SIZE);
}
// clean the heap
for (int i=heapPtr; i<heapEnd; ++i) {
Native.wrMem(0, i);
}
// allocate the monitor
monitor = new Object();
}
static void addToFreeList(int ref) {
// pointer to former freelist head
Native.wrMem(freeList, ref+OFF_NEXT);
// mark handle as free
Native.wrMem(0, ref+OFF_MTAB);
freeList = ref;
}
static void addToUseList(int ref) {
// add to used list
Native.wrMem(useList, ref+OFF_NEXT);
useList = ref;
}
static int getHandle(int ref, int size) {
int addr = freeList;
freeList = Native.rdMem(freeList+OFF_NEXT);
Native.wrMem(ref, addr);
// mark handle as non free
// will be class info...
Native.wrMem(1, addr+OFF_MTAB);
// should be from the class info
Native.wrMem(size, addr+OFF_SIZE);
// add to used list
addToUseList(addr);
return addr;
}
static int translate(int ref) {
return Native.rdMem(ref);
}
static int sp;
static void push(int ref) {
// if (ref==0) return; // that's a null pointer
// Only objects that are referenced by a handle in the
// handle area are considered for GC.
// Null pointer and references to static strings are not
// investigated.
if (ref<mem_start || ref>=mem_start+handle_cnt*HANDLE_SIZE) return;
// Is this handle on the free list?
// Is possible when using conservative stack scanning
if (Native.rdMem(ref+OFF_MTAB)==0) return;
Native.wrMem(ref, stackStart+sp);
++sp;
if (sp==heap_size) { // do we really need this check???
// mark stack should be large enough if the same size
// as the heap
// However, think about concurrent allocation space
// during mark.
System.out.println("GC stack overflow!");
System.exit(1);
}
}
static int pop() {
if (sp==0) {
return -1;
}
--sp;
return Native.rdMem(stackStart+sp);
}
static void getRoots() {
synchronized (monitor) {
// add static refs to root list
int addr = Native.rdMem(addrStaticRefs);
int cnt = Native.rdMem(addrStaticRefs+1);
for (int i=0; i<cnt; ++i) {
push(Native.rdMem(addr+i));
}
// add complete stack to the root list
int i = Native.getSP();
for (int j=Const.STACK_OFF; j<=i; ++j) {
push(Native.rdIntMem(j));
}
}
}
static void mark() {
int i;
sp = 0;
getRoots();
while (sp!=0) {
int ref = pop();
int size = Native.rdMem(ref+OFF_SIZE);
if (size<0) {
// allready marked
continue;
}
size |= 0x80000000;
Native.wrMem(size, ref+OFF_SIZE);
// get pointer to object
int addr = Native.rdMem(ref);
int flags = Native.rdMem(ref+OFF_TYPE);
if (flags==IS_VALARR) {
// is an array
} else if (flags==IS_REFARR) {
// is an array of references
size = Native.rdMem(addr-1);
for (i=0; i<size; ++i) {
push(Native.rdMem(addr+i));
}
// However, multinewarray does probably NOT work
} else {
// it's a plain object
// get pointer to method table
flags = Native.rdMem(addr-1);
// get real flags
flags = Native.rdMem(flags-2);
for (i=0; flags!=0; ++i) {
if ((flags|1)!=0) {
int child = Native.rdMem(addr+i);
push(child);
}
flags >>= 1;
}
}
}
}
static void sweep() {
int ref = useList;
useList = 0;
while (ref!=0) {
int size = Native.rdMem(ref+OFF_SIZE);
// read next element, as it is destroyed
// by addTo*List()
int next = Native.rdMem(ref+OFF_NEXT);
if (size<0) {
size &= 0x7fffffff;
Native.wrMem(size, ref+OFF_SIZE);
addToUseList(ref);
} else {
addToFreeList(ref);
}
ref = next;
}
}
static void compact() {
// switch from and to space
useA = !useA;
if (useA) {
heapPtr = heapStartA;
stackStart = heapStartB;
} else {
heapPtr = heapStartB;
stackStart = heapStartA;
}
heapEnd = heapPtr+heap_size;
int ref = useList;
while (ref!=0) {
int addr = Native.rdMem(ref+OFF_PTR);
--addr; // copy also the mtab pointer or arrays size!
// System.out.println(ref+" move from "+addr+" to "+heapPtr);
int size = Native.rdMem(ref+OFF_SIZE);
for (int i=0; i<size; ++i) {
int val = Native.rdMem(addr+i);
Native.wrMem(val, heapPtr+i);
}
// update object pointer to the new location
Native.wrMem(heapPtr+1, ref+OFF_PTR);
heapPtr += size;
ref = Native.rdMem(ref+OFF_NEXT);
}
// clean rest of the heap
for (int i=heapPtr; i<heapEnd; ++i) {
Native.wrMem(0, i);
}
// for tests clean also the former from space
for (int i=stackStart; i<stackStart+heap_size; ++i) {
Native.wrMem(0, i);
}
}
static void gc_alloc() {
System.out.println();
System.out.println("GC allocation triggered");
gc();
}
public static void gc() {
System.out.print("GC called, ");
// System.out.print(free());
// System.out.print(" words remaining - now ");
/* disabled for now (BG)
* TODO: get stack roots from threads!
*/
mark();
sweep();
compact();
System.out.print(freeMemory());
System.out.println(" bytes free");
}
static int free() {
return heapEnd-heapPtr;
}
/**
* Allocate a new Object. Invoked from JVM.f_new(cons);
* @param cons pointer to class struct
* @return address of the handle
*/
static int newObject(int cons) {
int size = Native.rdMem(cons); // instance size
// we are NOT using JVM var h at address 2 for the
// heap pointer anymore.
++size; // for the additional method pointer
//System.out.println("new "+heapPtr+" size "+size);
if (heapPtr+size >= heapEnd) {
gc_alloc();
}
if (heapPtr+size >= heapEnd) {
// still not enough memory
System.out.println("Out of memory error!");
System.exit(1);
}
if (freeList==0) {
// System.out.println("Run out of handles!");
// is this a good place to call gc????
// better check available handles on newObject
gc_alloc();
}
if (freeList==0) {
System.out.println("Still out of handles!");
System.exit(1);
}
// we need the object size.
// in the heap or in the handle structure
// or retrive it from the class info
int ref = getHandle(heapPtr+1, size);
// ref. flags used for array marker
Native.wrMem(IS_OBJ, ref+OFF_TYPE);
Native.wrMem(cons+3, heapPtr); // pointer to method table in objectref-1
// zero could be done on a flip and at initialization!
for (int i=1; i<size; ++i) {
Native.wrMem(0, heapPtr+i); // zero object
}
int addr = heapPtr+1;
heapPtr += size;
/*
*
// if (Startup.started) {
JVMHelp.wr("new ");
JVMHelp.wrSmall(addr);
JVMHelp.wrSmall(ref);
JVMHelp.wrSmall(cons);
JVMHelp.wr("\r\n");
// }
*/
return ref;
}
static int newArray(int size, boolean isRef) {
// we are NOT using JVM var h at address 2 for the
// heap pointer anymore.
++size; // for the additional size field
//System.out.println("new "+heapPtr+" size "+size);
if (heapPtr+size >= heapEnd) {
gc_alloc();
}
if (heapPtr+size >= heapEnd) {
// still not enough memory
System.out.println("Out of memory error!");
System.exit(1);
}
if (freeList==0) {
// System.out.println("Run out of handles!");
// is this a good place to call gc????
// better check available handles on newObject
gc_alloc();
}
if (freeList==0) {
System.out.println("Still out of handles!");
System.exit(1);
}
int ref = getHandle(heapPtr+1, size);
// ref. flags used for array marker
if (isRef) {
Native.wrMem(IS_REFARR, ref+OFF_TYPE);
} else {
Native.wrMem(IS_VALARR, ref+OFF_TYPE);
}
// we need the array size.
// in the heap or in the handle structure
Native.wrMem(size-1, heapPtr); // pointer to method table in objectref-1
for (int i=1; i<size; ++i) {
Native.wrMem(0, heapPtr+i); // zero array
}
heapPtr += size;
return ref;
}
// not used in JOP
static int getField(int ref, int off) {
return Native.rdMem(translate(ref)+off);
}
// not used in JOP
static void setField(int ref, int off, int val) {
Native.wrMem(val, translate(ref)+off);
}
/**
* @return
*/
public static int freeMemory() {
return free()*4;
}
/**
* @return
*/
public static int totalMemory() {
return heap_size*4;
}
}