jvm中方法区_永久代_元空间
方法区:
来看点jvm规范中的原文,7和8一样的。
The Java Virtual Machine has a method area that is shared among all Java Virtual Machine threads. The method area is analogous to the storage area for compiled code of a conventional language or analogous to the “text” segment in an operating system process. It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods (§2.9) used in class and instance initialization and interface initialization.
The method area is created on virtual machine start-up. Although the method area is logically part of the heap, simple implementations may choose not to either garbage collect or compact it. This specification does not mandate the location of the method area or the policies used to manage compiled code. The method area may be of a fixed size or may be expanded as required by the computation and may be contracted if a larger method area becomes unnecessary. The memory for the method area does not need to be contiguous.
A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of the method area, as well as, in the case of a varying-size method area, control over the maximum and minimum method area size.
The following exceptional condition is associated with the method area:
- If memory in the method area cannot be made available to satisfy an allocation request, the Java Virtual Machine throws an OutOfMemoryError.
方法区是逻辑上的东西,是JVM规范的东西,所有虚拟机必须遵守的。
根据JVM规范中,它存储每个类的结构,如运行时常量池、字段和方法数据,以及方法和构造函数的代码,包括在类和实例初始化和接口初始化中使用的特殊方法(2.9)。
jvm规范中也说了,方法区域是在虚拟机启动时创建的。虽然方法区域是在堆的逻辑部分,简单实现可以选择不垃圾收集或压缩。此版本的Java虚拟机规范不强制指定方法区域的位置或用于管理的策略编译的代码。
因此不同的虚拟机,或者不同的版本对方法区有了不同的实现,比如1.7中的永久代,1.8中的元空间。而方法区也被称为非堆。
永久代(PermGen)
在HotSpot虚拟中永久代是jdk1.7的实现,在HotSpot上把GC分代收集扩展至方法区,或者说使用永久代来实现方法区,而在其他虚拟机中并没有永久代的说法。
HotSpot使用GC分代来实现方法区内存回收。这里的垃圾回收是和老年代是绑定在一起的。
永久代的大小是启动时限制死的没有办法调整的,在动态生成类较多的情况下,容易出现内存溢出的问题,比如大量jsp页面的情况下,每个jsp都会生成servlet。
在1.7中,永久代和java堆是隔离的,但是他们使用的内存空间是连续的,也可以说永久代使用的是jvm的内存。
在HotSpot虚拟中这个实现在jdk1.8中被移除了。
元空间(matespace)
元空间是HotSpot虚拟机在jdk1.8对jvm规范的实现。
在1.8中,将1.7中永久代保存的数据一部分转移到了java堆中,一部分转移到了元空间中。
1.8中,元空间不在与堆连续,也就是不在jvm中了,而是使用本地内存。
本地内存又称为C-heap,是jvm自身进程使用的。元空间在的这部分内存因为不在jvm中,而不会发生GC。
理论上元空间不在会发生向永久代的内存溢出的问题,因为主机内存可以无限使用,这样可以加载的类的数量就不再受限制,但是一般可以通过参数设置最大空间。
元空间的大小如果不进行设置,JVM会自动根据类的元数据大小动态增加元空间的容量。
对于元空间的内存管理,jvm采用元空间虚拟机管理。在元空间中,类和其元数据的生命周期和其对应的类加载器是相同的。话句话说,只要类加载器存活,其加载的类的元数据也是存活的,因而不会被回收掉。
每一个类加载的存储区域都称为一个元空间,也就是元空间再主存中是零散的,所有的元空间合起来就是说的元空间。当一个类加载器被垃圾回收器标记为不再存活,其对应的元空间会被回收。在元空间的回收过程中没有重定位和压缩等操作。但是元空间内的元数据会进行扫描来确定Java引用。
总结
永久代是jdk1.7的实现,元空间是1.8的实现。
移除永久代原因
字符串存在永久代中,现实使用中易出问题, 由于永久代内存经常不够用或发生内存泄露,爆出异常。这一步再1.6更新到1.7时已经完成,也就是说移除永久代是持续的,分布的。
JDK1.7及之后版本的JVM已经将字符串常量池从方法区中移了出来,在Java 堆(Heap)中开辟了一块区域存放字符串常量池。
类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
永久代会为GC带来不必要的复杂度,并且回收效率偏低。
为了HotSpot和JRockit合并产生一个超级JVM。
移除工作从jdk1.7就开始了,只是没有完全移除。譬如符号引用(Symbols)转移到了native heap:字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap。
字面量就是
String hello = "helle"
,中引号中的部分,声明为final的(基本数据类型)常量值。
符号引用就是编译阶段,引用代替其他类的地址的东西。
区别
- 1.7与1.8的版本区别。
- 永久代在jvm中,元空间不在。
- 永久代大小是固定的,元空间不固定。
- 永久代内存是连续的且与堆相连,元空间内存是分散的。
- 永久代由JVM管理,元空间由元空间虚拟机管理。
- 永久代的GC与老年代绑定在一起,元空间是所持有其的类加载器被标记不在存活则会释放,还给操作系统。
存储内容
java永久代,元空间,常量池,方法区详解
Java中局部变量、实例变量和静态变量在方法区、栈内存、堆内存中的分配
根据JVM规范,方法区存储类的结构、运行时常量池、字段和方法数据,以及方法和构造函数的代码,包括在类和实例初始化和接口初始化中使用的特殊方法。
所以:—>线程共享的,主要存储类信息、常量池、静态变量、JIT编译后的代码等数据。方法区理论上来说是堆的逻辑组成部分;
对于常量池:
- class文件常量池
- 运行时常量池
- 字符串常量池
class文件常量池
Class文件常量池指的是编译生成的class字节码文件,其结构中有一项是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用(这里会被拆分、搬迁,看上面),这部分内容将在类加载后进入方法区的运行时常量池中存放。这里的数据貌似在jdk1.7被拆分了。
运行时常量池
根据规范->运行时常量池是类文件中constant_pool表的每个类或每个接口的运行时表示形式(4.4)。
Class文件常量池将在类加载后进入方法区的运行时常量池中存放(移除永久代的搬迁中会没了吧)。
字符串常量池
感觉有问题
String s = new String(“1”); 不考虑引用的情况下,不仅仅在堆上会分配一块内存,还会再字符串常量池生成一个对象。引号里面的再常量池,new出来的再堆上上,s在栈里,指向了堆里new的玩意。
- 在jdk1.6(含)之前也是方法区的一部分,并且其中存放的是字符串的实例。
- 在jdk1.7(含)之后是在堆内存之中,存储的是字符串对象的引用,也存储实例,字符串实例是在堆中。
- jdk1.8已移除永久代,字符串常量池是在本地内存当中,存储的也只是引用,也存储实例,实例在堆中。
JVM 中除了字符串常量池,8种基本数据类型中除了两种浮点类型剩余的6种基本数据类型的包装类,都使用了缓冲池技术,但是 Byte、Short、Integer、Long、Character 这5种整型的包装类也只是在对应值在 [-128,127] 时才会使用缓冲池,超出此范围仍然会去创建新的对象。