一、JVM是什么

JVM全名叫“Java Virtual Machine”,中文名叫“爪哇虚拟机”,是java和java系(如Scala、Kotlin)语言实现平台无关性的关键角色。牛逼但也不玄乎,归根结底它只是一个软件而已,也就是运行于操作系统上的一个应用程序,与即时通讯软件、游戏这些应用程序没有本质区别。但还有一点要说,JVM是一个概念,或者说是一类软件。比如“即时通讯软件”包括QQ、微信、网易泡泡、飞信等等,“游戏软件”包括阴阳师、王者荣耀、和平精英等等。“JVM类软件”包括Oracle Hotspot(Oracle公司持有的Hotspot虚拟机)、Azul Systems Zing(Azul Systems公司基于Hotspot开发的主打低延迟的虚拟机)、Alibaba AJVM(阿里巴巴公司基于OpenJDK开发了自己的JDK,我们姑且称其中的JVM为“Alibaba JVM”,简称“AJVM”)等等。即:

即时通讯软件:

  • QQ、微信、网易泡泡、飞信、......

游戏软件:

  • 阴阳师、王者荣耀、和平精英、......

JVM类软件:

  • Hotspot、Zing、AJVM、......

它们宏观上所处的位置大概是这样的,我顺便标明了Hotspot的作用。

Java虚拟机发展史 java虚拟机被称为什么_Java虚拟机发展史

 

二、JVM的结构是什么样的?

既然是个应用软件,那自然应该有很多内部模块和处理逻辑了,下面我们先看一下它大概长什么样?

这是Hotspot 在jdk1.8中结构的示意图,虽然配色有点野,可是结构该是对的(哪里不对可以评论教教我)。

Java虚拟机发展史 java虚拟机被称为什么_Java虚拟机发展史_02

我们可以看到,它内部大概分为了五部分:类装载器、运行时数据区、本地方法库、本地方法接口、执行引擎。下面逐一说明他们的作用及结构。

1.类装载器

功能

类装载器是用来装载Class文件的,具体的方式有

  • 从本地系统中直接加载
  • 通过网络下载.class文件
  • 从zip,jar等归档文件中加载.class文件
  • 从专有数据库中提取.class文件
  • 将Java源文件动态编译为.class文件

加载流程

类加载的过程包括了加载、验证、准备、解析、初始化五个阶段。其中的验证、准备、解析三个阶段也被称为连接阶段。

Java虚拟机发展史 java虚拟机被称为什么_JVM_03

加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持Java语言的运行时绑定(也称为动态绑定或晚期绑定)

2.运行时数据区

程序运行需要一定的内存空间用来存储诸如程序的代码、类名、方法名、变量名、对象、对象的引用等,这个内存空间就被称为运行时方法区。根据存储的对象的不同,运行时方法区被分为了五个区域。分别是方法区、Java栈、堆、本地方法栈、程序计数器。

Java虚拟机发展史 java虚拟机被称为什么_本地方法_04

2.1 方法区(元空间):

方法区主要存放已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据(比如spring 使用IOC或者AOP创建bean时,或者使用cglib,反射的形式动态生成class信息等)。它是线程共享的,需要考虑线程安全问题。

方法区在jdk1.8中的实现,叫做“元空间”。它被设计放在操作系统的内存之中。

Java虚拟机发展史 java虚拟机被称为什么_本地方法_05

ClassLoader

用来存放类加载器信息。

Class

用来存放类信息。

常量池

用来存放程序运行中的常量。

而在jdk1.8之前的jdk,如jdk1.6中,方法区的实现叫做“永久代”。

Java虚拟机发展史 java虚拟机被称为什么_字符串_06

2.2 堆

英文名叫heap,作用是存储对象实体,即各种类的实例化对象。它是线程共享的,需要考虑线程安全问题。它是GC的主战场。

Java虚拟机发展史 java虚拟机被称为什么_字符串_07

 

新生代

包含伊甸园区,幸存区。幸存区有两块,他们交替作为from区和to区。

老年代

存放从新生代幸存区晋升过来的大龄对象。当新生代连续空间不多的时候,也会有体积很大的对象在此处诞生。

StringTable

串池,当创建一个字符串变量时,会先从串池中搜索是否存在该字符串,若有,则直接引用,若没有,则创建该字符串并将其添加到串池,并引用。它的本质是一个HashTable,大小是固定的不可扩容。

  • 常量池中的字符串仅是符号,第一次用到时才变为对象。
  • 利用串池的机制,来避免重复创建字符串对象。
  • 字符串变量的拼接原理是StringBuilder(1.8)。
  • 字符串常量的拼接原理是编译期优化。
  • 可以使用intern方法,主动将串池中还没有的字符串对象放入串池
  • 1.8 将这个字符串对象尝试放入串池,如果有则并不放入,若没有则放入,会把串池中的对象返回。
  • 1.6 将这个字符串对象尝试放入串池,如果有则并不放入,若没有则复制一份放入,会把串池中的对象返回。

动态拼接的字符串,不会放在串池,只会存在于堆中。除非主动调用intern方法。

(StringTable是从jdk1.7开始,从原来的方法区常量池挪到堆中的)

2.3 本地方法库

本地方法:在java中,存在一些由其他语言实现的方法,比如c++/c,它们通常是用来控制底层资源的方法。它们通常由native修饰。

本地方法库就是用来存放这些所需的本地方法的地方。

2.4 本地方法接口

程序在调用本地方法时,是通过其接口来调用的。这些端口就存放在这里。

2.5 执行引擎

即基于代码逻辑和数据进行运算的地方。是程序的实际执行者。

说明:

jdk1.7的更新:StringTable的移动

jdk1.8的更新:方法区的重新实现

GC:内容过多,需要单独开一篇来讲