一、首先来看JDK1.7到JDK1.8的变化:
由图我们可以直观的看到,方法区不见了,伴生出的是元数据空间,而且这部分空间是在本地内存中的,也就是说它的大小不再受制于JVM内存的大小,而是和机器内存有关
JDK 1.8同JDK 1.7 最大的区别是:元数据取代了永久代,元空间的本质和永久代类似,都是对JVM规范中的方法区的实现,其元空间和永久代之间的最大区别在于:元数据空间不在虚拟机中,而是在本地内存中
JVM内存划分:
① 方法区 (线程共享) :常量 静态变量 JIT(即时编译器)编译后代码也在方法区存放
② 堆内存(线程共享) :垃圾回收的主要场地
③ 程序计数器 :当前线程执行的字节码的位置指示器
④ Java虚拟机栈(栈内存) :保存局部变量,基本数据类型以及堆内存中对象的引用变量
⑤ 本地方法栈 (C栈) :为JVM提供使用native方法的服务
什么是native本地方法?:被native关键字修饰的方法叫做本地方法;本地方法和其它方法不一样,本地方法意味着和平台有关,因此使用了native的程序可移植性都不太高;另外native方法在JVM中运行时数据区也和其它方法不一样,它有专门的本地方法栈;native方法主要用于加载文件和动态链接库,由于Java语言无法访问操作系统底层信息(比如:底层硬件设备等),这时候就需要借助C语言来完成了;被native修饰的方法可以被C语言重写
详细了解一下各个部分:
(1)、程序计数器(PC寄存器)
程序计数器的定义:是当前线程正在执行的哪一条字节码指令的地址,若当前线程正在执行的是一个本地方法,那么此时程序计数器为Undefined
程序计数器的作用:字节码解释器通过改变程序计数器来依次获取指令,从而实现代码的流程控制;在多线程的情况下可以记录当前线程执行 的位置
程序计数器的特点:一块较小的内存空间
线程私有,每个线程都有自己的程序计数器
生命周期:随着线程的创建而创建,随着线程的销毁而销毁
是唯一一个不会出现OutOfMemoryError的内存区域
(2)、Java虚拟机栈
定义: 描述Java方法运行过程的内存模型
Java虚拟机栈会为每一个即将运行的Java方法创建一块叫做"栈帧"的区域,用于存放该方法运行过程中的一些信息,如局部变量表 、操作数栈、动态链接、方法出口信息等等
由于Java虚拟机栈是线程对应的,数据不是共享的,因此不用关心数据一致性问题,也不会存在同步锁的问题,也是线程私有,随着线程的创建而创建,随着线程的销毁而销毁
(3)、本地方法栈(C栈)
定义: 是为了JVM运行native方法准备的空间,由于很多native方法都是用C语言实现的,所以通常又叫C栈,它与Java虚拟机栈实现的功能类似,只不过本地方法栈描述本地方法运行过程的内存模型
(4)、 堆
定义: 堆是用来对象的内存空间,几乎所有的对象都存储在堆中,是垃圾回收的主要场地,分为新生代和老生代,根据不同的区域存放不同的生命周期对象,并使用不同的垃圾回收算法
(5)、方法区
定义: Java虚拟机规范中定义方法区是堆的一个逻辑部分,方法区存放以下信息:已被虚拟机加载的类信息 /常量 /静态变量 /即时编译后代码,方法区的信息一般需要长期存在,用堆的划分方法,把方法区称为永生代,不过其内存回收效率低下,回收一遍只有少量的信息无效,回收它的主要目的是:对常量池的回收、对类型的卸载
运行时常量池:
方法区中存放:类信息 、常量 、静态变量 、即时编译器变编译后代码;常量就存放在运行时常量池中,当类被Java虚拟机加载后,class文件中的常量就存在方法区的运行常量池,而且在运行期间,可以向常量池中添加新的常量,如String类的intern()方法就能在运行期间向常量池中添加字符串常量