/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
//import org.jikesrvm.*;
/**
* A test to test java.lang.ref.*.
*/
class ReferenceTest {
public static <T> double checkReferenceArray(Reference<T> [] ra, ReferenceQueue<T> rq) {
// Verify that all references on rq belong to ra and that if so they are cleared
for (Reference<?> r = rq.poll(); r != null; r = rq.poll()) {
int i;
for (i = 0; i < ra.length; i++)
if (ra[i] == r)
break;
if (i == ra.length) return -2.0;
}
// Return fraction of ra array whose references are not cleared
int count = 0;
for (Reference<T> aRa : ra)
if (aRa.get() != null)
count++;
return count / ((double) ra.length);
}
static int microUnitSize = 100; // conservative upper bound for small object
static int allocateUnitSize = 5000;
public static int MBtoUnits(double amt) { // in Mbytes
return (int) ((amt * (1 << 20)) / allocateUnitSize);
}
public static Object[] allocateUnit() {
int t = allocateUnitSize / microUnitSize;
Object[] result = new Object[t];
for (int i = 0; i < t; i++)
result[i] = new byte[microUnitSize - 4]; // 4 bytes of header
return result;
}
static Object dummy;
public static void allocateDiscard(double amt) { // amt in Mb
int rounds = MBtoUnits(amt);
for (int i = 0; i < rounds; i++)
dummy = allocateUnit();
}
private static Object outOfMemoryHandle;
public static void allocateUntilOOM() {
try {
while (true) {
Object[] myArray = new Object[10000];
myArray[0] = outOfMemoryHandle;
outOfMemoryHandle = myArray;
}
} catch (OutOfMemoryError oome) {
outOfMemoryHandle = null;
System.out.println("Caught OutOfMemoryError");
}
}
public static double allocateUntilNextGC() {
int count = 0;
long lastFree = Runtime.getRuntime().freeMemory();
while (true) {
allocateDiscard(0.1);
long curFree = Runtime.getRuntime().freeMemory();
count++;
if (curFree > lastFree)
return (count * 0.1);
lastFree = curFree;
}
}
static final int WEAK = 0;
static final int SOFT = 1;
public static Reference<Object[]> [] allocateReferenceArray(int type, double amt, ReferenceQueue<Object[]> rq) { // amt in Mb
int rounds = MBtoUnits(amt);
@SuppressWarnings("unchecked")
Reference<Object[]> [] ra = new Reference[rounds];
for (int i = 0; i < rounds; i++) {
final Reference<Object[]> reference;
if (type == WEAK) {
reference = new WeakReference<Object[]>(allocateUnit(), rq);
} else if (type == SOFT) {
reference = new SoftReference<Object[]>(allocateUnit(), rq);
} else {
reference = null;
}
ra[i] = reference;
}
return ra;
}
public static double getHeapSize() {
allocateUntilNextGC(); // clear current heap twice
allocateUntilNextGC();
double size = allocateUntilNextGC();
System.out.print("ReferenceTest checking available size of heap (Mb): ");
System.out.println(size);
return size;
}
private static int failCount = 0;
private static final int NO_QUALITY = 0;
private static final int GOOD = 1;
private static final int POOR = 2;
private static void check(String msg, boolean correct, int quality) {
System.out.print(msg + " ");
System.out.print(correct ? "PASS" : "FAIL");
if (correct) {
if (quality == GOOD) System.out.print(" GOOD");
if (quality == POOR) System.out.print(" POOR");
} else {
failCount++;
}
System.out.println();
}
private static void check(String msg, boolean correct) {
check(msg, correct, NO_QUALITY);
}
/**
* Force compilation of each of the methods and report on the size
* of the generated machine code.
*/
public static void main(String[] args) throws Exception {
// ------ Compute initial heap size (inaccuracy subject to compilation) --------
double initialHeapSize = getHeapSize();
// ------ Test weak references -----------
System.out.println();
System.out.println("Checking weak references and reference queue");
ReferenceQueue<Object[]> wrq = new ReferenceQueue<Object[]>();
allocateUntilNextGC();
Reference<Object[]> [] wra = allocateReferenceArray(WEAK, 0.5 * initialHeapSize, wrq);
double weakAvail = checkReferenceArray(wra, wrq);
check("Fraction of weak references before GC still live = " + weakAvail, (weakAvail == 1.0));
allocateDiscard(0.75 * initialHeapSize);
weakAvail = checkReferenceArray(wra, wrq);
check("Fraction of weak references after GC still live = " + weakAvail, (weakAvail == 0.0));
// ------ Test soft references -----------
System.out.println();
System.out.println("Checking soft references and reference queue");
ReferenceQueue<Object[]> srq = new ReferenceQueue<Object[]>();
allocateUntilNextGC();
Reference<Object[]> [] sra = allocateReferenceArray(SOFT, 0.75 * initialHeapSize, srq);
double softAvail = checkReferenceArray(sra, srq);
check("Fraction of soft references before GC still live = " + softAvail, (softAvail == 1.0));
allocateUntilOOM();
softAvail = checkReferenceArray(sra, srq);
check("Fraction of soft references after GC still live = " + softAvail,
(softAvail >= 0.00) && (softAvail <= 0.67),
((softAvail >= 0.33) && (softAvail <= 0.66)) ? GOOD : POOR);
// ------ Finish up -----------
System.out.println();
getHeapSize();
System.out.println();
System.out.print("Overall: ");
System.out.println((failCount == 0) ? "ALL TESTS PASSED" : (failCount + " FAILURES"));
}
}