【JVM内存区域及创建对象的过程】
JVM内存区域及创建对象的过程
JVM内存区域
JVM 内存区域最粗略的划分可以分为 堆
和栈
,当然,按照虚拟机规范,可以划分为以下几个区域:
JVM 内存分为线程私有区和线程共享区,其中方法区
和堆
是线程共享区,虚拟机栈、本地方法栈 和 程序计数器 是线程隔离的数据区。
1、程序计数器
程序计数器(Program Counter Register)也被称为PC寄存器,是一块较小的内存空间。
它可以看作是当前线程所执行的字节码的行号指示器。
2、Java虚拟机栈
Java虚拟机栈(Java Virtual Machine Stack)也是线程私有的,它的生命周期与线程相同。
Java虚拟机栈描述的是Java方法执行的线程内存模型:方法执行时,JVM会同步创建一个栈帧,用来存储局部变量表、操作数栈、动态连接、方法出口等。
3、本地方法栈
本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。
Java 虚拟机规范允许本地方法栈被实现成固定大小的或者是根据计算动态扩展和收缩的。
4、Java堆
对于Java应用程序来说,Java堆(Java Heap)是虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,Java里“几乎”所有的对象实例都在这里分配内存。
Java堆是垃圾收集器管理的内存区域,因此一些资料中它也被称作“GC堆”(Garbage Collected Heap,)。从回收内存的角度看,由于现代垃圾收集器大部分都是基于分代收集理论设计的,所以Java堆中经常会出现新生代
、老年代
、Eden空间
、From Survivor空间
、To Survivor空间
等名词,需要注意的是这种划分只是根据垃圾回收机制来进行的划分,不是Java虚拟机规范本身制定的。
5.方法区
方法区是比较特别的一块区域,和堆类似,它也是各个线程共享的内存区域,用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
简单说方法区用来存储类型的元数据信息,一个.class文件是类被java虚拟机使用之前的表现形式,一旦这个类要被使用,java虚拟机就会对其进行装载、连接(验证、准备、解析)和初始化。
它特别在Java虚拟机规范对它的约束非常宽松,所以方法区的具体实现历经了许多变迁,例如jdk1.7之前使用永久代作为方法区的实现。
JDK1.6、1.7、1.8内存区域的变化?
JDK1.6、1.7/1.8内存区域发生了变化,主要体现在方法区的实现:
-
JDK1.6使用永久代实现方法区:
-
JDK1.7时发生了一些变化,将字符串常量池、静态变量,存放在堆上
-
在JDK1.8时彻底干掉了永久代,而在直接内存中划出一块区域作为元空间,运行时常量池、类常量池都移动到元空间。
创建对象的过程
在JVM中对象的创建,我们从一个new指令开始:
- 首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用
- 检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,就先执行相应的类加载过程
- 类加载检查通过后,接下来虚拟机将为新生对象分配内存。
- 内存分配完成之后,虚拟机将分配到的内存空间(但不包括对象头)都初始化为零值。
- 接下来设置对象头,对象头里包含了对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。
这个过程大概图示如下:
类的声明周期:
一个类从被加载到虚拟机内存中开始,到从内存中卸载,整个生命周期需要经过七个阶段:加载 (Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化 (Initialization)、使用(Using)和卸载(Unloading),其中验证、准备、解析三个部分统称为连接(Linking)。