JDK1.7及之前的JVM模型

JDK1.8及之后的JVM模型

堆与方法区是线程共享的
虚拟机栈、本地方法栈、程序计数器则是线程私有的
本地方法栈: 功能与Java虚拟机栈十分相同。区别在于,本地方法栈为虚拟机使用到的native方法服务,具体的虚拟机可以根据需要自由实现它,甚至有的Java虚拟机(譬如Hot-Spot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。
程序计数器:多线程情况下需要线程切换执行,用来记录当前线程执行到哪里了,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成.
如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地(Native)方法,这个计数器值则应为空(Undefined)。此内存区域是唯一一个在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域。
虚拟机栈:存储当前线程运行方法时所需要的数据,用来存储方法的传参、局部变量(即基本数据类型和对象引用)、方法出口等
这些数据类型在局部变量表中的存储空间以局部变量槽(Slot)来表示,其中64位长度的long和double类型的数据会占用两个变量槽,其余的数据类型只占用一个。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。

方法区:存储类信息、常量池、静态变量、即时编译后的代码等数据
JDK7中将方法区中的字符串常量池放到了堆中,而其他常量池,比如运行时常量池(就是编译后的类信息,比如类信息,编译后的代码数据,静态常量池)。
JDK8中将永久代移除,转而用元空间(在主内存中)实现
(1)方法区是规范层面的东西,规定了这一个区域要存放哪些东西
(2)永久代或者是元空间是对方法区的不同实现,是实现层面的东西。
为什么要放到主内存中?
1.为了融合其他JVM
官方文档:移除永久代是为融合HotSpot JVM与 JRockit VM而做出的努力,因为JRockit没有永久代,不需要配置永久代2..永久区的大小比较难设置,容易发生OOM
而且应该为PermGen分配多大的空间很难确定,因为PermSize的大小依赖于很多因素,比如JVM加载的class总数,常量池的大小,方法的大小等。
堆:
唯一目的就是存放实例对象,几乎所有的对象实例都在这里分配内存。
注意,常量池虽然在《JAVA虚拟机规范》中属于方法区中内容,但是部分具体实现(Sring常量池和,除了Double和Float两者的基本类型包装类)在JDK7后已经被放在堆中来实现了。
也是发生垃圾收集的地方

老年代 : 三分之二的堆空间
年轻代 : 三分之一的堆空间
eden区: 8/10 的年轻代空间
survivor0 : 1/10 的年轻代空间
survivor1 : 1/10 的年轻代空间
举个例子:
Student s = new Student(“小明”,18);
s 是指针,存放在栈中。
new Student(“小明”,18) 是对象 ,存放在堆中。
Student 类的信息存放在方法区。
网友评论