目录
一、ClassLoaderData
1、Dependencies
2、ChunkedHandleList
3、JNIMethodBlock
4、init_null_class_loader_data / init_dependencies
5、构造和析构函数
6、record_dependency
7、add_to_deallocate_list /free_deallocate_list
8、metaspace_non_null
9、add_class / remove_class
二、ClassLoaderDataGraph
1、定义
2、find_or_create
3、purge
4、do_unloading
在《Hotspot 内存管理之Metaspace(三) 源码解析》中讲解了Metaspace的初始化和内存管理等接口,总结了Metaspace相关底层类的数据结构和调用关系,本篇博客讲解跟Metaspace直接相关的负责初始化Metaspace实例的ClassLoaderData的相关类的实现。
ClassLoaderData负责初始化并销毁一个ClassLoader实例对应的Metaspace,是GC的根节点,提供引用遍历的迭代器。ClassLoaderData的定义在hotspot src/share/vm/classfile/classLoaderData.hpp中,包含的属性如下:
Dependencies是ClassLoaderData的内部类,表示引用此ClassLoaderDat的对象,特殊场景下使用,这些引用对象无法通过GC遍历到,其定义如下:
其init和add方法的源码实现如下:
void ClassLoaderData::Dependencies::init(TRAPS) {
//创建一个长度为2的Ojbect数组
_list_head = oopFactory::new_objectArray(2, CHECK);
}
void ClassLoaderData::Dependencies::add(Handle dependency, TRAPS) {
objArrayOop ok = _list_head;
objArrayOop last = NULL;
//不断遍历,如果已经在列表中则返回
while (ok != NULL) {
last = ok;
//第一个元素是obj
if (ok->obj_at(0) == dependency()) {
// Don't need to add it
return;
}
//第二个元素是一个obj数组
ok = (objArrayOop)ok->obj_at(1);
}
//last不能为空
assert (last != NULL, "dependencies should be initialized");
objArrayHandle last_handle(THREAD, last);
//创建一个新的数组,将目标dependency放到索引为0的位置
objArrayOop deps = oopFactory::new_objectArray(2, CHECK);
deps->obj_at_put(0, dependency());
// Must handle over GC points
objArrayHandle new_dependency(THREAD, deps);
locked_add(last_handle, new_dependency, THREAD);
}
void ClassLoaderData::Dependencies::locked_add(objArrayHandle last_handle,
objArrayHandle new_dependency,
Thread* THREAD) {
//获取对象锁,从而安全的添加新的依赖Obj
ObjectLocker ol(Handle(THREAD, _list_head), THREAD);
oop loader_or_mirror = new_dependency->obj_at(0);
// Since the dependencies are only added, add to the end.
objArrayOop end = last_handle();
objArrayOop last = NULL;
while (end != NULL) {
last = end;
//再检查一遍,可能其他线程已经完成添加了
if (end->obj_at(0) == loader_or_mirror) {
// Don't need to add it
return;
}
end = (objArrayOop)end->obj_at(1);
}
assert (last != NULL, "dependencies should be initialized");
//如果last本身是一个空数组则添加到索引为0的位置,否则放到索引为1的位置
if (last->obj_at(0) == NULL) {
last->obj_at_put(0, new_dependency->obj_at(0));
} else {
last->obj_at_put(1, new_dependency());
}
}
_list_head属性是一个长度为2的Obj数组,第一个元素是依赖的Obj,第二个元素是下一个依赖的obj数组的引用,下一个依赖的obj数组同样是2个元素的obj数组,如此便构成了一个依赖的obj的链表。添加的时候首先判断目标obj是否在链表中,如果不在则添加到末尾,添加的时候需要获取_list_head属性的对象锁。其中add方法的调用链如下:
ChunkedHandleList也是ClassLoaderData的内部类,其定义如下:
一个Chunk实际就是一个固定容量的oop数组,size表示该数组已使用的元素个数,next表示上一个已经被占满的Chunk。上述析构方法和add方法的实现如下:
ClassLoaderData::ChunkedHandleList::~ChunkedHandleList() {
Chunk* c = _head;
while (c != NULL) {
Chunk* next = c->_next;
delete c;
c = next;
}
}
oop* ClassLoaderData::ChunkedHandleList::add(oop o) {
//_head为NULL或者已满,则创建一个新的Chunk
if (_head == NULL || _head->_size == Chunk::CAPACITY) {
Chunk* next = new Chunk(_head);
//原子的更新_head属性,将新创建的next作为_head
OrderAccess::release_store_ptr(&_head, next);
}
//保存oop
oop* handle = &_head->_data[_head->_size];
*handle = o;
//增加size
OrderAccess::release_store(&_head->_size, _head->_size + 1);
return handle;
}
其中add方法的调用链如下:
JNIMethodBlock用来生成jmethodID的,每个jmethodID实际是一个Method**。JNIMethodBlock的定义如下:
其结构跟ChunkedHandleList的Chunk类似,top属性表示已经被分配的数组元素个数,next表示下一个可以分配的JNIMethodBlock,其add_method方法的实现如下:
Method** add_method(Method* m) {
//如果未分配完
if (_top < number_of_methods) {
// top points to the next free entry.
int i = _top;
_methods[i] = m;
_top++;
return &_methods[i];
} else if (_top == number_of_methods) {
//如果已分配完,判断是否存在空闲的元素
for (int i = 0; i< number_of_methods; i++) {
if (_methods[i] == _free_method) {
//使用空闲的元素分配
_methods[i] = m;
return &_methods[i];
}
}
_top++;
}
//没有空闲的,需要分配一个新的JNIMethodBlock
if (_next == NULL) {
_next = new JNIMethodBlock();
}
//使用next添加新的Method
return _next->add_method(m);
}
ClassLoaderData中设置_jmethod_ids属性的只有一个方法,set_jmethod_ids,该方法的调用链如下:
make_jmethod_id方法的实现如下:
生成的jmethodID实际就是add_method方法返回的Method**。
这两个方法都是用来初始化启动类加载器对应的_the_null_class_loader_data属性,都是在Universe初始化的时候调用的,其调用链如下:
两方法的源码如下:
static void init_null_class_loader_data() {
assert(_the_null_class_loader_data == NULL, "cannot initialize twice");
assert(ClassLoaderDataGraph::_head == NULL, "cannot initialize twice");
//初始化_the_null_class_loader_data
_the_null_class_loader_data = new ClassLoaderData((oop)NULL, false, Dependencies());
ClassLoaderDataGraph::_head = _the_null_class_loader_data;
assert(_the_null_class_loader_data->is_the_null_class_loader_data(), "Must be");
//DumpSharedSpaces默认为false
if (DumpSharedSpaces) {
_the_null_class_loader_data->initialize_shared_metaspaces();
}
}
bool is_the_null_class_loader_data() const {
return this == _the_null_class_loader_data;
}
void ClassLoaderData::initialize_shared_metaspaces() {
assert(DumpSharedSpaces, "only use this for dumping shared spaces");
assert(this == ClassLoaderData::the_null_class_loader_data(),
"only supported for null loader data for now");
assert (!_shared_metaspaces_initialized, "only initialize once");
MutexLockerEx ml(metaspace_lock(), Mutex::_no_safepoint_check_flag);
_ro_metaspace = new Metaspace(_metaspace_lock, Metaspace::ROMetaspaceType);
_rw_metaspace = new Metaspace(_metaspace_lock, Metaspace::ReadWriteMetaspaceType);
_shared_metaspaces_initialized = true;
}
void ClassLoaderData::init_dependencies(TRAPS) {
assert(!Universe::is_fully_initialized(), "should only be called when initializing");
assert(is_the_null_class_loader_data(), "should only call this for the null class loader");
_dependencies.init(CHECK);
}
两方法的源码如下:
ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool is_anonymous, Dependencies dependencies) :
_class_loader(h_class_loader()),
_is_anonymous(is_anonymous),
_keep_alive(is_anonymous || h_class_loader.is_null()),
_metaspace(NULL), _unloading(false), _klasses(NULL),
_claimed(0), _jmethod_ids(NULL), _handles(), _deallocate_list(NULL),
_next(NULL), _dependencies(dependencies),
_metaspace_lock(new Mutex(Monitor::leaf+1, "Metaspace allocation lock", true)) {
// empty
}
ClassLoaderData::~ClassLoaderData() {
//遍历该ClassLoader加载的所有Klass,执行release_C_heap_structures方法
classes_do(InstanceKlass::release_C_heap_structures);
Metaspace *m = _metaspace;
if (m != NULL) {
_metaspace = NULL;
//释放Metaspace
delete m;
}
if (_jmethod_ids != NULL) {
//释放_jmethod_ids
Method::clear_jmethod_ids(this);
}
//删除 lock
delete _metaspace_lock;
if (_deallocate_list != NULL) {
delete _deallocate_list;
}
}
void ClassLoaderData::classes_do(void f(InstanceKlass*)) {
//遍历所有加载的类
for (Klass* k = _klasses; k != NULL; k = k->next_link()) {
if (k->oop_is_instance()) {
f(InstanceKlass::cast(k));
}
assert(k != k->next_link(), "no loops!");
}
}
重点关注其调用链,如下:
record_dependency用于记录引用了当前ClassLoaderData的Klass,这些Klass不能通过正常的GC遍历方式找到,源码实现如下:
void ClassLoaderData::record_dependency(Klass* k, TRAPS) {
ClassLoaderData * const from_cld = this;
ClassLoaderData * const to_cld = k->class_loader_data();
// 启动类加载器不会被销毁,所以不需要记录引用该加载器的Klass
if (to_cld->is_the_null_class_loader_data()) {
return;
}
oop to;
if (to_cld->is_anonymous()) {
// Anonymous class dependencies are through the mirror.
to = k->java_mirror();
} else {
to = to_cld->class_loader();
//非匿名类加载器下,如果当前类加载器是目标类的类加载器的父类加载器,则不需要添加
if (!from_cld->is_anonymous()) {
// Check that this dependency isn't from the same or parent class_loader
oop from = from_cld->class_loader();
oop curr = from;
while (curr != NULL) {
if (curr == to) {
return;
}
curr = java_lang_ClassLoader::parent(curr);
}
}
}
//这种引用关系不能用过正常的GC遍历到,所以通过dependency的方式单独保存
Handle dependency(THREAD, to);
from_cld->_dependencies.add(dependency, CHECK);
}
保存下来的_dependencies主要是为了GC遍历使用,参考ClassLoaderData::oops_do方法的实现,如下:
该方法的调用链如下:
add_to_deallocate_list用于临时保存需要被释放的Klass,Method等元数据的指针,因为这些元数据可能依然被使用,所以不能立即释放,只能等到类加载器被卸载了才释放。free_deallocate_list是类加载器被卸载时调用来释放deallocate_list中的元数据,这两方法的调用链如下:
这两方法的源码如下:
// Add this metadata pointer to be freed when it's safe. This is only during
// class unloading because Handles might point to this metadata field.
void ClassLoaderData::add_to_deallocate_list(Metadata* m) {
// 共享的元数据不能被删除
if (!m->is_shared()) {
//获取锁
MutexLockerEx ml(metaspace_lock(), Mutex::_no_safepoint_check_flag);
if (_deallocate_list == NULL) {
//初始化_deallocate_list
_deallocate_list = new (ResourceObj::C_HEAP, mtClass) GrowableArray(100, true);
}
//添加到列表中
_deallocate_list->append_if_missing(m);
}
}
// Deallocate free metadata on the free list. How useful the PermGen was!
void ClassLoaderData::free_deallocate_list() {
//该方法只能在安全点的时候调用
assert(SafepointSynchronize::is_at_safepoint(), "only called at safepoint");
if (_deallocate_list == NULL) {
return;
}
// Go backwards because this removes entries that are freed.
for (int i = _deallocate_list->length() - 1; i >= 0; i--) {
Metadata* m = _deallocate_list->at(i);
//Metadata不在栈上,即不再被使用
if (!m->on_stack()) {
//从链表中移除
_deallocate_list->remove_at(i);
//根据Metadata的类型分别释放
if (m->is_method()) {
MetadataFactory::free_metadata(this, (Method*)m);
} else if (m->is_constantPool()) {
MetadataFactory::free_metadata(this, (ConstantPool*)m);
} else if (m->is_klass()) {
MetadataFactory::free_metadata(this, (InstanceKlass*)m);
} else {
ShouldNotReachHere();
}
}
}
}
free_metadata是一个模板方法,该方法的实现如下:
deallocate_contents方法完成md内部相关数据结构的清理,deallocate方法用于释放其对应的内存。
metaspace_non_null方法用于返回ClassLoaderData的_metaspace属性,如果未初始化则会完成该属性初始化。其源码实现如下:
Metaspace* ClassLoaderData::metaspace_non_null() {
assert(!DumpSharedSpaces, "wrong metaspace!");
//延迟初始化,因为部分ClassLoader并不会加载类,也就不需要从Metaspace分配空间
if (_metaspace == NULL) {
//获取锁metaspace_lock
MutexLockerEx ml(metaspace_lock(), Mutex::_no_safepoint_check_flag);
//重新检查,可能其他线程已经完成初始化
if (_metaspace != NULL) {
return _metaspace;
}
//如果是启动类加载器
if (this == the_null_class_loader_data()) {
assert (class_loader() == NULL, "Must be");
set_metaspace(new Metaspace(_metaspace_lock, Metaspace::BootMetaspaceType));
} else if (is_anonymous()) {//如果是匿名类类加载器
if (TraceClassLoaderData && Verbose && class_loader() != NULL) {
tty->print_cr("is_anonymous: %s", class_loader()->klass()->internal_name());
}
set_metaspace(new Metaspace(_metaspace_lock, Metaspace::AnonymousMetaspaceType));
} else if (class_loader()->is_a(SystemDictionary::reflect_DelegatingClassLoader_klass())) {
//如果是sun_reflect_DelegatingClassLoader类加载器
if (TraceClassLoaderData && Verbose && class_loader() != NULL) {
tty->print_cr("is_reflection: %s", class_loader()->klass()->internal_name());
}
set_metaspace(new Metaspace(_metaspace_lock, Metaspace::ReflectionMetaspaceType));
} else {
//如果是普通的类加载器
set_metaspace(new Metaspace(_metaspace_lock, Metaspace::StandardMetaspaceType));
}
}
return _metaspace;
}
add_class用于将已经完成类加载的类对应的Klass加入到ClassLoaderData管理的Klass链表中,remove_class用于类卸载的时候将Klass从ClassLoaderData管理的Klass链表中移除,这两方法的调用链如下:
InstanceKlass::deallocate_contents就是上一节free_deallocate_list方法释放Klass对应内存时调用的方法。这两方法的源码如下:
void ClassLoaderData::add_class(Klass* k) {
MutexLockerEx ml(metaspace_lock(), Mutex::_no_safepoint_check_flag);
//加入到链表的头部
Klass* old_value = _klasses;
k->set_next_link(old_value);
// link the new item into the list
_klasses = k;
if (TraceClassLoaderData && Verbose && k->class_loader_data() != NULL) {
ResourceMark rm;
//打印日志
tty->print_cr("[TraceClassLoaderData] Adding k: " PTR_FORMAT " %s to CLD: "
PTR_FORMAT " loader: " PTR_FORMAT " %s",
p2i(k),
k->external_name(),
p2i(k->class_loader_data()),
p2i((void *)k->class_loader()),
loader_name());
}
}
void ClassLoaderData::remove_class(Klass* scratch_class) {
MutexLockerEx ml(metaspace_lock(), Mutex::_no_safepoint_check_flag);
Klass* prev = NULL;
//遍历所有的Klass,找到待删除的Klass,然后从链表中移除
for (Klass* k = _klasses; k != NULL; k = k->next_link()) {
if (k == scratch_class) {
if (prev == NULL) {
//待删除的类位于链表头部
_klasses = k->next_link();
} else {
//待删除的类位于链表中间
Klass* next = k->next_link();
prev->set_next_link(next);
}
return;
}
prev = k;
assert(k != k->next_link(), "no loops!");
}
ShouldNotReachHere(); // should have found this class!!
}
ClassLoaderDataGraph的定义同样位于classLoaderData.hpp中,相当于ClassLoaderData的一个管理类,方便遍历所有的ClassLoaderData,其定义的属性和方法都是静态的,属性如下:
其中head表示当前活跃的ClassLoaderData链表,unloading表示即将被卸载的ClassLoaderData链表,通过do_unloading方法将某个不再活跃的ClassLoaderData加入到unloading链表中,最终由purge方法触发unloading链表中所有ClassLoaderData的内存释放。定义的方法主要是GC遍历相关的,如下:
其实现都是通过_head属性遍历所有的ClassLoaderData然后调用ClassLoaderData的对应遍历方法,重点关注以下方法的实现。
find_or_create用于查找某个java/lang/ClassLoader实例对应的ClassLoaderData,如果不存在则为该实例创建一个新的ClassLoaderData实例并添加到ClassLoaderDataGraph管理的ClassLoaderData链表中。注意ClassLoaderData指针的保存位置比较特殊,不是在ClassLoader实例的内存中,而是内存外,内存上方的8字节处,为什么这8字节在没有保存ClassLoaderData指针时是NULL了?因为Java对象创建的时候会保证对象间有8字节的空隙。该方法实现的源码如下:
inline ClassLoaderData *ClassLoaderDataGraph::find_or_create(Handle loader, TRAPS) {
//校验loader必须是一个oop
guarantee(loader() != NULL && loader()->is_oop(), "Loader must be oop");
//根据java/lang/ClassLoader对象的地址获取ClassLoaderData的指针,如果不为空说明这个ClassLoader对象
//已经添加到ClassLoaderDataGraph中了,否则需要添加
ClassLoaderData* loader_data= java_lang_ClassLoader::loader_data(loader());
if (loader_data) {
return loader_data;
}
return ClassLoaderDataGraph::add(loader, false, THREAD);
}
//创建一个新的ClassLoaderData实例,然后将其作为java/lang/ClassLoader的隐藏属性保存在ClassLoader实例中
ClassLoaderData* ClassLoaderDataGraph::add(Handle loader, bool is_anonymous, TRAPS) {
ClassLoaderData::Dependencies dependencies(CHECK_NULL);
//通过No_Safepoint_Verifier的构造方法校验当前不在安全点上,没有进行GC
No_Safepoint_Verifier no_safepoints;
ClassLoaderData* cld = new ClassLoaderData(loader, is_anonymous, dependencies);
//如果不是匿名类
if (!is_anonymous) {
//获取相对ClassLoader地址偏移为-1的地址,即在ClassLoader实例内存上方的8字节
ClassLoaderData** cld_addr = java_lang_ClassLoader::loader_data_addr(loader());
//将ClassLoaderData的地址原子的保存进去
ClassLoaderData* old = (ClassLoaderData*) Atomic::cmpxchg_ptr(cld, cld_addr, NULL);
if (old != NULL) {
//不等于NULL说明有其他线程完成了保存动作,因此将创建的ClassLoaderData释放掉,返回其他线程已经保存的ClassLoaderData
delete cld;
return old;
}
}
//设置成功,将新创建的ClassLoaderData加入到链表中
ClassLoaderData** list_head = &_head;
ClassLoaderData* next = _head;
do {
cld->set_next(next);
//原子的修改链表头,如果因为并发修改失败则不断重试
ClassLoaderData* exchanged = (ClassLoaderData*)Atomic::cmpxchg_ptr(cld, list_head, next);
if (exchanged == next) {
//修改成功
if (TraceClassLoaderData) {
//打印日志
ResourceMark rm;
tty->print("[ClassLoaderData: ");
tty->print("create class loader data " INTPTR_FORMAT, p2i(cld));
tty->print(" for instance " INTPTR_FORMAT " of %s", p2i((void *)cld->class_loader()),
cld->loader_name());
tty->print_cr("]");
}
return cld;
}
//链表头被其他线程修改了,重置next
next = exchanged;
} while (true);
}
该方法的调用链如下:
purge方法用于释放需要被卸载掉的ClassLoaderData,该方法的调用链如下:
该方法的源码如下:
void ClassLoaderDataGraph::purge() {
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!");
ClassLoaderData* list = _unloading;
_unloading = NULL;
ClassLoaderData* next = list;
//遍历待释放的ClassLoaderData列表
while (next != NULL) {
ClassLoaderData* purge_me = next;
next = purge_me->next();
//删除ClassLoaderData,调用其析构方法
delete purge_me;
}
//释放Metaspace中所有空闲的VirtualSpaceNode
Metaspace::purge();
}
do_unloading用于遍历所有的活跃ClassLoaderData,判断其是否活跃,如果不再活跃则将其从活跃链表中移除,加入到不活跃的ClassLoaderData链表中,并通知该ClassLoaderData加载的所有Klass类加载器被卸载。该方法的调用链如下:
该方法的源码如下:
// Move class loader data from main list to the unloaded list for unloading
// and deallocation later.
bool ClassLoaderDataGraph::do_unloading(BoolObjectClosure* is_alive_closure, bool clean_alive) {
ClassLoaderData* data = _head;
ClassLoaderData* prev = NULL;
//seen_dead_loader表示找到了需要卸载的ClassLoaderData
bool seen_dead_loader = false;
_saved_unloading = _unloading;
//从_head开始遍历
while (data != NULL) {
//如果是活跃的,不能被GC回收,则跳到下一个
if (data->is_alive(is_alive_closure)) {
prev = data;
data = data->next();
continue;
}
//需要被垃圾回收
seen_dead_loader = true;
ClassLoaderData* dead = data;
//执行ClassLoaderData卸载
dead->unload();
//将其从活跃链表中移除
data = data->next();
if (prev != NULL) {
prev->set_next(data);
} else {
assert(dead == _head, "sanity check");
_head = data;
}
//将其加入到待卸载链表中
dead->set_next(_unloading);
_unloading = dead;
}
if (clean_alive) {
//清理之前版本的Klass和ClassLoaderData的_deallocate_list
ClassLoaderDataGraph::clean_metaspaces();
}
if (seen_dead_loader) {
//遍历所有_unloading中的ClassLoaderData,发布类卸载事件
post_class_unload_events();
}
return seen_dead_loader;
}
bool ClassLoaderData::is_alive(BoolObjectClosure* is_alive_closure) const {
bool alive = keep_alive() //启动类加载器和未使用完的匿名类类加载器的ClassLoaderData的keep_alive返回true
|| is_alive_closure->do_object_b(keep_alive_object());
return alive;
}
void ClassLoaderData::unload() {
_unloading = true;
//遍历所有加载的Klass,通知即将被卸载
classes_do(InstanceKlass::notify_unload_class);
if (TraceClassLoaderData) {
//打印日志
ResourceMark rm;
tty->print("[ClassLoaderData: unload loader data " INTPTR_FORMAT, p2i(this));
tty->print(" for instance " INTPTR_FORMAT " of %s", p2i((void *)class_loader()),
loader_name());
if (is_anonymous()) {
tty->print(" for anonymous class " INTPTR_FORMAT " ", p2i(_klasses));
}
tty->print_cr("]");
}
}
void ClassLoaderDataGraph::clean_metaspaces() {
// mark metadata seen on the stack and code cache so we can delete unneeded entries.
bool has_redefined_a_class = JvmtiExport::has_redefined_a_class();
MetadataOnStackMark md_on_stack(has_redefined_a_class);
if (has_redefined_a_class) {
// 发生了类的重定义,通知所有已加载的Klass清理之前版本的Klass的常量池和弱方法引用等
for (ClassLoaderData* data = _head; data != NULL; data = data->next()) {
data->classes_do(InstanceKlass::purge_previous_versions);
}
}
//遍历所有的ClassLoaderData释放_deallocate_list保存的待释放内存
free_deallocate_lists();
}
void ClassLoaderDataGraph::free_deallocate_lists() {
for (ClassLoaderData* cld = _head; cld != NULL; cld = cld->next()) {
// We need to keep this data until InstanceKlass::purge_previous_version has been
// called on all alive classes. See the comment in ClassLoaderDataGraph::clean_metaspaces.
cld->free_deallocate_list();
}
// In some rare cases items added to the unloading list will not be freed elsewhere.
// To keep it simple, walk the _unloading list also.
for (ClassLoaderData* cld = _unloading; cld != _saved_unloading; cld = cld->next()) {
cld->free_deallocate_list();
}
}
void ClassLoaderDataGraph::post_class_unload_events(void) {
#if INCLUDE_TRACE
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!");
if (Tracing::enabled()) {
if (Tracing::is_event_enabled(TraceClassUnloadEvent)) {
assert(_unloading != NULL, "need class loader data unload list!");
_class_unload_time = Ticks::now();
//遍历_unloading中的所有ClassLoaderData的Klass,执行class_unload_event方法,发布EventClassUnload事件
classes_unloading_do(&class_unload_event);
}
Tracing::on_unloading_classes();
}
#endif
}
void ClassLoaderDataGraph::classes_unloading_do(void f(Klass* const)) {
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!");
// Only walk the head until any clds not purged from prior unloading
// (CMS doesn't purge right away).
for (ClassLoaderData* cld = _unloading; cld != _saved_unloading; cld = cld->next()) {
cld->classes_do(f);
}
}
void ClassLoaderDataGraph::class_unload_event(Klass* const k) {
// post class unload event
EventClassUnload event(UNTIMED);
event.set_endtime(_class_unload_time);
event.set_unloadedClass(k);
oop defining_class_loader = k->class_loader();
event.set_definingClassLoader(defining_class_loader != NULL ?
defining_class_loader->klass() : (Klass*)NULL);
event.commit();
}