前言
要想了解JAVA底层架构,首先需要知道JAVA语言是如何转换成操作系统能够识别的机器语言,举个例子,我们编写了一个HelloWord程序,那么它是如何运行的呢?
用非官方的理解就是,Java虚拟机将编译后的.class文件,翻译成机器能识别的机器指令,在Linux和Windows环境下可能略有不同,从而实现了跨平台、跨语言。
JVM结构
在1.8版本以前,JVM主要由5大部分组成:
堆
方法区
虚拟机栈
本地方法栈
程序计数器
虚拟机栈
什么是虚拟机栈
虚拟机栈(Java Virtual Machine Stacks),也就是我们俗称的JAVA栈,最主要的作用就用来存储当前线程运行方法时所需要的数据、指令、返回地址;在每个方法执行时,虚拟机栈都会创建一个栈帧(Stack Frame),用于存储:局部变量表、操作数栈、动态链接、方法出口等信息。
一些名词解释
栈帧
是用来存储数据和部分过程结果的数据结构。
局部变量
局部变量表存放了编译器可知的8种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用 和returnAddress类型(指向了一条字节码指令的地址)
操作数栈
操作数栈是用来存储在程序运行过程中产生的常量或者变量,遵循后进先出原则。
本地方法栈
本地方法栈是与虚拟机栈发挥的作用十分相似,区别是虚拟机栈执行的是Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的native方法服务,可能底层调用的c或者c++,我们打开jdk安装目录可以看到也有很多用c编写的文件,可能就是native方法所调用的c代码。
Native方法
指得就是Java程序调用了非Java代码,算是一种引入其它语言程序的接口
程序计数器
记录当前线程正在执行的字节码(.class)的地址,它是一块较小的内存空间,可以看做是指向当前线程所执行的字节码的行号指示器。类似我们在程序debug时的指针,可以向我们反馈程序具体的执行地址。
堆
在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor;
这样划分的目的是为了使JVM能够更好的管理堆内存中的对象,包括内存的分配以及回收。java堆是java虚拟机管理的内存中最大的一块,java堆是被所有线程共享的一块内存区域,堆的唯一目的就是存放实例对象,几乎所有的对象实例都在这里分配内存。
对象优先在Eden分配
大对象直接进入老年代
长期存活的对象将进入老年代
方法区(元空间)
在JDK1.8以后,JVM提供了一个新的概念叫元空间,替换了原有的方法区。
用于存储已被虚拟机加载的类信息、常量、静态变量以及运行时常量池存,如static修饰的变量加载类的时候就被加载到方法区中。
在老版jdk,方法区也被称为永久代,因为没有强制要求方法区必须实现垃圾回收,HotSpot虚拟机以永久代来实现方法区,从而JVM的垃圾收集器可以像管理堆区一样管理这部分区域,从而不需要专门为这部分设计垃圾回收机制。不过自从JDK7之后,Hotspot虚拟机便将运行时常量池从永久代移除了。
静态变量 + 常量 + 类信息(构造方法/接口定义) + 运行时常量池存在方法区中 。
元空间的特点:
每个加载器有专门的存储空间。
不会单独回收某个类。
元空间里的对象的位置是固定的。
如果发现某个加载器不再存货了,会把相关的空间整个回收。