JDK、JRE的区别
JDK:Java程序设计语言、Java虚拟机、Java类库
JRE:Java类库API中的Java SE API子集、Java虚拟机
运行时数据区域
JVM在程序运行时,会将自己管理的内存空间分为一下几个区域,实现内存的高效利用。每个区域各自的用途都不尽相同,所以它们的创建时间和销毁时间也有所不同,有些跟着虚拟机进程来创建销毁,有的则根据用户线程进行创建和销毁。
根据《Java虚拟机规范》,将内存划分为一下几个运行时数据区域:
接下来我们来看看,具体每一个区域的作用和它们所存储的内容。
程序计数器
当前线程执行的字节码的行号指示器,记录正在执行的虚拟机字节码指令的地址
在Java程序运行的过程中,在我们编译器中编写的只是我们的Java文件,可以称之为源文件。而真正在JVM中运行的则是我们源文件经过编译之后对应的class文件。而在虚拟机运行我们的class文件时,就是通过程序计数器在执行过程中,进行程序控制,分支、循环、跳转、异常处理、线程恢复等都需要借助这个计数器来完成。但是当执行本地方法(native)时,该计数器的值为0。
拓展知识:Java中的多线程
Java虚拟机中的多线程时通过各个线程的轮流切换(上下文切换),分配给处理器执行线程所花费时间的不同来实现的,所以,每一个线程都会对应自己的一个程序计数器,实现各个线程之间的隔离操作,我们将这一块区域称为线程私有区域
虚拟机栈
和程序计数器一样,这个内存区域也是线程私有的,它描述的是
Java方法执行时线程内存模型
在Java方法运行时,Java虚拟机会创建一个虚拟栈帧,用来存储局部变量表,操作数帧、动态连接、方法出口等信息。当方法执行时,将创建出来的栈帧放入虚拟机栈中,当方法执行结束,就将该栈帧弹出虚拟机栈。
扩展知识:局部变量表中的数据
1、编译期可知的各种Java虚拟机的八种基本数据类型(byte、boolean、short、char、int、float、double、long)
2、对象引用(用于对象访问定位,可能是一个直接指向对象实例的引用地址,也可能是指向代表对象的一个句柄)
3、retureAddress类型:一条字节码指令的地址
本地方法栈
与虚拟机栈的作用类似,虚拟机栈为Java虚拟机运行时提供Java方法(字节码),而本地方法栈为Java虚拟机运行时提供本地方法(native)。
Java堆
存放对象实例,是Java虚拟机管理空间最大的一块内存,艾Java虚拟机启动的时候创建,它同时也是需要垃圾回收的一块内存区域。
方法区
与Java堆一样,是线程共享的内存区域,用于存放已经被Java虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。这其中还包括一个运行时常量池,用来存放各种字面量和符号引用。
- 字面量:比较接近Java语言中的常量概念,如文本字符串、被声明为final的常量值等。
- 符号引用:编译原理方面的概念,主要包括以下方面
1、被模块导出或开放的包(Package)
2、类和接口的全限定名(Fully Qualified Name)
3、字段的名称和描述符(Descriptor)
4、方法的名称和描述符
5、方法的句柄和方法类型(Method Handle、Method Type、Dynamic Invoke)
6、动态调用点和动态常量
对于方法区常量的回收,主要回收的是废弃的常量和不再使用的类型,对于废弃常量的回收相对来说,相对容易理解一点,当程序中没有任何地方引用到常量池中的字符串变量,则在垃圾回收时,就会将改字符串常量会被系统清出方法区。但是注意如何判断一个类型属于“一个不再使用的类”:有下面三点需要注意
1、该类的所有实例都已经被回收,也就是说Java堆中在没有该类以及该类派生出子类的实例
2、加载该类的类加载器已经被回收
3、该类对应得java.lang.Class对象在任何地方都没有被引用,无法在任何地方通过反射访问该类的方法
满足这三个条件,我们可以说该类被允许被JVM回收了。
小拓展
在 JDK 6 之前,方法区还不叫方法区,那时候还有永久代的概念,当时已经提出要放弃永久代,逐步采用本地内存来实现方法区的计划了。到 JDK 7,开发者将原本放在永久代中的字符串常量池、静态变量等移出。到 JDK 8,就废弃了永久代,而是用在本地内存中实现的元空间代替