diff --git a/app/src/main/java/ch/nuth/zhaw/exbox/CObject.java b/app/src/main/java/ch/nuth/zhaw/exbox/CObject.java new file mode 100644 index 0000000..1fb7f78 --- /dev/null +++ b/app/src/main/java/ch/nuth/zhaw/exbox/CObject.java @@ -0,0 +1,29 @@ +package ch.nuth.zhaw.exbox; + +/** base class of collectable objects */ +public class CObject implements Collectable { + private boolean mark; // to mark object + public CObject next, down; + public String value; + + public CObject (String value) { + this.value = value; + } + + public void setMark(boolean mark) { + this.mark = mark; + } + + public boolean isMarked() { + return mark; + } + + public String toString() { + return value; + } + + @Override + public boolean equals(Object o) { + return this.getClass() == o.getClass() && this.value.equals(((CObject)o).value); + } +} diff --git a/app/src/main/java/ch/nuth/zhaw/exbox/Collectable.java b/app/src/main/java/ch/nuth/zhaw/exbox/Collectable.java new file mode 100644 index 0000000..944a959 --- /dev/null +++ b/app/src/main/java/ch/nuth/zhaw/exbox/Collectable.java @@ -0,0 +1,6 @@ +package ch.nuth.zhaw.exbox; + +public interface Collectable { + void setMark(boolean b); + boolean isMarked(); +} diff --git a/app/src/main/java/ch/nuth/zhaw/exbox/Storage.java b/app/src/main/java/ch/nuth/zhaw/exbox/Storage.java new file mode 100644 index 0000000..46a0cf0 --- /dev/null +++ b/app/src/main/java/ch/nuth/zhaw/exbox/Storage.java @@ -0,0 +1,188 @@ +package ch.nuth.zhaw.exbox; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +public class Storage { + public static boolean generationalGCActive = true; // in Aufgabe 2 verwendet + public static StringBuffer log = new StringBuffer(); + private static List root; + private static List youngHeap; + private static List oldHeap; + public static boolean youngGenerationOnly = true; + + static { + clear(); + } + + public static void clear() { + root = new LinkedList<>(); + youngHeap = new LinkedList<>(); + // oldHeap erst in Aufgabe 2 verwenden! + oldHeap = new LinkedList<>(); + } + + /* add root object */ + public static void addRoot(Collectable obj) { + root.add(obj); + } + + // create a collectable object of class cls + public static Collectable _new(String cls, Object arg) { + Collectable obj = null; + try { + // create an object and call constructor + Constructor cst = Class.forName(getPackage() + cls).getConstructor(arg.getClass()); + obj = (Collectable) cst.newInstance(new Object[] { arg }); + // add object to heap + youngHeap.add(obj); + log.append("New: ").append(obj).append("\n"); + } catch (Exception ex) { + log.append("error creating object ").append(cls).append("\n"); + } + return obj; + } + + private static String getPackage() { + Package pack = Storage.class.getPackage(); + if (pack != null && !pack.getName().equals("")) { + return pack.getName() + "."; + } else { + return ""; + } + } + + /* remove object from heap */ + public static void delete(Collectable obj) { + if (youngHeap.remove(obj)) { + if (generationalGCActive) { + log.append("Delete young heap: ").append(obj).append("\n"); + } else { + log.append("Delete heap: ").append(obj).append("\n"); + } + } else if (oldHeap.remove(obj)) { + log.append("Delete old heap: ").append(obj).append("\n"); + } else { + log.append("error trying to delete not existing object ").append(obj).append("\n"); + } + } + + /* get all root objects */ + public static Iterable getRoot() { + return new LinkedList<>(root); + } + + /* get young heap */ + public static Iterable getYoungHeap() { + return new LinkedList<>(youngHeap); + } + + /* get old heap */ + public static Iterable getOldHeap() { + return new LinkedList<>(oldHeap); + } + + /* get heap */ + public static Iterable getHeap() { + return new LinkedList<>(youngHeap); + } + + /* get references to collectables of an object */ + public static Iterable getRefs(Collectable obj) { + // get all fields of an object + List fieldList = new LinkedList<>(); + for (Field field : obj.getClass().getFields()) { + try { + Object o = field.get(obj); + if (o instanceof Collectable) { + fieldList.add((Collectable) o); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + return fieldList; + } + + /* dump an iterator */ + public static void dump(String s, Iterable itr) { + log.append(s); + for (Collectable o : itr) { + log.append(" ").append(o); + } + log.append("\n"); + } + + public static String getLog() { + return log.toString(); + } + + private static void mark(Collectable cObject) { + // TODO Aufgabe 13.1 + if (!cObject.isMarked()) { + cObject.setMark(true); + + for (Collectable item : getRefs(cObject)) { + mark(item); + } + } + } + + private static void sweep() { + // TODO Aufgabe 13.1 und Aufgabe 13.2 + Map, Consumer> heaps = new HashMap<>(); + Consumer noOp = (item) -> {}; + + Consumer copy = (item) -> { + youngHeap.remove(item); + oldHeap.add(item); + }; + + heaps.put(getYoungHeap(), generationalGCActive ? copy : noOp); + + if (!youngGenerationOnly) { + heaps.put(getOldHeap(), noOp); + } + + for (Map.Entry, Consumer> entry : heaps.entrySet()) { + for (Collectable item : entry.getKey()) { + if (!item.isMarked()) { + delete(item); + } else { + item.setMark(false); + entry.getValue().accept(item); + } + } + } + } + + public static void gc() { + if (generationalGCActive) { + if (youngGenerationOnly) { + log.append("\nCollector start young generation only\n"); + } else { + log.append("\nCollector start young and old generation\n"); + } + } else { + log.append("\nCollector start\n"); + } + + // TODO Aufgabe 13.1 und Aufgabe 13.2 + for (Collectable item : getRoot()) { + mark(item); + } + + sweep(); + + if (generationalGCActive) { + youngGenerationOnly = !youngGenerationOnly; + } + + log.append("Collector end\n"); + } +} diff --git a/app/src/main/java/ch/nuth/zhaw/exbox/TestCObjectServer.java b/app/src/main/java/ch/nuth/zhaw/exbox/TestCObjectServer.java new file mode 100644 index 0000000..d601b11 --- /dev/null +++ b/app/src/main/java/ch/nuth/zhaw/exbox/TestCObjectServer.java @@ -0,0 +1,66 @@ +package ch.nuth.zhaw.exbox; + +public class TestCObjectServer implements CommandExecutor { + private static CObject new_CObject(Object s) { + return (CObject) Storage._new("CObject", s); + } + + static CObject a; + static CObject e; + + public String execute (String input) { + run(); + return Storage.getLog(); + } + + private void run() { + a = new_CObject("A"); + CObject b = new_CObject("B"); + CObject c = new_CObject("C"); + CObject d = new_CObject("D"); + e = new_CObject("E"); + CObject f = new_CObject("F"); + CObject g = new_CObject("G"); + Storage.addRoot(a); + Storage.addRoot(e); + a.next = b; b.next = c; b.down = a; c.down = d; + e.next = f; f.next = g; g.next = e; + Storage.dump("\nRoots:", Storage.getRoot()); + if (Storage.generationalGCActive) { + Storage.dump("\nYoung heap 1:", Storage.getYoungHeap()); + Storage.dump("Old heap 1:", Storage.getOldHeap()); + } else { + Storage.dump("Heap 1:", Storage.getYoungHeap()); + } + Storage.gc(); + if (Storage.generationalGCActive) { + Storage.dump("Young heap 2:", Storage.getYoungHeap()); + Storage.dump("Old heap 2:", Storage.getOldHeap()); + } else { + Storage.dump("Heap 2:", Storage.getYoungHeap()); + } + b.next = f; + Storage.gc(); + if (Storage.generationalGCActive) { + Storage.dump("Young heap 3:", Storage.getYoungHeap()); + Storage.dump("Old heap 3:", Storage.getOldHeap()); + } else { + Storage.dump("Heap 3:", Storage.getYoungHeap()); + } + f.next = null; + Storage.gc(); + if (Storage.generationalGCActive) { + Storage.dump("Young heap 4:", Storage.getYoungHeap()); + Storage.dump("Old heap 4:", Storage.getOldHeap()); + } else { + Storage.dump("Heap 4:", Storage.getYoungHeap()); + } + Storage.gc(); + if (Storage.generationalGCActive) { + Storage.dump("Young heap 5:", Storage.getYoungHeap()); + Storage.dump("Old heap 5:", Storage.getOldHeap()); + } else { + Storage.dump("Heap 5:", Storage.getYoungHeap()); + } + } +} diff --git a/app/src/test/java/ch/nuth/zhaw/exbox/ADS13_1_test.java b/app/src/test/java/ch/nuth/zhaw/exbox/ADS13_1_test.java new file mode 100644 index 0000000..9745fd5 --- /dev/null +++ b/app/src/test/java/ch/nuth/zhaw/exbox/ADS13_1_test.java @@ -0,0 +1,92 @@ +package ch.nuth.zhaw.exbox; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ADS13_1_test { + private CObject new_CObject(Object s) { + return (CObject) Storage._new("CObject", s); + } + + static CObject a, d; + CObject b, c, e, f, g; + + @BeforeEach + public void setUp() { + Storage.generationalGCActive = false; + Storage.clear(); + newObjects(); + } + + private void testContent(String message, Iterable content, String expected) { + StringBuilder b = new StringBuilder(); + content.forEach(b::append); + assertEquals(expected, b.toString(), message); + } + + private void newObjects() { + a = new_CObject("A"); + b = new_CObject("B"); + c = new_CObject("C"); + d = new_CObject("D"); + e = new_CObject("E"); + f = new_CObject("F"); + g = new_CObject("G"); + Storage.addRoot(a); + Storage.addRoot(d); + a.next = b; + b.next = c; + b.down = a; + c.down = d; + d.next = e; + e.next = f; + f.next = g; + } + + @Test + public void testRoot() { + testContent("ROOT", Storage.getRoot(), "AD"); + } + + @Test + public void testInitialHeap() { + testContent("HEAP1", Storage.getHeap(), "ABCDEFG"); + } + + @Test + public void testAfterFirstGC() { + Storage.gc(); + testContent("HEAP2", Storage.getHeap(), "ABCDEFG"); + } + + @Test + public void testAfterFirstChangeGC() { + Storage.gc(); + e.next = d; + Storage.gc(); + testContent("HEAP3", Storage.getHeap(), "ABCDE"); + } + + @Test + public void testAfterSecondChangeGC() { + Storage.gc(); + e.next = d; + Storage.gc(); + a.next = null; + Storage.gc(); + testContent("HEAP4", Storage.getHeap(), "ADE"); + } + + @Test + public void testFinalHeap() { + Storage.gc(); + e.next = d; + Storage.gc(); + a.next = null; + Storage.gc(); + Storage.gc(); + testContent("HEAP5", Storage.getHeap(), "ADE"); + } +} diff --git a/app/src/test/java/ch/nuth/zhaw/exbox/ADS13_3_test.java b/app/src/test/java/ch/nuth/zhaw/exbox/ADS13_3_test.java new file mode 100644 index 0000000..f86cf10 --- /dev/null +++ b/app/src/test/java/ch/nuth/zhaw/exbox/ADS13_3_test.java @@ -0,0 +1,130 @@ +package ch.nuth.zhaw.exbox; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ADS13_3_test { + private CObject new_CObject(Object s) { + return (CObject) Storage._new("CObject", s); + } + + static CObject a, d; + CObject b, c, e, f, g, h; + + @BeforeEach + public void setUp() { + Storage.generationalGCActive = true; + Storage.youngGenerationOnly = true; + Storage.clear(); + newObjects(); + } + + private void testContent(String message, Iterable content, String expected) { + StringBuilder b = new StringBuilder(); + content.forEach(b::append); + assertEquals(expected, b.toString(), message); + } + + private void newObjects() { + a = new_CObject("A"); + b = new_CObject("B"); + c = new_CObject("C"); + d = new_CObject("D"); + e = new_CObject("E"); + f = new_CObject("F"); + g = new_CObject("G"); + Storage.addRoot(a); + Storage.addRoot(d); + a.next = b; + b.next = c; + b.down = a; + c.down = d; + d.next = e; + e.next = f; + f.next = g; + } + + @Test + public void testRoot() { + testContent("ROOT", Storage.getRoot(), "AD"); + } + + @Test + public void testInitialHeap() { + testContent("YOUNG HEAP1", Storage.getYoungHeap(), "ABCDEFG"); + testContent("OLD HEAP1", Storage.getOldHeap(), ""); } + + @Test + public void testAfterFirstGC() { + Storage.gc(); + Storage.getYoungHeap(); + Storage.getOldHeap(); + testContent("YOUNG HEAP2", Storage.getYoungHeap(), ""); + testContent("OLD HEAP2", Storage.getOldHeap(), "ABCDEFG"); + } + + @Test + public void testAfterFirstChangeGC() { + Storage.gc(); + e.next = d; + Storage.gc(); + testContent("YOUNG HEAP3", Storage.getYoungHeap(), ""); + testContent("OLD HEAP3", Storage.getOldHeap(), "ABCDE"); + } + + @Test + public void testAfterSecondChangeGC() { + Storage.gc(); + e.next = d; + Storage.gc(); + a.next = null; + Storage.gc(); + testContent("YOUNG HEAP4", Storage.getYoungHeap(), ""); + testContent("OLD HEAP4", Storage.getOldHeap(), "ABCDE"); + } + + @Test + public void testAfterSecondCallGC() { + Storage.gc(); + e.next = d; + Storage.gc(); + a.next = null; + Storage.gc(); + Storage.gc(); + testContent("YOUNG HEAP5", Storage.getYoungHeap(), ""); + testContent("OLD HEAP5", Storage.getOldHeap(), "ADE"); + } + + @Test + public void testAfterAddObjecthGC() { + Storage.gc(); + e.next = d; + Storage.gc(); + a.next = null; + Storage.gc(); + Storage.gc(); + h = new_CObject("H"); + a.next = h; + testContent("YOUNG HEAP5", Storage.getYoungHeap(), "H"); + testContent("OLD HEAP5", Storage.getOldHeap(), "ADE"); + } + + @Test + public void testFinalHeap() { + Storage.gc(); + e.next = d; + Storage.gc(); + a.next = null; + Storage.gc(); + Storage.gc(); + h = new_CObject("H"); + a.next = h; + testContent("YOUNG HEAP5", Storage.getYoungHeap(), "H"); + testContent("OLD HEAP5", Storage.getOldHeap(), "ADE"); + Storage.gc(); + testContent("YOUNG HEAP5", Storage.getYoungHeap(), ""); + testContent("OLD HEAP5", Storage.getOldHeap(), "ADEH"); + } +}