【JVM】metaspace系列-metaspace初识
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源码
链接:https://pan.baidu.com/s/1jiv7v5SAolC21SHCB66SEA
提取码:p7d0
复制这段内容后打开百度网盘手机App,操作更方便哦--来自百度网盘超级会员V4的分享