每个java开发同学不论是日常工作中还是面试里,都会遇到JDK、JVM和GC的问题。本文会从以下10个问题为切入点,带着大家一起全面理解一下JVM的方方面面。

  1. JVM、JRE和JDK的区别和分割
  2. JVM是什么?以及它的次要作用
  3. JVM的外围性能有哪些
  4. 类加载机制和过程
  5. 运行时数据区的逻辑构造
  6. JVM的内存模型
  7. 如何确定对象是垃圾
  8. 垃圾收集的算法有哪些
  9. 各种问世的垃圾收集器
  10. JVM调优的参数配置

1、JVM、JRE和JDK的区别和分割

这个根本是步入java世界的入门级常识认知,首先咱们来看一下来自java官网的一张图:


从这张图里咱们根本就能够看出“JRE”是运行Java语言编写的程序所不可短少的运行环境。有了JRE咱们写的java程序才能够运行起来被用户所应用。

而“JDK”俗称java开发工具包,它包含了Java运行环境JRE(Java Runtime Envirnment)以及一堆Java工具(javac/java/jdb等)和Java根底的类库(即Java API 包含rt.jar)。

但不论是JRE还是JDK都是以JVM为基石的。能够说JVM是java程序能够在某台机器上得以运行的最底层的保障。

2、那么什么是JVM?它的次要作用又是什么?

JVM是Java Virtual Machine(Java虚拟机)的缩写,它的用处简略的说就是它能让咱们写的java程序在不同的操作系统的不同CPU上运行。咱们写的java程序会利用开发工具(如Intellij idea)把它编译成.class文件,但这个class文件是不能间接被操作系统辨认运行的,须要利用jvm按jvm标准将编译好的.class文件转变成机器语言,再交由操作系统提交给cpu去执行。

用一句话评估JVM的次要作用就是:JVM屏蔽了与具体操作系统平台相干的信息,使得Java程序只需生成在Java虚拟机上运行的指标代码(字节码),就能够在多种平台上不加批改地运行。

3、这么牛的JVM的外围性能有哪些?

JVM中外围的性能总体有三块:

  1. 类加载器:在JVM启动时或者在类运行时将须要的class文件加载到JVM中
  2. 执行引擎:负责执行class文件,包含调配运行时数据区(如程序计数器、本地办法栈和虚构栈)和 最终将class中的字节码指令转为机器指令通过操作系统交给CPU执行
  3. 垃圾回收器:对JVM的堆内存进行治理,及时回收调无用的资源开释内存空间

4、JVM类的加载机制和过程?

首先,咱们谈谈开发工具编译生成的class文件是如何被JVM加载的。所谓的类加载机制其实就是:虚拟机(JVM)把class文件加载到内存中,而后对它进行正确性的校验,查看通过再进行解析和初始化,最终把class文件变成一个内存中能够间接应用的java.lang.Class对象。

从一个class文件的装载到销毁,它的生命周期根本能够分为以下五个阶段:装载、链接(验证、筹备和解析)、初始化、应用和卸载。

  1. 装载:装载(Load)阶段总共有三项工作

    (1)通过类的全限定名获取其定义的二进制字节流,须要借助类装载器(ClassLoader)实现;

    (2)在运行时数据区的“办法区”中调配一块区域保留这个类的信息,包含类的根本信息、常量和动态变量等等;

    (3)在“Java堆”内存上生成一个该类的java.lang.Class对象,用于对外裸露应用该类的入口。

  2. 链接:链接(link)阶段同样有三项工作

    (1)验证(Verify),验证文件格式、元数据、字节码和符号援用,以保障被加载类的准确性;

    (2)筹备(Prepare),为动态变量分配内存并初始化为默认值。

    (3)解析(Resolve),解析阶段是虚拟机将常量池内的符号援用替换为间接援用的过程。解析动作次要针对类或接口、字段、类办法、接口办法、办法类型、办法句柄和调用限定符7类符号援用进行。

  3. 初始化:初始化(Initialize)阶段所做的工作就是对类的动态成员变量和静态方法进行初始化赋值或调用。

比方下面的动态变量age初始化之后的值变为了10。

在装载阶段的第(2),(3)步能够发现有运行时数据区,堆,办法区等名词,那么到底什么是“运行时数据区”,它有哪些构造形成?

5、什么是JVM运行时数据区?及其逻辑构造

“运行时数据区”是JVM在执行Java程序的过程中出于内存治理方面的目标,在设计上把内存分为若干个不同的区域。这些区域有着各自的用处,有的区域生命周期跟虚拟机一样,随着虚拟机过程的启动而存在,随同这虚拟机的过程完结而沦亡。而有些区域则依赖用户线程的启动和完结而建设和销毁。具体如下图:

  1. 办法区(Method Area):

(1)用于存储已被虚拟机加载的类信息、常量、动态变量、即时编译器编译后的代码等数据;

(2)办法区是各个线程共享的内存区域,在虚拟机启动时创立,因为同一个class类信息只须要加载一份就够了;

(3)java虚拟机标准中把办法区形容为堆内存的一个逻辑局部,但它有另外一个别名叫“非堆”,用于与java堆辨别开来。在JDK8之前办法区叫做Perm space,在JDK8及当前叫做Metaspace(即元数据区)。

  1. 堆(Heap):Java堆是被所有线程共享,虚拟机启动时创立,此内存区域惟一的目标就是寄存对象实例,在Java虚拟机标准中的形容是:所有的对象实例以及数组都要在堆上调配,然而随着JIT编译器的倒退和逃逸剖析技术逐步成熟,栈上调配,标量替换优化技术将会导致一些奥妙的变动产生,所有的对象都调配在堆上也就变得不那么相对了。
  2. 虚拟机栈(Java Virtual Machine Stacks):虚拟机栈是线程公有的或者说是独有的,随着线程的创立而创立。一个线程的运行状态(正在调用哪个办法),就是由这个线程对应的虚拟机栈来保留的。

每一个被线程执行的办法,为虚拟机栈中的一个栈帧,调用一个办法,就会向栈中压入一个栈帧;一个办法调用实现,就会把该栈帧从栈中弹出。如下图解:

  1. 程序计数器(The Pc Register):咱们都晓得一个JVM过程中有多个线程在执行,而线程中的内容是否可能领有执行权,是依据CPU调度来的。如果线程A正在执行到某个中央,忽然失去了CPU的执行权,切换到线程B了,而后当线程A再取得CPU执行权的时候,怎么能继续执行呢?这就是须要在线程中保护一个变量,记录线程执行到的地位,这就是程序计数器。
  2. 本地办法栈(Native Method Stacks):本地办法栈与虚拟机栈所施展的作用十分类似,他们之间的区别不过是虚拟机栈为虚拟机执行Java办法(字节码)服务,而本地办法栈则为虚拟机中应用到的native办法服务。即如果以后线程执行的办法是Native类型的,这些办法就会在本地办法栈中执行。

总结一下,就JVM的设计规范,从应用用处角度JVM的内存大体的分为:线程公有内存区 和 线程共享内存区。

线程公有内存区在类加载器编译某个class文件时就确定了执行时须要的“程序计数器”和“虚构栈帧”等所需的空间,并且会随同着以后执行线程的产生而产生,执行线程的沦亡而沦亡,因而“线程公有内存区”并不需要思考内存治理和垃圾回收的问题。

线程共享内存区在虚拟机启动时创立,被所有线程共享,是Java虚拟机所治理内存中最应该关注的和最大的一块。

那么JVM内存模型是如何设计的?JVM又是如何进行内存治理(也就是垃圾回收)的?垃圾回收算法有哪些?目前罕用的垃圾回收器又有哪些?我会在下篇文章跟您独特解答这些问题。

作者:宜信技术学院 谭文涛