1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import AAA.Derived;
18 
19 public class Main {
main(String[] args)20     public static void main(String[] args) {
21         try {
22             // Allocate memory for the "AAA.Derived" class name before eating memory.
23             String aaaDerivedName = "AAA.Derived";
24             System.out.println("Eating all memory.");
25             // Resolve VMClassLoader before eating all the memory since we can not fail
26             // initialization of boot classpath classes.
27             Class.forName("java.lang.VMClassLoader");
28             Object memory = eatAllMemory();
29 
30             // This test assumes that Derived is not yet resolved. In some configurations
31             // (notably interp-ac), Derived is already resolved by verifying Main at run
32             // time. Therefore we cannot assume that we get a certain `value` and need to
33             // simply check for consistency, i.e. `value == another_value`.
34             int value = 0;
35             try {
36                 // If the ArtMethod* is erroneously left in the DexCache, this
37                 // shall succeed despite the class Derived being unresolved so
38                 // far. Otherwise, we shall throw OOME trying to resolve it.
39                 value = Derived.foo();
40             } catch (OutOfMemoryError e) {
41                 value = -1;
42             }
43             int another_value = 0;
44             try {
45                 // For comparison, try to resolve the class Derived directly.
46                 Class.forName(aaaDerivedName, false, Main.class.getClassLoader());
47                 another_value = 42;
48             } catch (OutOfMemoryError e) {
49                 another_value = -1;
50             }
51             boolean memoryWasAllocated = (memory != null);
52             memory = null;
53             System.out.println("memoryWasAllocated = " + memoryWasAllocated);
54             System.out.println("match: " + (value == another_value));
55             if (value != another_value || (value != -1 && value != 42)) {
56                 // Mismatch or unexpected value, print additional debugging information.
57                 System.out.println("value: " + value);
58                 System.out.println("another_value: " + another_value);
59             }
60         } catch (Throwable t) {
61             t.printStackTrace(System.out);
62         }
63     }
64 
exhaustJavaHeap(Object[] data, int index, int size)65     private static int exhaustJavaHeap(Object[] data, int index, int size) {
66         Runtime.getRuntime().gc();
67         // Let out-of-bound exception be thrown if we go past the array length. This should
68         // never happen if the logic in the caller is right. The exception acts as an assertion.
69         while (size != 0) {
70             try {
71                 data[index] = new byte[size];
72                 ++index;
73             } catch (OutOfMemoryError oome) {
74                 size /= 2;
75             }
76         }
77         return index;
78     }
79 
eatAllMemory()80     public static Object eatAllMemory() {
81       Object[] result = null;
82       int size = 1000000;
83       // Make sure that there is no reclaimable memory in the heap. Otherwise we may throw
84       // OOME to prevent GC thrashing, even if later allocations may succeed.
85       Runtime.getRuntime().gc();
86       System.runFinalization();
87       // NOTE: There is a GC invocation in exhaustJavaHeap. So we don't need one here.
88 
89       while (result == null && size != 0) {
90           try {
91               result = new Object[size];
92           } catch (OutOfMemoryError oome) {
93               size /= 2;
94           }
95       }
96       if (result != null) {
97           int index = 0;
98           // Repeat to ensure there is no space left on the heap.
99           index = exhaustJavaHeap(result, index, size);
100           index = exhaustJavaHeap(result, index, /*size*/ 4);
101           index = exhaustJavaHeap(result, index, /*size*/ 4);
102       }
103       return result;
104   }
105 }
106