方法区是线程共享的内存区域
方法区大小决定了内存可以保存多少个类,如果定义了太多类导致溢出会发生OOM
JDK7称永久代 JDK8称元空间
区别在于:原空间不在虚拟机设置的内存中,而是使用本地内存
替换原因:为永久代设置空间大小是很难确定的,调优也很困难
存储被虚拟机加载的类型信息,常量,静态变量,即时编译器编译后的代码缓存
类型信息:堆每个加载的类型(类calss,接口interface,枚举enum,注解annotation),jvm都会在方法区中存储一下类型信息
1)完全限定名
2)直接父类的完全限定名
3)类型的修饰符以及生命顺序(public,abstract,final的某个子集)
4)这个类型直接接口的一个有序列表
5)方法信息(方法名,方法返回类型,方法参数个数和类型,方法修饰符,方法的zi’jie’ma)
常量池和运行时常量池
常量池表:包括各种字面量和堆类型,域,方法的符号引用,JVM根据指令找到要执行的各个信息
一个java源文件中的类,接口,编译后产生一个字节码文件,而java中字节码需要数据支持,通常这种数据会很大以至于不能直接存到字节码里,另一种方式就是存到常量池,这些字节码包含了指向常量池的引用,在动态链接的时候会用到运行时常量池。
每个#都是引用存在常量池中基本信息,可以达到复用
运行时常量池:
在加载类和接口到虚拟机时,就会创建对应的运行时常量池
引用转换位真实地址,相对于class文件常量池另一特征是具备动态性。
JDK7及之前 使用-XX:PermSize可以设置永久代初始分配空间 默认20.75MB
-XX:MaxPermSize设置最大可分配空间 32位机器默认64MB 64位默认82MB
JDK8之后使用-XX:MetaspaceSize 默认21MB 和-xx:MaxMetaspaceSize 默认-1 即没有限制 参数
21MB是元空间大小的初始高水位线,达到这个大小Full GC会触发自动清理没有用的类,然后将高水位线重置
如何解决OOM:
分清是出现内存泄漏还是内存溢出
1)内存泄漏,找到GC Roots的引用链,找到无法垃圾回收的关联对象的类型性信息
2)如果空间不足发生溢出,减少程序运行期的内存消耗
java虚拟机规范中对于方法区的垃圾回收约束是非常宽松的
一般主要分为
常量池中废弃的常量:
只要常量池中的常量没有被任何地方引用,就可以被回收,与回收java堆中的对象类似
不再使用的类型:
需要同时满足:
1)该类所有的实例都已经被回收,包括该类派生的任何子类
2)加载该类的类加载器已经被回收
3)该类对应的java.lang.Class对象没有在任何地方被引用,无法通过反射访问该类的方法。
JDK8 引入元空间 而元空间即使用的是 直接内存
直接内存是java堆外,直接向系统申请的内存区域,访问速度优于java堆,读写性能高,用于数据缓冲区
由于直接内存在java堆外,因此他的大小不会受jvm参数指定 ,但以然受限于操作系统能给出的最大内存
缺点:
分配回收成本高
不受JVM内存回收管理