JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点

JVM虚拟机

  • 一、JVM的概述
    • 1.为什么要学习JVM
    • 2.虚拟机
    • 3.JVM的作用
      • 作用
      • 特点
    • 4.JVM的位置
    • 5.JVM的分类
    • 6.各个组成部分的用途
    • 7.Java 代码的执行流程
    • 8.JVM 架构模型
  • 二、JVM 结构-类加载
    • 1. 类加载子系统
    • 2.类加载的角色
    • 3.类加载过程
      • 加载
      • 连接
      • 初始化
    • 4.类加载器
    • 5.双亲委派机制
    • 6.类的主动使用/被动使用
  • 三、JVM 运行时数据区
    • 运行时数据区的概念和组成
      • 1.程序计数器
      • 2.java虚拟机栈
      • 3.本地方法栈
      • 4.java堆内存 *
      • 5.方法区
  • 四、本地方法接口
    • 1.什么是本地方法
    • 2.为什么要使用 Native Method
  • 五、执行引擎
    • 1.概述
    • 2.什么是解释器,什么是JIT编译器
    • 3.为什么Java是半编译半解释型语言
  • 六、垃圾回收
    • 1.垃圾回收的概述
      • 什么是垃圾/li>
      • 为什么需要 GC/li>
      • 早期垃圾回收
      • 垃圾回收机制
    • 2.垃圾回收的相关算法
      • 3.垃圾回收中的相关概念

一、JVM的概述

1.为什么要学习JVM

    JVM是java底层代码的运行机制,虽然我们不学习jvm,也可以写出漂亮的代码,但是在一些面试过程中,不懂JVM会被面试官虐的体无完肤。一切知识点都是为了面试而准备,当然,这也是作为一个中高级程序员的必修之课。学会了JVM,才能清楚项目管理和性能调优的基本原理。

2.虚拟机

    1.所谓虚拟机(Virtual Machine),就是一台虚拟的计算机。它是一款软件,用来执 行一系列虚拟计算机指令。大体上,虚拟机可以分为系统虚拟机和程序虚拟机。
    2.大名鼎鼎的 VMware 就属于系统虚拟机,它是完全对物理计算机的仿真,提供了一 个可运行完整操作系统的软件平台。程序虚拟机典型的代表就是 java 虚拟机了,它专门为 执行某个单个计算机程序而设计。在 java 虚拟机中执行的指令我们称为 java 字节码指令。
    3.Java 虚拟机是一种执行 java 字节码文件的虚拟计算机,它拥有独立的运行机制。
    4.Java 技术的核心就是 java 虚拟机,因为所有的 java 程序都运行在 java 虚拟机内部。

3.JVM的作用

作用

    我们都知道JVM是java代码的运行环境,他是JDK不可或缺的一部分,而java虚拟机就是二进制字节码的运行环境,负责装在字节码到内部。,解释/编译为对应平台上的机器码指令执行。对每一条 java 指令,java 虚拟机中都有详细定义。如怎么取操作数,怎么处理操作数,处理结果放在哪儿

4.JVM的位置

5.JVM的分类

  1. 类加载器(ClassLoader)
  2. 运行时数据区(Runtime Data Area)
  3. 执行引擎(Execution Engine)
  4. 本地库接口(Native Interface)

简图:

6.各个组成部分的用途

程序在执行之前先要把 java 代码转换成字节码(class 文件),jvm 首先需要把字节码通过一定的方式 类加载器(ClassLoader) 把文件加载到内存中 的运行时数据区(Runtime Data Area),而字节码文件是 jvm 的一套指 令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器将字节码翻译成底层系统指令再交由 CPU 去执行,而这个过程中需要调用其他语言的接口来实现整个程序的功能,这就是这 4 个主要组成部分的职责与功能
而我们通常所说的 JVM 组成指的是运行时数据区(Runtime Data Area),因为通常需要程序员调试分析的区域就是“运行时数据区”,或者 更具体的来说就是“运行时数据区”里面的 Heap(堆)模块。

7.Java 代码的执行流程

8.JVM 架构模型

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

    这两种架构之间的区别:
基于栈式架构的特点

  1. 设计和实现更简单,适用于资源受限的系统.
  2. 使用零地址指令方式分配,其执行过程依赖于操作栈,指令集更小,编译器容易实现
  3. 不需要硬件支持,可移植性好,更好实现跨平台.

基于寄存器式架构特点:

  1. 指令完全依赖于硬件,可移植性差.
  2. 性能优秀,执行更高效.
  3. 完成一项操作使用的指令更少.

2.类加载的角色

加载

  1. 根据类的地址,从硬盘上读取类的信息,
  2. 将信息读入到方法区,生成Class类的对象

连接

    验证: 验证字节码文件格式是否是当前虚拟机所支持的文件格式,语法格式
    准备: 为静态成员分配默认值(int 默认值0) 注意static final在编译期间赋值
    解析: 将字节码中符 引用 替换成 直接引用

初始化

  类在什么时候开始初始化/strong>

  1. )创建类的实例,也就是 new 一个对象
  2. 访问某个类或接口的静态变量,或者对该静态变量赋值
  3. 调用类的静态方法
  4. 反射(Class.forName(“”))
  5. 初始化一个类的子类(会首先初始化子类的父类)

  类初始化的数据

  1. 先初始化静态的,多个静态的按照从上向下的顺序执行,
  2. 如果类有父类,则先初始化父类的静态,然后是子类.
  3. 如果是创建对象,先调用父类的构造方法,然后是子类自己的构造方法

4.类加载器

从JVM来说,类加载器可以分为两种

  1. 启动类加载器(不是java语言写的)
    1. 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请 求委托给父类的加载器去执行.
    2. 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器.
    3. 如果父类加载器可以完成类的加载任务,就成功返回,倘若父类加载器无法完 成加载任务,子加载器才会尝试自己去加载,这就是双亲委派机制.
    4. 如果均加载失败,就会抛出 ClassNotFoundException 异常。

    目的: 为了安全考虑 避免了用户自己写的类覆盖了系统中的类.
    为什么要用到双亲委派机制如我们建一个java.lang包,里面class一个String类,我们加载类时,加载的是直接的类还是JDK的类呢br>

    1. 安全,可避免用户自己编写的类动态替换 Java 的核心类,如 java.lang.String
    2. 避免全限定命名的类重复加载(使用了 findLoadClass()判断当前类是否已加 载)

    6.类的主动使用/被动使用

    JVM 规定,每个类或者接口被首次主动使用时才对其进行初始化,有主动使用,自然就有被动使用.
        主动使用:

    1. 通过new关键字被导致类的初始化
    2. 访问类的静态变量、静态方法
    3. 对某个类进行反射操作
    4. 初始化子类,父类也会被初始化
    5. 执行main方法

        被动使用:

    1. 仅仅使用类的静态常量 而且是直接赋字面量的那种,比如
      public final static int NUMBER = 5 ; //不会导致类初始化,被动使用
      public final static int RANDOM = new Random().nextInt() ; //会导致类的初 始化,主动使用
    2. 构造某个类的数组时不会导致该类的初始化
      比如:Student[] students = new Student[10] ;

    主动使用和被动使用的区别在于类是否会被初始化.

    三、JVM 运行时数据区

    运行时数据区的概念和组成

    JVM 的运行时数据区,不同虚拟机实现可能略微有所不同,但都会遵从Java 虚拟机规范,Java 8 虚拟机规范规定,Java 虚拟机所管理的内存将会包括以下几 个运行时数据区域: 程序计数器、java虚拟机栈、本地方法栈、java堆内存和方法区

    1. Java 和 C++语言的区别,就在于垃圾收集技术和内存动态分配上,C++语 言没有垃圾收集技术,需要程序员手动的收集。
    2. .垃圾收集,不是 Java 语言的伴生产物。早在 1960 年,第一门开始使用内存 动态分配和垃圾收集技术的 Lisp 语言诞生。
    3. 关于垃圾收集有三个经典问题:哪些内存需要回收什么时候回收如何回收/li>
    4. 垃圾收集机制是 Java 的招牌能力,极大地提高了开发效率。

    总结: 垃圾收集机制并不是java语言首创的,但是又是java的招牌,java可以自动垃圾回收。

    什么是垃圾/h3>

    垃圾是指在运行程序中没有任何引用指向的对象,这个对象就是需要被回收的垃圾。

    为什么需要 GC/h3>

    垃圾如果不及时清理,越积越多,可能会导致内存溢出。
    垃圾多了,内存碎片较多 例如数组 需要连续空间

    早期垃圾回收

         在早期的 C/C++时代,垃圾回收基本上是手工进行的。开发人员可以使用 new 关键字进行内存申请,并使用 delete 关键字进行内存释放。比如以下代码:

         这种方式可以灵活控制内存释放的时间,但是会给开发人员带来频繁申请和释放 内存的管理负担。倘若有一处内存区间由于程序员编码的问题忘记被回收,那么 就会产生内存泄漏,垃圾对象永远无法被清除,随着系统运行时间的不断增长, 垃圾对象所耗内存可能持续上升,直到出现内存溢出并造成应用程序崩溃。

        有了垃圾回收机制后,上述代码极有可能变成这样

        java语言是自动垃圾收集的

    垃圾回收机制

        自动内存管理: 无需开发人员手动参与内存的分配与回收,这样降低内存泄漏和内存溢出的风险。以更专心地专注于业务开发。

        自动收集的担忧: 自动回收方便了程序员的开发,但是降低处理内存问题的能力。自动虽好,但是还是应该了解并掌握一些相关内存管理的知识.

    Java 堆是垃圾收集器的工作重点

    1. 频繁收集 Young 区

    2. 较少收集 Old 区

    3. 基本不收集元空间(方法区)

    2.垃圾回收的相关算法

        内存溢出与内存泄漏
        溢出:内存不够用了
        泄露:有些对象已经在程序不被使用了,但是垃圾回收机制并不能判定其为垃圾对象,不能将其回收掉。 这样的对象越积越多, 长久也会导致内存不够用.
        例如: 与数据库连接完之后,需要关闭连接通道,但是没有关闭。
                 io 读写完成后没有关闭

    垃圾收集算法分为两大类:
    1.垃圾标记阶段算法
        主要是来判定哪些对象已经不再被使用,标记为垃圾对象。判定对象为垃圾的标准: 不被任何引用所指向的对象. Object obj = new Object();

        垃圾回收阶段的算法:
    (1)引用计数算法(在jvm中不被使用)。如果有一个引用指向此对象,那么计数器加1. 如果没有引用指向,计数器为0, 此时就判定为垃圾.
        优点: 方便使用,设计简洁。
        缺点: 增加了计数器的存储空间,计数需要消耗时间。导致一个循环引用问题. 好几个对象之间相互引用,但是没有其他引用指向他们,此时垃圾回收不能回收他们,但是也没有引用指向. 这就造成了内存泄漏。

    (2)可达性分析算法 / 根搜素算法(这是java目前所使用的垃圾标记算法):从一些活跃引用(GCRoots 根)开始, 如果对象被根直接或间接引用,那么此对象不是垃圾, 否则标记为垃圾对象。解决 了循环引用问题,设计简单 ,运行高效,防止内存泄漏。那么又一个问题,哪些引用被用来当做根呢br>

    JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点

        可以本当做跟的引用:

    1. 虚拟机栈中引用的对象 (方法中引用的对象)
    2. 本地方法栈中引用的对象
    3. 静态变量所引用的对象
    4. 常量引用指向的对象
    5. 被synchronized当做锁的对象
    6. Java 虚拟机内部的引用

    总结: 栈中引用的(正在使用的) 方法区,常量池中(生命周期较长的),被synchronized当做锁的对象

    finalize() 方法机制
        java允许对象在销毁前去调用finalize(),去处理一些逻辑. 一般不用(不建议用)。不要自己显示的去调用finalize()方法,在里面写代码一定要慎重。 在 finalize()时可能会导致对象复活。 finalize()由垃圾回收器调用,没有固定的时间。一个糟糕的 finalize()会严重影响 GC 的性能。比如 finalize 是个死循环。
        对象状态:

    1. 可触及的:从根节点开始,可以到达这个对象 。 (没有被标记为垃圾)
    2. 可复活的:对象的所有引用都被释放,但是对象有可能在 finalize()中复活。 确定为垃圾了,但没有调用finalize()方法.
    3. 不可触及的:对象的 finalize()被调用,并且没有复活,那么就会进入不可触及 状态。不可触及的对象不可能被复活,因为 finalize()只会被调用一次.

    2.垃圾回收阶段算法

    (1)标记-清除算法
    分为两个阶段:

    1. 标记: 标记出从根可达的对象,标记的是被引用的对象.
    2. 清除: 此清除并非直接将垃圾对象清除掉, 而是将垃圾对象的地址维护到一个空闲列表中。之后如果有新的对象产生,判断空闲列表中的对象空间能否存放得下新的对象,如果能放得下,那么就覆盖垃 圾对象.

    优点: 简单,容易理解
    缺点: 效率低, 会产生STW(在回收时,停止整个应用程序), 会产生内存碎片.
    (2)复制算法
    将内存分为大小相等的两块,每次只使用其中的一块儿区域即可。 当回收时,将不是垃圾的对象,复制到另一块内存中,排放整齐。 然后将原来的内存块清空,减少内存碎片。

    优点:运行高效,减少内存碎片
    缺点:用到两倍的内存空间 ,对于G1垃圾回收器,将每个区域又可以拆分成更多的小区域,需要维护各区之间的关系.在新生代中的幸存者0和幸存者1这两个区域使用复制算法.
    (3)标记压缩算法
    背景: 复制算法需要移动对象位置,移动的数量如果多的情况下,效率低. 对于新生代来讲还是不错的. 对于老年代,大量的对象是存活的. 如果需要移动就比较麻烦效率低.
    实现: 将存活对象标记出来,重新在本内存空间中排放位置,清除其他空间的垃圾对象.

    标记-清除 和 标记-压缩对比 标记清除是不移动对象, 不会把垃圾对象清除掉(维护在一个空闲列表中); 标记-压缩是要移动对象的. 要清除掉垃圾对象.

    优点: 不会像标记-清除算法那样会产生内存碎片
    缺点: 效率相对低, 对象位置移动后需要重新设置对象地址, 也会有STW

    3.垃圾回收中的相关概念

    System.gc() 的理解 调用System.gc()方法,会触发FULL GC(整堆收集), 但是不一定调用后会立刻生效。因为垃圾回收是自动的。一般情况下,不要在项目中显示的去调用.

    Stop the World Stop the World –>STW 在垃圾回收时,会导致整个应用程序停止。 在标记垃圾对象时,需要以某个时间节点上内存中的情况进行分析(拍照 快照) 因为不进行停顿的话,内存中的对象不停的变化,导致分析结果不准确。 停顿是不可避免的,优秀的垃圾回收器尽可能减少停顿的时间.

    对象引用 Object obj = new Object(); 就是将对象分等级: 强引用(有引用指向的对象) 软引用 弱引用 虚引用(都是垃圾了)

    1. 强引用:Object obj = new Object(); obj引用创建的对象 那么此对象就是被强引用的。 这种情况下,即使内存不够用了, 内存溢出,也不会回收。
    2. 软引用:当内存足够使用时,先不回收这类对象,当虚拟机内存不够用时,要回收此类对象.
    3. 弱引用: 此类对象只能生存到下次垃圾回收时, 只要发生垃圾回收,就会回收此类对象.
    4. 虚引用:发现即回收.

    声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年3月11日
下一篇 2022年3月11日

相关推荐