每一个java开发同窗不论是平常工做中仍是面试里,都会遇到JDK、JVM和GC的问题。本文会从如下10个问题为切入点,带着你们一块儿全面了解一下JVM的方方面面。java
这个基本是步入java世界的入门级知识认知,首先咱们来看一下来自java官网的一张图:面试
而“JDK”俗称java开发工具包,它包括了Java运行环境JRE(Java Runtime Envirnment)以及一堆Java工具(javac/java/jdb等)和Java基础的类库(即Java API 包括rt.jar)。算法
但不论是JRE仍是JDK都是以JVM为基石的。能够说JVM是java程序能够在某台机器上得以运行的最底层的保障。数组
JVM是Java Virtual Machine(Java虚拟机)的缩写,它的用途简单的说就是它能让咱们写的java程序在不一样的操做系统的不一样CPU上运行。咱们写的java程序会利用开发工具(如Intellij idea)把它编译成.class文件,但这个class文件是不能直接被操做系统识别运行的,须要利用jvm按jvm规范将编译好的.class文件转变成机器语言,再交由操做系统提交给cpu去执行。jvm
用一句话评价JVM的主要做用就是:JVM屏蔽了与具体操做系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就能够在多种平台上不加修改地运行。ide
JVM中核心的功能整体有三块:工具
首先,咱们谈谈开发工具编译生成的class文件是如何被JVM加载的。所谓的类加载机制其实就是:虚拟机(JVM)把class文件加载到内存中,而后对它进行正确性的校验,检查经过再进行解析和初始化,最终把class文件变成一个内存中能够直接使用的java.lang.Class对象。开发工具
从一个class文件的装载到销毁,它的生命周期基本能够分为如下五个阶段:装载、连接(验证、准备和解析)、初始化、使用和卸载。优化
装载:装载(Load)阶段总共有三项工做idea
(1)经过类的全限定名获取其定义的二进制字节流,须要借助类装载器(ClassLoader)完成;
(2)在运行时数据区的“方法区”中分配一块区域保存这个类的信息,包括类的基本信息、常量和静态变量等等;
(3)在“Java堆”内存上生成一个该类的java.lang.Class对象,用于对外暴露使用该类的入口。
连接:连接(link)阶段一样有三项工做
(1)验证(Verify),验证文件格式、元数据、字节码和符号引用,以保证被加载类的准确性;
(2)准备(Prepare),为静态变量分配内存并初始化为默认值。
(3)解析(Resolve),解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。解析动做主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符7类符号引用进行。
初始化:初始化(Initialize)阶段所作的工做就是对类的静态成员变量和静态方法进行初始化赋值或调用。
好比上面的静态变量age初始化以后的值变为了10。
在装载阶段的第(2),(3)步能够发现有运行时数据区,堆,方法区等名词,那么究竟什么是“运行时数据区”,它有哪些结构构成?
“运行时数据区”是JVM在执行Java程序的过程当中出于内存管理方面的目的,在设计上把内存分为若干个不一样的区域。这些区域有着各自的用途,有的区域生命周期跟虚拟机同样,随着虚拟机进程的启动而存在,伴随这虚拟机的进程结束而消亡。而有些区域则依赖用户线程的启动和结束而创建和销毁。具体以下图:
(2)方法区是各个线程共享的内存区域,在虚拟机启动时建立,由于同一个class类信息只须要加载一份就够了;
(3)java虚拟机规范中把方法区描述为堆内存的一个逻辑部分,但它有另一个别名叫“非堆”,用于与java堆区分开来。在JDK8以前方法区叫作Perm space,在JDK8及之后叫作Metaspace(即元数据区)。
堆(Heap):Java堆是被全部线程共享,虚拟机启动时建立,此内存区域惟一的目的就是存放对象实例,在Java虚拟机规范中的描述是:全部的对象实例以及数组都要在堆上分配,可是随着JIT编译器的发展和逃逸分析技术逐渐成熟,栈上分配,标量替换优化技术将会致使一些微妙的变化发生,全部的对象都分配在堆上也就变得不那么绝对了。
虚拟机栈(Java Virtual Machine Stacks):虚拟机栈是线程私有的或者说是独有的,随着线程的建立而建立。一个线程的运行状态(正在调用哪一个方法),就是由这个线程对应的虚拟机栈来保存的。
每个被线程执行的方法,为虚拟机栈中的一个栈帧,调用一个方法,就会向栈中压入一个栈帧;一个方法调用完成,就会把该栈帧从栈中弹出。以下图解:
程序计数器(The Pc Register):咱们都知道一个JVM进程中有多个线程在执行,而线程中的内容是否可以拥有执行权,是根据CPU调度来的。假如线程A正在执行到某个地方,忽然失去了CPU的执行权,切换到线程B了,而后当线程A再得到CPU执行权的时候,怎么能继续执行呢?这就是须要在线程中维护一个变量,记录线程执行到的位置,这就是程序计数器。
本地方法栈(Native Method Stacks):本地方法栈与虚拟机栈所发挥的做用很是类似,他们之间的区别不过是虚拟机栈为虚拟机执行Java方法(字节码)服务,而本地方法栈则为虚拟机中使用到的native方法服务。即若是当前线程执行的方法是Native类型的,这些方法就会在本地方法栈中执行。
总结一下,就JVM的设计规范,从使用用途角度JVM的内存大致的分为:线程私有内存区 和 线程共享内存区。
线程私有内存区在类加载器编译某个class文件时就肯定了执行时须要的“程序计数器”和“虚拟栈帧”等所需的空间,而且会伴随着当前执行线程的产生而产生,执行线程的消亡而消亡,所以“线程私有内存区”并不须要考虑内存管理和垃圾回收的问题。
线程共享内存区在虚拟机启动时建立,被全部线程共享,是Java虚拟机所管理内存中最应该关注的和最大的一块。
那么JVM内存模型是如何设计的?JVM又是如何进行内存管理(也就是垃圾回收)的?垃圾回收算法有哪些?目前经常使用的垃圾回收器又有哪些?我会在下篇文章跟您共同解答这些问题。
做者:宜信技术学院 谭文涛