Java 8元空间
今天想要测试方法区空间溢出会怎么样,然后调试了半天.使用String,静态变量,发现最后都是heap space OOM,经过一番学习,发现一些Java 8 中的一些改变.记录一下.
在java 8中方法区也就是永久代已经是不存在了,出现了一个替代者:元空间.
元空间
Java 8Hotpot JVM开始使用本地化的内存存放类的元数据,这个空间也叫作元空间(Metaspace).它存储在native heap中,不受jvm内存限制,仅仅只受本地内存的限制.
Java Hotpot明确了元数据空间从操作系统请求空间,然后将其分成块.每个类加载器有一个块.类加载器从其块中为元数据分配空间.当类加载器卸载掉类的时候,它的块会被循环使用,或者会被操作系统回收.元数据一般使用一种内存映射文件的方法分配的空间,而不是malloc申请的空间.
这个样子的话就是表示着一个让人烦恼的问题也就是java.lang.OutOfMemoryError: PermGen的问题将不会存在.并且不需要在调整和监控这个内存空间.但是这个改变并不会消除类和类加载器的内存泄露,而是需要以一些不同的方式和学习新名词来追查这些问题.
- -XX:MetaspaceSize 初始元空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整,如果释放了大量空间,就适当减少,如果释放了很少的空间,在不超过MaxMetaspaceSize时可以增加.
- -XX:MaxMetaspaceSize 最大的元空间大小,元空间大小不得超过这个值,如果超过会抛出OOM异常
- -XX:MinMetaspaceFreeRatio 在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
- -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集
永久区的情况
- 永久代被完全移除
- JVM参数PermSize和MaxPermSize也会被忽略.
元空间内存分配模型
- 绝大多数的类元数据的空间都从本地内存分配.
- 每个类加载器分配一个内存块的列表.
- 这个内存块是连续分配的.
- 元空间主要存储类信息,常量池,方法数据以及方法代码 从JDK7开始就进行移除永久代的准备 比如进行如下改变
- 符号引用转换到了native heap中
- 字面量常量(String)分配到java heap中
- 类的静态变量转移到了java heap中
在JDK8中则是完全移除了永久代 连带着设置永久代大小的参数也被移除,取而代之的是一个Metaspace,这些空间直接在堆上进行分配.
为什么这么更改呢: 总结一下:
- 首先是为了融合Hotpot JVM和JRockit VM而做出的努力,因为JRockit没有永久代
- 字符串存放在永久代,很容易出现性能问题和内存溢出.
- 类即方法信息难以确定大小,尤其是现在大多数的动态代理.而永久代的大小指定比较困难,大小容易出现永久代溢出,太大会导致老年代溢出
- 永久代对于GC不方便,而且效率比较低
元空间的优势
- 以前的jar包以及自己项目的class放在永久代中,但是永久代大小固定,所以经常可能出现永久代溢出.
- 永久代溢出将会不存在,元空间直接与放在本地堆中,大小可以动态增长
- 使用元空间后,每个项目都会共享可用的class内存空间,元空间中的class可以被多个项目使用,以前的永久代则是每个项目都有自己的永久代,并且用独立的class对象
元空间的容量
- 默认情况下,类元数据分配受到可用的本机内存容量的限制(容量依然取决于你使用32位JVM还是64位操作系统的虚拟内存的可用性
- 新参数 MaxMetaspaceSize被使用,允许限制类元数据的最大本地内存,如果没有指定,会根据程序运行情况动态设置
元空间垃圾回收 - 如果元空间的中的元数据达到参数”MaxMetaspaceSize”设置的值,才会触发对死亡对象和类加载器的垃圾回收
- 元空间中的对象的位置是固定的
- 元空间不会单独回收某个类
- 元空间提高了GC的性能,省掉了GC扫描的时间.