aboutsummaryrefslogtreecommitdiff
path: root/test/compiler/classUnloading/anonymousClass/TestAnonymousClassUnloading.java
blob: 9ff45492f468b0674619600fb99bbe4ba614d670 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

import sun.hotspot.WhiteBox;
import sun.misc.IOUtils;
import sun.misc.Unsafe;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLConnection;

/*
 * @test TestAnonymousClassUnloading
 * @bug 8054402
 * @summary "Tests unloading of anonymous classes."
 * @library /testlibrary /testlibrary/whitebox
 * @compile TestAnonymousClassUnloading.java
 * @run main ClassFileInstaller TestAnonymousClassUnloading
 *                              sun.hotspot.WhiteBox
 *                              sun.hotspot.WhiteBox$WhiteBoxPermission
 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-BackgroundCompilation TestAnonymousClassUnloading
 */
public class TestAnonymousClassUnloading {
    private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
    private static int COMP_LEVEL_SIMPLE = 1;
    private static int COMP_LEVEL_FULL_OPTIMIZATION = 4;

    /**
     * We override hashCode here to be able to access this implementation
     * via an Object reference (we cannot cast to TestAnonymousClassUnloading).
     */
    @Override
    public int hashCode() {
        return 42;
    }

    /**
     * Does some work by using the anonymousClass.
     * @param anonymousClass Class performing some work (will be unloaded)
     */
    static private void doWork(Class<?> anonymousClass) throws InstantiationException, IllegalAccessException {
        // Create a new instance
        Object anon = anonymousClass.newInstance();
        // We would like to call a method of anonymousClass here but we cannot cast because the class
        // was loaded by a different class loader. One solution would be to use reflection but since
        // we want C2 to implement the call as an IC we call Object::hashCode() here which actually
        // calls anonymousClass::hashCode(). C2 will then implement this call as an IC.
        if (anon.hashCode() != 42) {
            new RuntimeException("Work not done");
        }
    }

    /**
     * Makes sure that method is compiled by forcing compilation if not yet compiled.
     * @param m Method to be checked
     */
    static private void makeSureIsCompiled(Method m) {
        // Make sure background compilation is disabled
        if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) {
            throw new RuntimeException("Background compilation enabled");
        }

        // Check if already compiled
        if (!WHITE_BOX.isMethodCompiled(m)) {
            // If not, try to compile it with C2
            if(!WHITE_BOX.enqueueMethodForCompilation(m, COMP_LEVEL_FULL_OPTIMIZATION)) {
                // C2 compiler not available, try to compile with C1
                WHITE_BOX.enqueueMethodForCompilation(m, COMP_LEVEL_SIMPLE);
            }
            // Because background compilation is disabled, method should now be compiled
            if(!WHITE_BOX.isMethodCompiled(m)) {
                throw new RuntimeException(m + " not compiled");
            }
        }
    }

    /**
     * This test creates stale Klass* metadata referenced by a compiled IC.
     *
     * The following steps are performed:
     * (1) An anonymous version of TestAnonymousClassUnloading is loaded by a custom class loader
     * (2) The method doWork that calls a method of the anonymous class is compiled. The call
     *     is implemented as an IC referencing Klass* metadata of the anonymous class.
     * (3) Unloading of the anonymous class is enforced. The IC now references dead metadata.
     */
    static public void main(String[] args) throws Exception {
        // (1) Load an anonymous version of this class using the corresponding Unsafe method
        URL classUrl = TestAnonymousClassUnloading.class.getResource("TestAnonymousClassUnloading.class");
        URLConnection connection = classUrl.openConnection();

        int length = connection.getContentLength();
        byte[] classBytes = IOUtils.readAllBytes(connection.getInputStream());
        if (length != -1 && classBytes.length != length) {
            throw new IOException("Expected:" + length + ", actual: " + classBytes.length);
        }

        Class<?> anonymousClass = UNSAFE.defineAnonymousClass(TestAnonymousClassUnloading.class, classBytes, null);

        // (2) Make sure all paths of doWork are profiled and compiled
        for (int i = 0; i < 100000; ++i) {
            doWork(anonymousClass);
        }

        // Make sure doWork is compiled now
        Method doWork = TestAnonymousClassUnloading.class.getDeclaredMethod("doWork", Class.class);
        makeSureIsCompiled(doWork);

        // (3) Throw away reference to anonymousClass to allow unloading
        anonymousClass = null;

        // Force garbage collection to trigger unloading of anonymousClass
        // Dead metadata reference to anonymousClass triggers JDK-8054402
        WHITE_BOX.fullGC();
    }
}