xref: /aosp_15_r20/art/test/080-oom-throw/src/Main.java (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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) {
59             try {
60                 data[index] = new byte[size];
61                 ++index;
62             } catch (OutOfMemoryError oome) {
63                 // Rapidly shrink the object size to fill any remaining space.
64                 // Use few different sizes, since detecting out-of-memory is slow.
65                 if (size >= 32) {
66                     size /= 32;
67                 } else if (size > 1) {
68                     size = 1;
69                 } else {
70                     break;
71                 }
72             }
73         }
74         return index;
75     }
76 
eatAllMemory()77     public static Object eatAllMemory() {
78         Object[] result = null;
79         int size = 1000000;
80         // Make sure that there is no reclaimable memory in the heap. Otherwise we may throw
81         // OOME to prevent GC thrashing, even if later allocations may succeed.
82         Runtime.getRuntime().gc();
83         System.runFinalization();
84         // NOTE: There is a GC invocation in exhaustJavaHeap. So we don't need one here.
85 
86         while (result == null && size != 0) {
87             try {
88                 result = new Object[size];
89             } catch (OutOfMemoryError oome) {
90                 size /= 2;
91             }
92         }
93         if (result != null) {
94             int index = 0;
95             // Repeat to ensure there is no space left on the heap.
96             index = exhaustJavaHeap(result, index, size);
97             index = exhaustJavaHeap(result, index, /*size*/ 4);
98             index = exhaustJavaHeap(result, index, /*size*/ 4);
99         }
100         return result;
101     }
102 
triggerArrayOOM()103     static boolean triggerArrayOOM() {
104         ArrayMemEater.blowup(new char[128 * 1024][]);
105         return ArrayMemEater.sawOome;
106     }
107 
triggerInstanceOOM()108     static boolean triggerInstanceOOM() {
109         InstanceMemEater memEater = InstanceMemEater.allocate();
110         InstanceMemEater lastMemEater = memEater;
111         do {
112             lastMemEater.next = InstanceMemEater.allocate();
113             lastMemEater = lastMemEater.next;
114         } while (lastMemEater != null);
115         memEater.confuseCompilerOptimization(memEater);
116         InstanceMemEater.hook = null;
117         return InstanceMemEater.sawOome;
118     }
119 
main(String[] args)120     public static void main(String[] args) {
121         if (triggerReflectionOOM()) {
122             System.out.println("Test reflection correctly threw");
123         }
124         if (triggerReflectionOOM2()) {
125             System.out.println("Test reflection2 correctly threw");
126         }
127 
128         if (triggerArrayOOM()) {
129             System.out.println("NEW_ARRAY correctly threw OOME");
130         }
131 
132         if (triggerInstanceOOM()) {
133             System.out.println("NEW_INSTANCE correctly threw OOME");
134         }
135     }
136 
137     static Object[] holder;
138 
blowup()139     public static void blowup() throws Exception {
140         int size = 2 * 1024 * 1024;
141         for (int i = 0; i < holder.length; ) {
142             try {
143                 holder[i] = new char[size];
144                 i++;
145             } catch (OutOfMemoryError oome) {
146                 size = size / 16;
147                 if (size == 0) {
148                      break;
149                 }
150             }
151         }
152         holder[0] = new char[100000];
153     }
154 
triggerReflectionOOM()155     static boolean triggerReflectionOOM() {
156         try {
157             Class<?> c = Main.class;
158             Method m = c.getMethod("blowup");
159             holder = new Object[1000000];
160             m.invoke(null);
161             holder = null;
162             System.out.println("Didn't throw from blowup");
163         } catch (OutOfMemoryError e) {
164             holder = null;
165         } catch (InvocationTargetException e) {
166             holder = null;
167             if (!(e.getCause() instanceof OutOfMemoryError)) {
168                 System.out.println("InvocationTargetException cause not OOME " + e.getCause());
169                 return false;
170             }
171         } catch (Exception e) {
172             holder = null;
173             System.out.println("Unexpected exception " + e);
174             return false;
175         }
176         return true;
177     }
178 
triggerReflectionOOM2()179     static boolean triggerReflectionOOM2() {
180         Object memory = eatAllMemory();
181         boolean result = false;
182         try {
183             Main.class.getDeclaredMethods();
184         } catch (OutOfMemoryError e) {
185             result = true;
186         }
187         if (!result) {
188             boolean memoryWasAllocated = (memory != null);
189             memory = null;
190             System.out.println("memoryWasAllocated = " + memoryWasAllocated);
191         }
192         return result;
193     }
194 }
195