jdk1.7中JVM内存模型
jdk1.8中JVM内存模型
1. 虚拟机栈(Method Stack)
java线程执行的内存模型,是线程私有的,
一个线程对应一个栈,每个方法在执行的同时都会创建一个栈帧(用于存储局部变量
表,操作数栈,动态链接,方法出口等信息),调用方法时执行入栈,方法返回时执行出栈,不存在垃圾回收问题,只要线程一结束该栈就释放,生命周期和线程一
致。
2. 程序计数器(Program Counter Register)
保存当前线程所执行的字节码地址,指向方法区中的方法字节码(指向吓一跳指令的地址,也即将要执行的指令代码),由执行引擎(Execution Engine)读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计
。每个线程工作时都有一个独立的计数器。程序计数器为执行 Java 方法服务,执行 native 方法时,程序计数器为空。
3. 本地方法栈 (Native Method Stack)
和栈作用很相似,用来保存线程执行时的信息。区别不过是Java栈为JVM执行Java方法服务,而本地方法栈为JVM执行native方法服务。登记native方法,在Execution Engine执行时加载本地方法库。
4. 堆 (Heap)
虚拟机启动时自动分配创建,用于存放对象的实例,几乎所有对象都在堆上分配内存,当对象无法在该空间申请到内存时将抛出OutOfMemoryError异常。同时也是垃圾收集器管理的主要区域,根据对象存活的周期不同,JVM 把堆内存进行分代管理,由垃圾回收器来进行对象的回收管理
。
5. 元空间/方法区 (Method Area)
用于存储被虚拟机已加载的类信息(
构造
方法/接口定义)、静态变量、常量、运行时常量池都存在方法区中,虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是为了和Java的堆区分开(jdk1.8以前hotspot虚拟机叫永久代、持久代,jdk1.8时叫元空间)。
很多人更愿意称方法区为:“永久代(Permanent Generation)”,不过本质上两者并不等价,仅仅是因为习惯使用HotSpot虚拟机的设计团队选择吧GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已,这样HotSpot的垃圾收集器就可以像管理Java堆一样管理这部分内存,能够省去专门为方法区变编写内存管理代码的工作。
Jvm堆内存模型
1. 年轻代
类出生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。
新生代分为两部分:伊甸区(Eden space)和幸存者区(Survivor space),所有的类都是在伊甸区被new出来的。幸存区又分为From和To区。当Eden区的空间用完时,程序又需要创建对象,JVM的垃圾回收器将Eden区进行垃圾回收(Minor GC),将Eden区中的不再被其它对象应用的对象进行销毁。然后将Eden区中剩余的对象
会在两个 Survivor 区交替保存,达到一定次数的对象会晋升到老年代。
2. 老年代
新生代经过多次GC仍然存活的对象移动到老年区。若老年代也满了,这时候将发生Major GC(也可以叫Full GC),进行老年区的内存清理。若老年区执行了Full GC之后发现依然无法进行对象的保存,就会抛出OOM (OutOfMemory Error)异常。
3. 永久代
主要保存类信息等内容,这里的永久代是指对象划分方式,不是专指 1.7 的 PermGen,或者 1.8 之后的 Metaspace。
在JDK1.8之后,元空间替代了永久代,它是对JVM规范中方法区的实现,区别在于元数据区不在虚拟机当中,而是用的本地内存,永久代在虚拟机当中,永久代逻辑结构上也属于堆,但是物理上不属于。
为什么要废除1.7的永久区?
1. 在jdk1.8之前的HotSpot实现中,类的元数据如方法数据、方法信息(字节码、栈和变量的大小)、运行时常量池等被保存在永久代,32位默认永久代大小为64M,64位默认85M,可以通过参数 -XX:MaxPermSize进行设置,一旦类的元数据超过了永久代的大小,就会抛出OOM(内存过大,虚拟机死掉了)异常。
2. 对永久代的调优过程非常困难,永久代的大小很难确定,其中涉及到太多因素,如类的总数,常量池大小和方法数量等,而且永久代的数据可能会随着每一次Full GC而发生移动。
3. 而在jdk1.8中,类的元数据保存在本地内存中,元空间的最大可分配空间就是系统可用内存空间。
4. 官网给的解释:为了融合HotSpot JVM 与 JRockit VM ,因为JRockit VM没有永久代,不需要配置永久代。