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 java.lang.reflect.InvocationHandler;
18 import java.lang.reflect.Method;
19 import java.lang.reflect.Proxy;
20 import java.util.ArrayList;
21 
22 /**
23  * Ensure that one can dispatch without aborting when the heap is full.
24  */
25 public class OOMEOnDispatch implements InvocationHandler {
26 
27     static ArrayList<Object> storage = new ArrayList<>(100000);
28 
exhaustJavaHeap(int size)29     private static void exhaustJavaHeap(int size) {
30       Runtime.getRuntime().gc();
31       while (size > 0) {
32         try {
33           storage.add(new byte[size]);
34         } catch (OutOfMemoryError e) {
35           size = size/2;
36         }
37       }
38     }
39 
main(String[] args)40     public static void main(String[] args) {
41         InvocationHandler handler = new OOMEOnDispatch();
42         OOMEInterface inf = (OOMEInterface)Proxy.newProxyInstance(
43                 OOMEInterface.class.getClassLoader(), new Class[] { OOMEInterface.class },
44                 handler);
45 
46         // Stop the JIT to be sure nothing is running that could be resolving classes or causing
47         // verification.
48         Main.stopJit();
49         Main.waitForCompilation();
50 
51         // Make sure that there is no reclaimable memory in the heap. Otherwise we may throw
52         // OOME to prevent GC thrashing, even if later allocations may succeed.
53         Runtime.getRuntime().gc();
54         System.runFinalization();
55         // NOTE: There is a GC invocation in the exhaustJavaHeap(). So we don't need one here.
56 
57         int initial_size = 1024 * 1024;
58         // Repeat to ensure there is no space left on the heap.
59         exhaustJavaHeap(initial_size);
60         exhaustJavaHeap(/*size*/ 4);
61         exhaustJavaHeap(/*size*/ 4);
62 
63         try {
64             inf.foo();
65             storage.clear();
66             System.out.println("Did not receive OOME!");
67         } catch (OutOfMemoryError oome) {
68             storage.clear();
69             System.out.println("Received OOME");
70         }
71 
72         Main.startJit();
73     }
74 
invoke(Object proxy, Method method, Object[] args)75     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
76         storage.clear();
77         System.out.println("Should not have reached OOMEOnDispatch.invoke!");
78         return null;
79     }
80 }
81 
82 interface OOMEInterface {
foo()83     public void foo();
84 }
85