动态加载 class,卸载 class,metaspace 系列
动态加载 class,卸载 class
从指定位置的 jar 中加载 class,和卸载 class
第一种
URL url = new File("/文件路径/entityMaker.jar").toURI().toURL();
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
URLClassLoader classLoader = new URLClassLoader(
new URL[]{url},
systemClassLoader
);
//加载class
//不能使用Class.forName("com.hebaibai.entitymaker.util.SqlUtils")加载Class
Class sqlUtils = classLoader.loadClass("com.hebaibai.entitymaker.util.SqlUtils");
final Object instance = sqlUtils.newInstance();
System.out.println(instance.getClass());
//卸载类
Method close = URLClassLoader.class.getDeclaredMethod("close");
close.invoke(classLoader);
//已经加载过得没法卸载
sqlUtils = classLoader.loadClass("com.hebaibai.entitymaker.util.FileUtils");
instance = sqlUtils.newInstance();
System.out.println(instance.getClass());
第二种
URL url = new File("/home/hjx/Desktop/entityMaker/entityMaker.jar").toURI().toURL();
URLClassLoader classLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
if (!addURL.isAccessible()) {
addURL.setAccessible(true);
}
addURL.invoke(classLoader, url);
addURL.setAccessible(addURL.isAccessible());
//可以使用Class.forName("com.hebaibai.entitymaker.util.SqlUtils")加载Class
Class<?> aClass = Class.forName("com.hebaibai.entitymaker.util.SqlUtils");
Object instance = aClass.newInstance();
System.out.println(instance.getClass());
https://www.cnblogs.com/colin-xun/p/14184845.html
【JVM】metaspace 系列 - metaspace 初识 - colin_xun - 博客园
metaspace 引入#
在 JDK8 之前 JDK 开发人员就在慢慢的把永久代中的东西往外面移动,到 JDK8 的时候直接讲永久代删除,同时引入了元空间 metaspace。
Symbols => native memory
Interned strings => Java Heap
Class statics => Java Heap 参考
public static void main(String[] args) {
String s=new String("1");
s.intern();
String s2="1";
System.out.println(s==s2);
String s3=new String("1")+new String("1");
s3.intern();
String s4="11";
System.out.println(s3==s4);
}
在 JDK6 中,由于常量池和堆分开,所以放入就,是再 copy 一份到常量池,那么就算后面显式声明一个内容相同的字符串,它的地址是在常量池,== 永远不会是 true;
在 JDK7 和 JDK8 中,由于常量池和堆在一起,所以放入就是,常量池中放入堆中该对象的地址,如果后面显示声明一个内容相同的字符串,==. 就会是 true 了。但是如果在 intern 之前,常量池中已经有了当前字符串,那么就不会相等了。s1 == s2 是 false 就是因为 new 的时候,现在常量池中放了一个”1“,那么 s1 就会在堆里,s2 还是在常量池咯!
永久代和方法区的区别
我们知道在 JDK8 之前类的元数据放在 Permanent Generation,也就是永久代。
其实《JVM 虚拟机规范》里面只是规定了方法区的概念以及规范了他的作用是什么,但是并没有规范他的实现,在 HotSpot 上,因为 GC 回收是按分代算法来回收的,他的回收范围包含到了方法区,对这一块分区他有了自己的定义叫做永久代,所以其他你可以说 HotSpot 是用永久代来实现了方法区
永久代其实是一种逻辑概念。同时需要注意的点就是永久代是和堆内存紧邻的,但是到了 JDK8,元空间是不和堆内存紧邻的。
metaspace 定义#
官网解释
What is Metaspace?#
Metaspace is a native (as in: off-heap) memory manager in the hotspot.
It is used to manage memory for class metadata. Class metadata are allocated when classes are loaded. Their lifetime is usually scoped to that of the loading classloader - when a loader gets collected, all class metadata it accumulated are released in bulk. The memory manager does not need to track individual allocations for the purpose of freeing them. Hence, the metaspace allocator is an Arena- or Region-Based Allocator. It is optimized for fast, low-overhead allocation of native memory at the cost of not being able to (easily) delete arbitrary blocks.
通过官网解释我们可以得到元空间的两个关键信息
- 元空间是一块用来管理类的元数据信息的内存区域
- 元空间是一块本地内存空间
源码解释
src/share/vm/memory/metaspace.hpp
从注解可以看到每个 Metaspace 都有一个 SpaceManager,这个 SpaceManager 用来管理 MetaChunk 的分配问题,如果当前 MetaChunk 耗尽,那么 SpaceManager 会从当前 VirtualSpace 里面获取一个新的 MetaChunk,如果当前 VirtualSpace 耗尽,那么 SpaceManager 讲从列表中获取一个新的,这个 VirtualSpace 的列表也是 SpaceManager 在维护着。同时 SpaceManager 管理了一个 freelists,这个列表记录了哪些 MetaChunk 是可用的
这些变量的定义我们可以从源码中 Metaspace 类中看到
把这张图整理以后我们可以用下图表示
metaspace 初始化#
上面每个 Metaspace 都有一个 SpaceManager 这句话不解释的话,等下面讲完其实容易让大家疑惑,所以我这里解释一下,我们在 metaspace.hpp 中可以看到 JVM 对 metaspace 进行了类型划分,这些其实是 ClassLoaderMetaspace。Metaspace 和 ClassLoaderMateSpace 的加载时机是不一样的
metaspace 加载#
在 src/share/vm/classfile/classLoaderData.cpp 中我们可以看到 metaspace 有三个 initialize 相关的方法,分别是 ergo_initialize () 和 global_initialize () 以及 post_initialize ()。这三者是有顺序的。
-
先是 Arguments::apply_ergo () 时调用 Metaspace::ergo_initialize ()。这里主要设置一些全局变量,包括 MaxMetaspaceSize、MinMetaspaceExpansion、MaxMetaspaceExpansion 和 CompressedClassSpaceSize
-
接着在 universe_init () 时调用 Metaspace::global_initialize ()。主要是用来初始化 VirtualSpaceList 和 ChunkManager。Universe(宇宙、天地万物)类用来保存 JVM 中重要的系统类及其实例
-
最后调用 Metaspace::post_initialize ()。主要是用于 MetaspaceGC 的初始化
注意:
初始完 Metaspace,后期 Metaspace 的调整是通过 MetaspaceGC 这个类中的方法进行调整的,首先 MetaspaceGC 不是为了来执行 GC 的,他是用来维护上面图中的_capacity_until_GC 变量的,这个值其实就是一个水位线的值 HWM,因为获取主内存 mmap 或释放主内存都需要通过系统调用来完成,但是频繁的系统调用会影响性能,所以不是一个一个的增加,当 Metaspace 已经分配的内存打到这个值的话他就会触发一次 GC,GC 结束后该值会重新计算。重新计算是通过 MetaspaceGC::compute_new_size() 方法来实现,而真实的增加和回收分别是通过 MetaspaceGC::inc_capacity_until_GC 和 MetaspaceGC::dec_capacity_until_GC。
以上三步都是在 JVM 初始化的过程中执行的。
ClassLoaderMetaspace 加载#
ClassLoaderMetaspace 的加载和 Metaspace 不太一样,他是使用懒加载的方式,当对应的 ClassLoader 需要 metaspace 的时候他才会进行初始化,他初始化的位置在 src/share/vm/classfile/classLoaderData.cpp 的 metaspace_non_null () 方法
参考#
- Thomas Stüfe 的 Metaspace 系列文章
- 孙大圣 666 的 Metaspace 源码解读系列文章
- 整理文章
- metaspace 初始化文章
- JDK 官网 BUG - 元空间碎片问题修复
- JDK 官网动态元空间
- JDK7 改动
- 压缩类指针
资源#
该文章截图使用的 JDK 源码
【JVM】metaspace 系列 - metaspace 分层设计
从 metaspace 系列 - metaspace 初识中我们知道 metaspace 这个类定义了几个属性,这几个属性其实是三对,因为 metaspace 里面包含了两类数据,分别是类相关的元数据和非类相关的元数据,从下图截取 src/share/vm/memory/metaspace.hpp 的源码图可以看出来元数据分为两种。接下来我们挨个看看所有的属性
SpaceManager#
SpaceManager 是用来给 Metaspace 管理内存的。
关注几个属性
属性名称 | 属性作用 |
---|---|
Metaspace::MetadataType _mdtype; | metadata 的类型,分为类相关 metadata 和非类相关 metadata |
Metachunk* _chunks_in_use[NumberOfInUseLists]; | 当前 SpaceManager 中正在被使用的 chunk 的列表,这个在 SpaceManager 被回收的时候可以用来快速定位到那些 chunk 应该被回收 |
size_t _allocated_blocks_words; | 所有 block 所占的内存大小 |
size_t _allocated_chunks_words; | 所有 chunk 所占的内存大小 |
BlockFreelist _block_freelists; | 空闲的 chunk 列表,这里面的 chunk 再分配时会被重新使用 |
allocate#
上面是 SpaceManager 的 allocate 的方法,这里需要注意的是分配的时候不是直接就从_block_freelists 这个空闲列表中分配,因为从空闲列表直接分配的话,他需要遍历找到一个合适大小的,而遍历的成本比较高,所以他只当空闲列表变大的时候,也就是到达 4K 的时候才会从空闲列表中分配
retire_current_chunk#
我们从图中可以看到对于当前 chunk,如果他的剩余空间不够分配到新的 block,而且他的空间大于 min_size 的话,他会把这块空间放到 block_freelists 空闲列表中
析构函数#
ChunkManager#
ChunkManager 是用来全局管理 chunk 空闲列表的
_free_chunks#
ChunkManager 中的_free_chunks 是用来管理所有的空闲 chunk 的,SpaceManager 中的 _block _freelist 是用来管理所有空闲的 block 的。
这里的_free_chunks 是一个 ChunkList 链表的数组,NumberOfInUseLists 为 4,因为 Chunk 总共分为四类,四类
chunk_freelist_allocate#
free_chunks_get#
VirtualSpaceList#
VirtualSpaceList 是用来管理 VirtualSpaceNode 的,VirtualSpaceNode 是用来 metadata 分配的