1/*
2 * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24#include "precompiled.hpp"
25#include "classfile/classLoaderDataGraph.hpp"
26#include "classfile/systemDictionary.hpp"
27#include "code/codeBehaviours.hpp"
28#include "code/codeCache.hpp"
29#include "code/dependencyContext.hpp"
30#include "gc/shared/gcBehaviours.hpp"
31#include "gc/shared/suspendibleThreadSet.hpp"
32#include "gc/z/zLock.inline.hpp"
33#include "gc/z/zNMethod.hpp"
34#include "gc/z/zOopClosures.hpp"
35#include "gc/z/zStat.hpp"
36#include "gc/z/zUnload.hpp"
37#include "oops/access.inline.hpp"
38
39static const ZStatSubPhase ZSubPhaseConcurrentClassesUnload("Concurrent Classes Unload");
40
41class ZIsUnloadingOopClosure : public OopClosure {
42private:
43 ZPhantomIsAliveObjectClosure _is_alive;
44 bool _is_unloading;
45
46public:
47 ZIsUnloadingOopClosure() :
48 _is_alive(),
49 _is_unloading(false) {}
50
51 virtual void do_oop(oop* p) {
52 const oop o = RawAccess<>::oop_load(p);
53 if (o != NULL && !_is_alive.do_object_b(o)) {
54 _is_unloading = true;
55 }
56 }
57
58 virtual void do_oop(narrowOop* p) {
59 ShouldNotReachHere();
60 }
61
62 bool is_unloading() const {
63 return _is_unloading;
64 }
65};
66
67class ZIsUnloadingBehaviour : public IsUnloadingBehaviour {
68public:
69 virtual bool is_unloading(CompiledMethod* method) const {
70 nmethod* const nm = method->as_nmethod();
71 ZReentrantLock* const lock = ZNMethod::lock_for_nmethod(nm);
72 ZLocker<ZReentrantLock> locker(lock);
73 ZIsUnloadingOopClosure cl;
74 ZNMethod::nmethod_oops_do(nm, &cl);
75 return cl.is_unloading();
76 }
77};
78
79class ZCompiledICProtectionBehaviour : public CompiledICProtectionBehaviour {
80public:
81 virtual bool lock(CompiledMethod* method) {
82 nmethod* const nm = method->as_nmethod();
83 ZReentrantLock* const lock = ZNMethod::lock_for_nmethod(nm);
84 lock->lock();
85 return true;
86 }
87
88 virtual void unlock(CompiledMethod* method) {
89 nmethod* const nm = method->as_nmethod();
90 ZReentrantLock* const lock = ZNMethod::lock_for_nmethod(nm);
91 lock->unlock();
92 }
93
94 virtual bool is_safe(CompiledMethod* method) {
95 if (SafepointSynchronize::is_at_safepoint()) {
96 return true;
97 }
98
99 nmethod* const nm = method->as_nmethod();
100 ZReentrantLock* const lock = ZNMethod::lock_for_nmethod(nm);
101 return lock->is_owned();
102 }
103};
104
105ZUnload::ZUnload(ZWorkers* workers) :
106 _workers(workers) {
107
108 if (!ClassUnloading) {
109 return;
110 }
111
112 static ZIsUnloadingBehaviour is_unloading_behaviour;
113 IsUnloadingBehaviour::set_current(&is_unloading_behaviour);
114
115 static ZCompiledICProtectionBehaviour ic_protection_behaviour;
116 CompiledICProtectionBehaviour::set_current(&ic_protection_behaviour);
117}
118
119void ZUnload::prepare() {
120 if (!ClassUnloading) {
121 return;
122 }
123
124 CodeCache::increment_unloading_cycle();
125 DependencyContext::cleaning_start();
126}
127
128void ZUnload::unlink() {
129 SuspendibleThreadSetJoiner sts;
130 bool unloading_occurred;
131
132 {
133 MutexLocker ml(ClassLoaderDataGraph_lock);
134 unloading_occurred = SystemDictionary::do_unloading(ZStatPhase::timer());
135 }
136
137 Klass::clean_weak_klass_links(unloading_occurred);
138
139 ZNMethod::unlink(_workers, unloading_occurred);
140
141 DependencyContext::cleaning_end();
142}
143
144void ZUnload::purge() {
145 {
146 SuspendibleThreadSetJoiner sts;
147 ZNMethod::purge(_workers);
148 }
149
150 ClassLoaderDataGraph::purge();
151 CodeCache::purge_exception_caches();
152}
153
154class ZUnloadRendezvousClosure : public ThreadClosure {
155public:
156 void do_thread(Thread* thread) {}
157};
158
159void ZUnload::unload() {
160 if (!ClassUnloading) {
161 return;
162 }
163
164 ZStatTimer timer(ZSubPhaseConcurrentClassesUnload);
165
166 // Unlink stale metadata and nmethods
167 unlink();
168
169 // Make sure stale metadata and nmethods are no longer observable
170 ZUnloadRendezvousClosure cl;
171 Handshake::execute(&cl);
172
173 // Purge stale metadata and nmethods that were unlinked
174 purge();
175}
176
177void ZUnload::finish() {
178 // Resize and verify metaspace
179 MetaspaceGC::compute_new_size();
180 MetaspaceUtils::verify_metrics();
181}
182