1 /*
2  * Copyright (C) 2009 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 java.lang.reflect.InvocationTargetException;
18 import java.lang.reflect.Method;
19 
20 public class Main {
21     static class ArrayMemEater {
22         static boolean sawOome;
23 
blowup(char[][] holder)24         static void blowup(char[][] holder) {
25             try {
26                 for (int i = 0; i < holder.length; ++i) {
27                     holder[i] = new char[1024 * 1024];
28                 }
29             } catch (OutOfMemoryError oome) {
30                 ArrayMemEater.sawOome = true;
31             }
32         }
33     }
34 
35     static class InstanceMemEater {
36         static boolean sawOome;
37         static InstanceMemEater hook;
38 
39         InstanceMemEater next;
40         double d1, d2, d3, d4, d5, d6, d7, d8; // Bloat this object so we fill the heap faster.
41 
allocate()42         static InstanceMemEater allocate() {
43             try {
44                 return new InstanceMemEater();
45             } catch (OutOfMemoryError e) {
46                 InstanceMemEater.sawOome = true;
47                 return null;
48             }
49         }
50 
confuseCompilerOptimization(InstanceMemEater instance)51         static void confuseCompilerOptimization(InstanceMemEater instance) {
52           hook = instance;
53         }
54     }
55 
exhaustJavaHeap(Object[] data, int index, int size)56     private static int exhaustJavaHeap(Object[] data, int index, int size) {
57         Runtime.getRuntime().gc();
58         while (index != data.length && size != 0) {
59             try {
60                 data[index] = new byte[size];
61                 ++index;
62             } catch (OutOfMemoryError oome) {
63                 size /= 2;
64             }
65         }
66         return index;
67     }
68 
eatAllMemory()69     public static Object eatAllMemory() {
70         Object[] result = null;
71         int size = 1000000;
72         // Make sure that there is no reclaimable memory in the heap. Otherwise we may throw
73         // OOME to prevent GC thrashing, even if later allocations may succeed.
74         Runtime.getRuntime().gc();
75         System.runFinalization();
76         // NOTE: There is a GC invocation in exhaustJavaHeap. So we don't need one here.
77 
78         while (result == null && size != 0) {
79             try {
80                 result = new Object[size];
81             } catch (OutOfMemoryError oome) {
82                 size /= 2;
83             }
84         }
85         if (result != null) {
86             int index = 0;
87             // Repeat to ensure there is no space left on the heap.
88             index = exhaustJavaHeap(result, index, size);
89             index = exhaustJavaHeap(result, index, /*size*/ 4);
90             index = exhaustJavaHeap(result, index, /*size*/ 4);
91         }
92         return result;
93     }
94 
triggerArrayOOM()95     static boolean triggerArrayOOM() {
96         ArrayMemEater.blowup(new char[128 * 1024][]);
97         return ArrayMemEater.sawOome;
98     }
99 
triggerInstanceOOM()100     static boolean triggerInstanceOOM() {
101         InstanceMemEater memEater = InstanceMemEater.allocate();
102         InstanceMemEater lastMemEater = memEater;
103         do {
104             lastMemEater.next = InstanceMemEater.allocate();
105             lastMemEater = lastMemEater.next;
106         } while (lastMemEater != null);
107         memEater.confuseCompilerOptimization(memEater);
108         InstanceMemEater.hook = null;
109         return InstanceMemEater.sawOome;
110     }
111 
main(String[] args)112     public static void main(String[] args) {
113         if (triggerReflectionOOM()) {
114             System.out.println("Test reflection correctly threw");
115         }
116         if (triggerReflectionOOM2()) {
117             System.out.println("Test reflection2 correctly threw");
118         }
119 
120         if (triggerArrayOOM()) {
121             System.out.println("NEW_ARRAY correctly threw OOME");
122         }
123 
124         if (triggerInstanceOOM()) {
125             System.out.println("NEW_INSTANCE correctly threw OOME");
126         }
127     }
128 
129     static Object[] holder;
130 
blowup()131     public static void blowup() throws Exception {
132         int size = 2 * 1024 * 1024;
133         for (int i = 0; i < holder.length; ) {
134             try {
135                 holder[i] = new char[size];
136                 i++;
137             } catch (OutOfMemoryError oome) {
138                 size = size / 16;
139                 if (size == 0) {
140                      break;
141                 }
142             }
143         }
144         holder[0] = new char[100000];
145     }
146 
triggerReflectionOOM()147     static boolean triggerReflectionOOM() {
148         try {
149             Class<?> c = Main.class;
150             Method m = c.getMethod("blowup");
151             holder = new Object[1000000];
152             m.invoke(null);
153             holder = null;
154             System.out.println("Didn't throw from blowup");
155         } catch (OutOfMemoryError e) {
156             holder = null;
157         } catch (InvocationTargetException e) {
158             holder = null;
159             if (!(e.getCause() instanceof OutOfMemoryError)) {
160                 System.out.println("InvocationTargetException cause not OOME " + e.getCause());
161                 return false;
162             }
163         } catch (Exception e) {
164             holder = null;
165             System.out.println("Unexpected exception " + e);
166             return false;
167         }
168         return true;
169     }
170 
triggerReflectionOOM2()171     static boolean triggerReflectionOOM2() {
172         Object memory = eatAllMemory();
173         boolean result = false;
174         try {
175             Main.class.getDeclaredMethods();
176         } catch (OutOfMemoryError e) {
177             result = true;
178         }
179         if (!result) {
180             boolean memoryWasAllocated = (memory != null);
181             memory = null;
182             System.out.println("memoryWasAllocated = " + memoryWasAllocated);
183         }
184         return result;
185     }
186 }
187