blob: 2b57634896bf4ad53d6ba7e7d8a6be49b55987b4 [file] [log] [blame]
/*
* Copyright (C) 2017 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.
*/
import AAA.Derived;
public class Main {
public static void main(String[] args) {
try {
// Allocate memory for the "AAA.Derived" class name before eating memory.
String aaaDerivedName = "AAA.Derived";
System.out.println("Eating all memory.");
// Resolve VMClassLoader before eating all the memory since we can not fail
// initialization of boot classpath classes.
Class.forName("java.lang.VMClassLoader");
Object memory = eatAllMemory();
// This test assumes that Derived is not yet resolved. In some configurations
// (notably interp-ac), Derived is already resolved by verifying Main at run
// time. Therefore we cannot assume that we get a certain `value` and need to
// simply check for consistency, i.e. `value == another_value`.
int value = 0;
try {
// If the ArtMethod* is erroneously left in the DexCache, this
// shall succeed despite the class Derived being unresolved so
// far. Otherwise, we shall throw OOME trying to resolve it.
value = Derived.foo();
} catch (OutOfMemoryError e) {
value = -1;
}
int another_value = 0;
try {
// For comparison, try to resolve the class Derived directly.
Class.forName(aaaDerivedName, false, Main.class.getClassLoader());
another_value = 42;
} catch (OutOfMemoryError e) {
another_value = -1;
}
boolean memoryWasAllocated = (memory != null);
memory = null;
System.out.println("memoryWasAllocated = " + memoryWasAllocated);
System.out.println("match: " + (value == another_value));
if (value != another_value || (value != -1 && value != 42)) {
// Mismatch or unexpected value, print additional debugging information.
System.out.println("value: " + value);
System.out.println("another_value: " + another_value);
}
} catch (Throwable t) {
t.printStackTrace(System.out);
}
}
private static int exhaustJavaHeap(Object[] data, int index, int size) {
Runtime.getRuntime().gc();
// Let out-of-bound exception be thrown if we go past the array length. This should
// never happen if the logic in the caller is right. The exception acts as an assertion.
while (size != 0) {
try {
data[index] = new byte[size];
++index;
} catch (OutOfMemoryError oome) {
size /= 2;
}
}
return index;
}
public static Object eatAllMemory() {
Object[] result = null;
int size = 1000000;
// Make sure that there is no reclaimable memory in the heap. Otherwise we may throw
// OOME to prevent GC thrashing, even if later allocations may succeed.
Runtime.getRuntime().gc();
System.runFinalization();
// NOTE: There is a GC invocation in exhaustJavaHeap. So we don't need one here.
while (result == null && size != 0) {
try {
result = new Object[size];
} catch (OutOfMemoryError oome) {
size /= 2;
}
}
if (result != null) {
int index = 0;
// Repeat to ensure there is no space left on the heap.
index = exhaustJavaHeap(result, index, size);
index = exhaustJavaHeap(result, index, /*size*/ 4);
index = exhaustJavaHeap(result, index, /*size*/ 4);
}
return result;
}
}