方法区和堆一样是多个线程共享的,
《java虚拟机规范》中声明,所有的方法区在逻辑上可以看作是堆的一部分,但是一些简单的实现可能不会选择区进行垃圾收集或者压缩,
而对于hotSpot而言,方法区还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开,所以,方法区可以看作是一块独立与堆的内存空间,而且堆的主要目的是存放创建出来的各种对象,方法区里则会有类的信息,从内容来说,也不相同
方法区在jvm启动时被创建,它的实际物理内存空间和java堆区一样都是可以不连续的
方法区的大小和堆空间一样,可以固定大小也可以扩展
方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法去溢出,虚拟机同样会抛出oom:PermGen space 或者oom : Metspace (1.7之前叫做永久代,1.8叫元空间,报的错误也不一样)
关闭jvm就会释放这个区域的内存
1.7之前习惯性的把方法区叫做永久代,1.8使用元空间取代了永久代,(可以把方法区看作是接口,永久代和元空间都是对应的实现)
方法区和永久代并不等价,只是在hotSpot中是等价的,《java虚拟机规范》中并没有规定具体的实现,不同的虚拟机是不一样的,
永久代使用的是虚拟机的内存,内存方面限制较大,容易溢出
元空间采用的是本地内存来实现的,空间更大
jdk1.7的永久代
-XX:PermSize 初始分配空间 ,默认:20.75M
-XX:MaxPermSize 最大空间 ,32位物理机默认64M,64位默认82M
1.8 元空间:因为用的是本地内存,所以和平台有关系
-XX:MetaspaceSize 初始分配空间 ,windows默认21M
-XX:MaxMetaspaceSize 最大空间 ,默认是-1,也就是没有限制

文件被加载到方法后,会保留是被哪一个类加载器加载的该文件,也算是类的一部分
方法区用于存储已被虚拟机加载的类型信息(类、接口、枚举、注解)、常量、静态变量、即时编译器编译后的代码缓存等
类型信息:对每个加载的类型信息,jvm必须在方法去中存储以下类型信息:
域(Feild,俗称属性)信息
方法信息:
non-final的类变量
全局常量(final 的 static),在编译的时候就会被分配了
方法区内部包含了运行时常量池。
字节码文件,包含常量池
把常量池的信息加载到内存后,就叫做运行时常量池
常量池包含了各种字面量和对类型、域和方法的符号引用,也就是一些基本信息
常量池可以看作是一张表,虚拟机指令根据这张表找到要执行的类名、方法名、参数类型、字面量等信息
常量池是字节码文件的一部分,用于存放编译时生成的各种字面量与符号引用,这部分内容在类加载后会存放到方法区的运行时常量池
运行时常量池是方法区的一部分
每个类或接口都会有一个常量池,当中的数据就像数组一样,可以用索引来访问
运行时常量池中,存储的就不是符号引用了,是真实的地址
运行时常量池相较于常量池来说,具备动态性,有可能常量池中并没有相关信息,比如虚方法等
只有hotspot才有永久代
在jdk1.6之前,有永久代,而且静态变量存放在永久代上(new的对象的实体始终在堆中,引用在永久代)
1.7,依然有永久代,但是已经在逐步去除永久代了,字符串常量池、静态变量等从永久代中移除,转移到堆中
1.8及以后,就没有永久代了,类型信息、字段、方法、常量保存至本地内存的元空间,但是字符串常量池、静态变量依然放在堆中

为什么要用元空间代替永久代
官方给的消息是,jdk8 ,oracle将hotSport 和 JRockit 进行了整合,而 JRockit 是没有永久代的,所以1.8也没有永久代
也有以下原因:
字符串常量池的变化
jdk 7 将StringTable放到了堆空间,因为永久代的回收效率很低,只有在full gc 是才会触发,而full GC 是老年代、永久代不足时才会触发,这就使得StingTable回收效率不高,而一般开发中通常会有大量的字符串被创建,如果回收效率低,容易导致永久代内存不足,放在堆里。可以及时的回收内存
《java虚拟机规范》对方法区的约束是比较宽松的,方法区的垃圾回收,在不同的虚拟机实现是不同的,是可以不进行垃圾回收的
一般来说,方法区的垃圾回收效果是比较困难的,尤其是类型的卸载,条件十分苛刻,但是方法区的回收有时又是非常由必要的,否则有可能会出现内存泄漏
方法区的垃圾回收主要涉及两部分:
