Java程序员必备的JVM学习

java程序员必备的JVM学习


文章目录

  • 前言
  • JVM概述
    • 为什么要学习jvm
    • 虚拟机
    • JVM作用
    • JVM位置
    • JVM整体组成可分为以下四个部分
    • 各组成部分用途
    • java代码的执行流程
    • JVM架构模型
  • JVM类加载
    • 类加载子系统
    • 类加载的角色
    • 类加载过程
      • 加载
      • 链接
      • 初始化
        • 类什么时候初始化/li>
        • 类的初始化顺序
    • 类加载器分类
    • 双亲委派机制
      • 工作原理
      • 优点
    • 类的主动使用/被动使用
  • JVM运行时数据区
    • 运行时数据区组成概述
    • 程序计数器(Program Counter Register)
    • Java 虚拟机栈(Java Virtual Machine Stacks)
      • 背景
      • 分清栈和堆
      • Java 虚拟机栈是什么
      • 作用
      • 栈的特点
      • 栈中出现的异常
      • 栈中存储什么/li>
      • 栈的运行原理
      • 栈帧的内部结构
    • 本地方法栈
    • Java 堆内存
      • 概述
      • 堆内存区域划分
      • 为什么分区(代)
      • 对象创建内存分配过程
      • 新生区与老年区配置比例
      • 分代收集思想 Minor GC、Major GC、Full GC
      • TLAB 机制
      • 堆空间的参数设置
      • 字符串常量池
    • 方法区
      • 概述
      • 方法区大小设置
      • 方法区的内部结构
      • 方法区的垃圾回收
    • 本地方法接口
      • 什么是本地方法
      • 为什么要使用 Native Method
    • 执行引擎
      • 概述
      • 什么是解释器么是JIT编译器
      • 为什么Java是半编译半解释型语言/li>
  • 垃圾回收
    • 垃圾回收概述
      • 概述
      • 什么是垃圾
      • 为什么需要GC/li>
      • 早期垃圾回收
    • Java 垃圾回收机制
      • 自动内存管理
      • 应该关心哪些区域的回收/li>
    • 垃圾回收相关算法
      • 垃圾标记阶段算法
        • 标记阶段目的
        • 引用计数算法
        • 可达性分析算法
        • 对象的 finalization 机制
        • 生存还是死亡
      • 垃圾回收阶段算法
        • 标记-清除算法
        • 复制算法
        • 标记-压缩算法
        • 垃圾回收算法小结
        • 分代收集
    • 垃圾回收相关概念
      • System.gc() 的理解
      • 内存溢出与内存泄漏
        • 内存溢出
        • 内存泄漏
        • 常见例子
      • Stop the World
      • 对象引用
        • 概述
        • 强引用
        • 软引用(Soft Reference):内存不足即回收
        • 弱引用(Weak Reference)发现即回收
        • 虚引用(Phantom Reference):对象回收跟踪
    • 垃圾回收器
      • 概述
      • 垃圾回收器分类
      • 性能指标
      • HotSpot 垃圾回收器
        • 1.Serial 垃圾收集器(单线程)
        • 2.Serial Old 垃圾收集器(单线程)
        • 3.ParNew 垃圾收集器(多线程)
        • 4.Parallel Scavenge 垃圾收集器(多线程)
        • 5.Parallel Old 垃圾收集器(多线程)
        • 6. CMS 回收器(低延迟)
        • 7.G1(Garbage First)回收器(区域划分代式)

前言

JVM的学习毋庸置疑是非常重要的,在面试中也是非常常见 本次的内容比较多,如果您能耐心看完,我想一定能对你有帮助


JVM概述

为什么要学习jvm

JVM是中高级程序员必备技能,可以对项目进行管理,对性能进行调优

虚拟机

? 所谓虚拟机,就是一台虚拟机。它是一款软件,用来执行一系列虚拟计算机指令。大体上,虚拟机可以分为系统虚拟机和程序虚拟机。

? 大名鼎鼎的VMware就属于虚拟机,它是完全对物理计算机的仿真,提供了一个可运行完整操作系统的软件平台。程序虚拟机典型的代表就是java虚拟机了,它专门为执行某个单个计算机程序而设计。在java虚拟机中执行的指令我们称为java字节码指令。

? java虚拟机是一种执行java字节码文件的虚拟计算机,它拥有独立的 运行机制。

? java技术的核心就是java虚拟机,因为所有的java程序都运行在java虚拟机内部。

JVM作用

Java程序员必备的JVM学习

JVM位置

Java程序员必备的JVM学习

JVM整体组成可分为以下四个部分

1、类加载器(ClassLoader)

2、运行时数据区(Runtime Data Area)

3、执行引擎(Execution Engine)

4、本地库接口(Native Interface)

Java程序员必备的JVM学习

各组成部分用途

程序在执行之前先要把java代码转换成字节码(class文件),JVM首先需要把字节码通过一定的方式类加载器(ClassLoader)把文件加载到内存中的运行时数据区(Runtime Data Area),而字节码文件是JVM的一套指令集规范,并不能直接交给底层操作系统去执行。因此需要特定的命令解析器**执行引擎(Execution Engine)将字节码翻译成底层操作系统指令再交由CPU去执行,而这个过程中需要调用其他语言的接口本地库接口(Native Interface)来实现整个程序的功能,这就是这4个组成部分的职责与功能。

而我们通常所说的JVM组成指的是运行时数据区(Runtime Data Area),因为通常需要程序员吊事分析的区域就是”运行时数据区“,或者更具体的来说就是”运行时数据区“里面的Heap(堆)模块。

java代码的执行流程

Java程序员必备的JVM学习

java编译器编译过程中,任何一个结点执行失败就会造成编译失败。虽然各个平台的java虚拟机内部实现细节不尽相同,但是它们执行的字节码内容确实一样的。

JVM主要任务就是负责将字节码装载到其内部,解释/编译为对应平台上的机器指令执行。JVM使用类加载器(ClassLoader)装载class文件。

类加载完成后,会进行字节码校验,字节码校验通过之后JVM解释器会把字节码翻译成机器码交由操作系统执行。

但不是所有的代码都是解释执行,JVM对此作了优化,比如HotSpot虚拟机,它本身提供了JIT(Just In Time)编译器。

JVM架构模型

java编译器输入的指令流基本上是一种基于栈的指令集架构,另一种指令集架构是基于寄存器的指令集架构

这两种架构之间的区别:

基于栈式架构的特点

? 设计和实现更简单,适用于资源受限的系统。

? 使用零地址指令方式分配,其执行过程依赖于操作栈,指令集更小,编译器容易实现。

基于寄存器式架构特点:

? 指令完全依赖于硬件,可移植性差。

? 性能优秀,执行更高效。

? 完成一项操作使用的指令更少。

Java程序员必备的JVM学习

类加载器子系统负责从文件系统或者网络中加载 class 文件。 classLoader 只

负责 class 文件的加载,至于它是否可以运行,则由 Execution Engine 决定。

加载的类信息存放于一块称为方法区(元空间)的内存空间。

类加载的角色

类加载过程

加载

? 根据类的地址,从硬盘上读取类的信息,

? 将信息读入到方法区,生成Class类的对象

链接

验证:检验被加载的类是否有正确的内部结构,并和其他类协调一致;

? 对文件格式的验证:class 文件在文件开头有特定的文件标识(字节

码文件都以 CA FE BA BE 标识开头);主,次版本号是否在当前 java 虚拟机接收

范围内。

? 对元数据的验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合java 语言规范的要求,例如这个类是否有父类;是否继承浏览不允许被继承的类 (final 修饰的类)…

准备:准备阶段则负责为类的静态属性分配内存,并设置默认初始值

? 不包含用 final 修饰的 static 常量,在编译时进行初始化

例如: public static int value = 123;

? value 在准备阶段后的初始值是 0,而不是 123。

解析:将类的二进制数据中的符号引用替换成直接引用(符号引用是 Class 文

件的逻辑符号,直接引用指向的方法区中某一个地址)。

初始化

类什么时候初始化/h4>

1 )创建类的实例,也就是 new一个对象

2)访问某个类或接口的静态变量,或者对该静态变量赋值

3)调用类的静态方法

4)反射(Class.forName(“”))

5)初始化一个类的子类(会首先初始化子类的父类)

类的初始化顺序

对 static 修饰的变量或语句块进行赋值.

如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。

如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。

顺序是:父类 static –> 子类 static –> 父类构造方法- -> 子类构造方法

类加载器分类

站在 JVM 的角度看,类加载器可以分为两种:

  1. 引导类加载器(启动类加载器 Bootstrap ClassLoader). (java语言写的)
  2. 其他所有类加载器,这些类加载器由 java 语言实现,独立存在于虚拟机外部,并且全部继承自抽象类 java.lang.ClassLoader。(其他语言写的)

站在 java 开发人员的角度来看,类加载器就应当划分得更细致一些。自 JDK1.2 以来 java 一直保持者三层类加载器。

Java程序员必备的JVM学习

用户自定义类加载器

例如Tomcat

双亲委派机制

Java 虚拟机对 class 文件采用的是按需加载的方式,也就是说当需要该类时才会 将它的 class 文件加载到内存中生成 class 对象.而且加载某个类的 class 文件时,Java 虚拟机采用的是双亲委派模式,即把请求交由父类处理,它是一种任务委 派模式.

Java程序员必备的JVM学习

Java 虚拟机定义了序运行期间会使用到的运行数据区,其中有一些会随着虚拟机启动而创建,随着虚拟机退出而销毁.另外一些则是与线程一一对应的.这些与 线程对应的区域会随着线程开始和结束而创建销毁.

如图:红色的为多个线程共享,灰色的为单个线程私有的,即线程间共享:堆,方法区.

线程私有:程序计数器,栈,本地方法栈

Java程序员必备的JVM学习

栈的特点

栈是一种快速有效的分配存储方式,访问速度仅次于程序计数器。

JVM 直接对 java 栈的操作只有两个:1、调用方法,进栈 2、执行结束后出栈

对于栈来说不存在垃圾回收问题.

Java程序员必备的JVM学习

不同线程中所包含的栈帧(方法)是不允许存在相互引用的,即不可能在一个栈中 引用另一个线程的栈帧(方法).

如果当前方法调用了其他方法,方法返回之际,当前栈帧会传回此方法的执行结果给前一个栈帧,接着虚拟机会丢弃当前栈帧,使得前一个栈帧重新成为当前栈帧.Java 方法有两种返回的方式,一种是正常的函数返回,使用 return 指令,另一种是 抛出异常.不管哪种方式,都会导致栈帧被弹出.

栈帧的内部结构

局部变量表、操作数栈、动态链接、方法返回地址

局部变量表

? 局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局 部变量。对于基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用。

操作数栈(Operand Stack)(或表达式栈)

? 栈最典型的一个应用就是用来对表达式求值。在一个线程执行方法的过程中,实际上就是不断执行语句的过程,而归根到底就是进行计算的过程。因此可以这么说,程序中的所有计算过程都是在借助于操作数栈来完成的。

动态链接(Dynamic Linking) (或指向运行时常量池的方法引用)

? 因为在方法执行的过程中有可能需要用到类中的常量,所以必须要有一个引用指向运行时常量。

方法返回地址(Retuen Address)(或方法正常退出或者异常退出的定义)

? 当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保 存一个方法返回地址。

Java程序员必备的JVM学习
  • 一个 JVM 实例只存在一个堆内存,堆也是 Java 内存管理的核心区域

  • Java 堆区在 JVM 启动时的时候即被创建,其空间大小也就确定了,是 JVM 管理的最大一块内存空间.

  • 堆内存的大小是可以调节.

    ? 例如: -Xms:10m(堆起始大小) -Xmx:30m(堆最大内存大小)

    ? 一般情况可以将起始值和最大值设置为一致,这样会减少垃圾回收之后堆内存重新分配大小的次数,提高效率.

  • 《Java 虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但逻辑上它应该被视为连续的.

  • 所有的线程共享 Java 堆,在这里还可以划分线程私有的缓冲区.

  • 《Java 虚拟机规范》中对 Java 堆的描述是:所有的对象实例都应当在运行时分配在堆上.

  • 在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除.

  • 堆是 GC(Garbage Collection,垃圾收集器)执行垃圾回收的重点区域.

堆内存区域划分

Java8 及之后堆内存分为:新生区(新生代)+老年区(老年代)

新生区分为 Eden(伊甸园)区和 Survivor(幸存者)区

Java程序员必备的JVM学习

为什么分区(代)

将对象根据存活概率进行分类,对存活时间长的对象,放到固定区,从而减少扫 描垃圾时间及 GC 频率。针对分类进行不同的垃圾回收算法,对算法扬长避短。

对象创建内存分配过程

为新对象分配内存是一件非常严谨和复杂的任务,JVM 的设计者们不仅需要考虑内存如何分配,在哪分配等问题,并且由于内存分配算法与内存回收算法密切相关,所以还需要考虑 GC 执行完内存回收后是否会在内存空间中产生内存碎片

  1. new 的新对象先放到伊甸园区,此区大小有限制.

  2. 当伊甸园的空间填满时,程序又需要创建对象时,JVM 的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),将伊甸园区中的不再被引用的对象进行销毁.再加载新的对象放到伊甸园区.

  3. 然后将伊甸园区中的剩余对象移动到幸存者 0 区.

  4. 如果再次出发垃圾回收,此时上次幸存下来存放到幸存者 0 区的对象,如果没有回收, 就会被放到幸存者 1 区,每次

    来源:金清泽

    声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年1月9日
下一篇 2022年1月9日

相关推荐