JVM——类加载和垃圾回收

这篇具有很好参考价值的文章主要介绍了JVM——类加载和垃圾回收。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

前言

JVM简介

JVM内存区域划分

JVM的类加载机制

1.加载

双亲委派模型

2.验证

验证选项

3.准备

4.解析

5.初始化

触发类加载

JVM的垃圾回收策略 GC

一:找     谁是垃圾 

1.引用计数

2.可达性分析  (这个方案是Java采取的方案)。

二:释放垃圾对象

三种典型的策略

JVM实现思路


前言

我们在学习JVM的时候,其实里面的内容是非常之多的,但是里面的大部分内容都是属于八股,想要彻底搞明白,就需要看大量的关于JVM的源代码,JVM的源代码是C++写的。想要深入研究的可以去看看《深入理解Java虚拟机》这本书。

这篇文章主要针对JVM中的常见的面试题来展开。

JVM简介

JVM 是 Java Virtual Machine 的简称,意为 Java虚拟机。
虚拟机是指通过软件模拟的具有完整硬件功能的、运行在一个完全隔离的环境中的完整计算机系统。
常见的虚拟机:JVM、VMwave、Virtual Box。
JVM 和其他两个虚拟机的区别:

  1. VMwave与VirtualBox是通过软件模拟物理CPU的指令集,物理系统中会有很多的寄存器;
  2. JVM则是通过软件模拟Java字节码的指令集,JVM中只是主要保留了PC寄存器,其他的寄存器都进行了裁剪。

JVM 是一台被定制过的现实当中不存在的计算机。

JVM内存区域划分

JVM其实就是一个Java进程,Java进程也就是JVM会从操作系统这里申请一大块内存空间,给Java代码来使用。

JVM从操作系统申请的这块内存空间中,进行进一步的划分,给出了每块划分后的空间的不同用途。

JVM——类加载和垃圾回收,JVM,jvm,后端,java

其中,最核心的就是栈、堆、元数据区(方法区)。

  • 虚拟机栈是给Java代码来使用的,主要存放一些局部变量,还有维护方法之间的调用关系。
  • 本地方法栈则是给JVM内部的本地方法来使用的。
  • 堆上存放的就是new出来的对象、成员变量。
  • 程序计数器中存放的就是一个内存地址,这个内存地址就是下一个要执行字节码所在的地址,作用就是记录当前程序执行到那个指令了。

需要注意的是,堆和元数据区,在一个JVM 中只存在一份,也就是多个线程共享堆区和元数据区。

栈(本地方法栈和虚拟机栈)和程序计数器则是存在多份的,也就是每个线程都会有一份。

JVM的线程操作和操作系统的线程操作是一对一的关系。也就是说每次在Java代码中创建的线程都会在操作系统中有一个线程与之对应。

这里的面试题主要就是判断某个变量或者对象在JVM的那个区域?

例如下面代码:

void func() {
    Test t1 = new Test();
}

上述代码在一个方法里面我们实例化了一个Test对象。

JVM——类加载和垃圾回收,JVM,jvm,后端,java

 func方法是在元数据区以一些二进制的指令来存储的。

我们可以看到t1变量是一个在方法里面定义的,所以他是一个局部变量,局部变量就存储在栈上。

而new Test(); 这个对象的本体则是在堆上的。

其实像这里的关于JVM区域的面试题,我们只需要知道JVM的每个区域都是存储什么东西的就好了。

  • 虚拟机栈是给Java代码来使用的,主要存放一些局部变量,还有维护方法之间的调用关系。
  • 本地方法栈则是给JVM内部的本地方法来使用的。
  • 堆上存放的就是new出来的对象、成员变量。
  • 程序计数器中存放的就是一个内存地址,这个内存地址就是下一个要执行字节码所在的地址,作用就是记录当前程序执行到那个指令了。

JVM的类加载机制

对与一个类来说,他的生命周期是这样的:

JVM——类加载和垃圾回收,JVM,jvm,后端,java

 前面的5步也是类加载的过程和固定的顺序。我们主要研究前面的5步。

类加载具体就是把一个.class文件,也就是类编译后的文件,加载到内存中,得到了类对象这样的过程就称之为类加载。

一个程序想要运行,就需要把指令和数据加载到内存中。类加载就是做的这个事情。

下面是类加载的5个步骤:

1.加载

这里的加载过程其实简单,就是找到.class文件,然后读取文件的内容。

但是在找.class文件的这个过程中,会有一个非常重要的机制:双亲委派模型

双亲委派模型

在JVM中,加载类需要用到一组特殊的模块:类加载器。

在JVM中,内置了三个类加载器。

  • BootStrap ClassLoader    负责加载Java标准库中的类
  • Extension ClassLoader     负责加载一些非标准的但是是Sun/Oracle扩展库的类
  • Application ClassLoader    负责加载项目中自己写的类、以及第三方库中的类

当具体加载一个类的时候,他的过程是这样的:

需要先给定一个类的全限定类名,"java.lang.String"  这个类名是一个字符串的形式。

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层BootStrap ClassLoader类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。

具体可以参考下图:

JVM——类加载和垃圾回收,JVM,jvm,后端,java

2.验证

由于.class文件有着明确的数据格式(二进制的),这一阶段的主要目的就是确保Class文件中的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求。

验证选项

文件格式验证

字节码验证

符号引用验证……

3.准备

准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段。

比如下面这样的代码:

public static int value = 123;

此时在准备阶段value的值并不是123,而是0。
 

4.解析

解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程,也就是初始化常量的过程。

  • 符号引用:就是字符串常量在.class文件已经存在,但是他们只知道彼此之间的相对位置,并不知道自己在内存中的具体位置。
  • 直接引用:真正的加载到内存中,就会把字符串常量填充到内存中的特定地址上去。此时字符串引用的就是直接引用,(也就是Java中普通的引用)。

5.初始化

在初始化阶段,JVM才真正的执行类中编写的Java代码,将主导权交给应用程序,初始化阶段就是执行类的构造方法的过程。(类要是有父类,就需要先初始化父类,在初始化子类)。

触发类加载

注意:类加载这个动作不是说JVM一启动就会进行加载,因为JVM整体是一个懒加载的策略,也就是非必要,不加载。

以下三种请况就会加载:

  1. 创建了这个类的实例
  2. 使用了这个类的静态方法/静态属性
  3. 使用子类,会触发父类的加载

JVM的垃圾回收策略 GC

Java中的垃圾回收是为了帮助我们自动释放内存的一种机制。

面试题:为什么需要垃圾回收机制

因为在程序运行过程中,会向操作系统申请大量的内存空间,但是这些空间也有可能会消耗尽,因为不断地分配内存空间而不进行回收,就好像不停地生产生活垃圾而从来不打扫一样。

上面我们谈到了关于JVM的几个区域,那么垃圾回收释放的是那个区域的空间呢?

需要注意的是,栈和程序计数器是每个线程都会有一份的。他们会随着线程的销毁而一起销毁的。

而元数据区里面的存储的类对象,很少会进行销毁。

所以我们释放的就是堆中的空间。上面我们谈到堆中主要就是存放new 出来的对象的。

GC也就是以对象为单位进行释放的。(释放对象)

GC中主要分为两个阶段:

一:找     谁是垃圾 

Java通过引用来判断是否是垃圾对象,如果没有引用指向,就判定这个对象是垃圾。

1.引用计数

给对象安排一个额外的空间,保存了一个整数,表示该对象有几个引用指向它。Java实际上并没有采取这样的方案,(Python、PHP采用了这个方案)。

Test t1 = new Test();

JVM——类加载和垃圾回收,JVM,jvm,后端,java

 此时是有一个引用指向的,所以引用计数器为1。

如果代码变成这样:

Test t1 = new Test();
Test t2 = t1;

JVM——类加载和垃圾回收,JVM,jvm,后端,java

 也就是说随着引用的增加,计数器就会增加,引用的销毁,计数器就会减少。

当计数器为0时,就会认为该对象没有引用指向了,就是垃圾了。

但是缺点也是很明显:

  1. 浪费内存空间
  2. 存在循坏引用的情况

2.可达性分析  (这个方案是Java采取的方案)。

把对象之间的引用关系理解成为了一个树形结构,从一些特殊的起点出发,进行遍历,只要能访问到,是可达的,不是垃圾,再把不可达的当做垃圾即可。

JVM——类加载和垃圾回收,JVM,jvm,后端,java

 此时通过root这个引用是可以访问到整个树的任意节点的。

可达性分析的关键要点在于要进行上述的遍历,需要有起点的。

起点可以是:

  1. 栈上的局部变量(每个栈的每个局部变量都是起点)
  2. 常量池中引用的对象
  3. 方法区中静态成员引用的对象

可达性分析,总体就是从所有的起点出发,看看该对象里面又通过哪些引用能访问到那些对象,顺藤摸瓜的把所有可以访问的对象都访问一遍,遍历的同时把对象标记为“可达”。

可达性分析,克服了引用计数的两个缺点

但是也是有自己的问题:

  • 消耗更多的时间 因此即使某个对象成了垃圾,也不能第一时间发现,因为在扫描的过程中,也是需要时间的。
  • 在进行可达性分析的时候,要顺藤摸瓜,一旦这个过程中,当前代码中的对象的引用关系发生了变化,就可以出现bug。

因此为了更好的完成这个顺藤摸瓜的过程,就需要让其他的业务线程都暂停工作!!!(STW)

(STW)   stop the world !

但是Java毕竟发展了这么多年,拉进回收这里也是在不断的进行优化,STW这个问题也可以比较好的对付了。

二:释放垃圾对象

三种典型的策略

1:标记清除

 如果现在向内存申请了一块下面这样的空间,然后我标出来的就是垃圾对象,需要清除的。

JVM——类加载和垃圾回收,JVM,jvm,后端,java

 这种策略就是直接把垃圾对象的内存就释放了。

但是这种简单粗暴的方式会产生内存碎片。

内存碎片:申请空间都是连续的整块空间,现在上述图中的空闲空间都是散落在独立的空间里面的。现在空闲总空间可能超过1G,但是我想申请500M,却是申请不了。

2:复制算法

这种方法是把空间分为两部分。一次只使用一半。

复制算法就是把不是垃圾的对象拷贝到一边去,然后在统一释放整个区域。

JVM——类加载和垃圾回收,JVM,jvm,后端,java

 此时我要释放的是2和4,我就需要把剩下1和3复制到另一边去。然后再把这边全部释放。

JVM——类加载和垃圾回收,JVM,jvm,后端,java

 复制算法解决了内存碎片的问题,但是也有缺点:

  • 内存利用率比较低
  • 如果大部分对象都是保留的,垃圾很少,此时的复制成本就比较高

3:标记整理

类似于顺序表删除中间元素,有一个搬运的过程

JVM——类加载和垃圾回收,JVM,jvm,后端,java 解决了内存碎片问题但是搬运的整体开销也是比较大的。

JVM实现思路

实际上,JVM的实现方式是结合了上述几种思想之后的方法。

分代回收思想

具体细节:

  • 给对象设置年龄这样的概念,用来描述这个对象存在多久了。如果一个对象刚诞生,那么就是0岁。
  • 每次进过一次扫描(可达性分析)如果没有被标记为垃圾对象,这是对象年龄就增加一岁。
  • 通过年龄来区分这个对象的活动时间。

经验规律:年龄越大的对象,也将会持续存在更长的时间。

针对不同的年龄来采取不同的回收策略

JVM——类加载和垃圾回收,JVM,jvm,后端,java

JVM针对这几个区域来执行不同的策略。

1:新创建的对象,放在伊甸区

垃圾回收扫描到伊甸区之后,大多数的对象将会在第一轮扫描下被GC给淘汰掉。

2:如果伊甸区的对象,熬过第一轮GC,就会通过复制算法,拷贝到生存区。

生存区分为两半(大小相等),一次只使用其中的一半。

如果GC在扫描生存区的时候,发现垃圾对象也就淘汰,不是垃圾的,就通过复制算法拷贝到生存区的另一边。

3:当对象在生存区熬过了若干次GC的时候,年龄也变大了。此时就会通过复制算法拷贝到老年代。

4:进入老年代之后,由于年龄都比较大了,被标记为垃圾对象的概念也很小,所以针对老年代的GC扫描也会降低频率。

特殊情况:如果对象非常大,直接进入老年代(大对象进行复制算法,成本非常高,而且大对象也不会很多)。文章来源地址https://www.toymoban.com/news/detail-562850.html

到了这里,关于JVM——类加载和垃圾回收的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 了解 JVM - 认识垃圾回收机制与类加载过程

    本篇通过介绍JVM是什么,认识JVM的内存区域的划分,了解类加载过程,JVM中垃圾回收机制,从中了解到垃圾回收机制中如何找到存活对象的方式,引用计数与可达性分析的方式,再释放垃圾对象时使用的方式,标准清除,复制算法,标准整理,分代回收等等,如有错误,请在

    2024年02月16日
    浏览(40)
  • JVM解密: 解构类加载与GC垃圾回收机制

    JVM 其实是一个 Java 进程,该进程会从操作系统中申请一大块内存区域,提供给 Java 代码使用,申请的内存区域会进一步做出划分,给出不同的用途。 其中最核心的是栈,堆,方法区这几个区域: 堆,用来放置 new 出来的对象,类成员变量。 栈,维护方法之间的调用关系,放

    2024年02月10日
    浏览(45)
  • JVM基础知识(内存区域划分,类加载,GC垃圾回收)

    目录 内存区域划分 JVM中的栈 JVM中的堆 程序计数器 方法区(元数据区) 给一段代码,某个变量在哪个区域上? 类加载 类加载时机 双亲委派模型 GC 垃圾回收机制 GC 实际工作过程 1.找到垃圾/判定垃圾 1.可达性分析(Java中的做法) 2.引用计数 2.清理垃圾 1.标记清除 2.复制算法 3.标记整

    2024年02月07日
    浏览(65)
  • 【Java JVM】垃圾回收

    当前大部分的垃圾收集器都遵循着 “分代收集” (Generational Collection) 的理论进行设计的, 建立在 2 个分代假设之上 弱分代假说 (Weak Generational Hypothesis): 绝大多数对象都是朝生夕灭的 强分代假说 (Strong Generational Hypothesis): 熬过越多次垃圾收集过程的对象就越难以消亡 根据这

    2024年03月14日
    浏览(46)
  • 3.Java面试题—JVM基础、内存管理、垃圾回收、JVM 调优

    一篇文章掌握整个JVM,JVM超详细解析!!! JVM (Java虚拟机) 是运行 Java 字节码 的 虚拟机 。 JVM 针对 不同系统 有 特定实现 ( Windows 、 Linux 等),目的是 同样的代码 在 不同平台 能运行出 相同的结果 。 Java 语言 要经过 编译 和 解释 两个步骤: 编译 :通过 编译器 将 代码 一

    2024年02月15日
    浏览(50)
  • 【Java】图解 JVM 垃圾回收(一):GC 判断策略、引用类型、垃圾回收算法

    垃圾 是指运行程序中 没有任何引用指向的对象 ,需要被回收。 内存溢出 :经过垃圾回收之后,内存仍旧无法存储新创建的对象,内存不够溢出。 内存泄漏 :又叫 “ 存储泄漏 ”,对象不会再被程序使用了,但是 GC 又不能回收它们。例如:IO 流不适用了但是没有被 Close、

    2024年02月19日
    浏览(50)
  • Java虚拟机(JVM)、垃圾回收器

    JRE(Java Runtime Environment,运行环境) 所有的程序都要在JRE下才能够运行。包括JVM和Java核心类库和支持文件。 JDK(Java Development Kit,开发工具包) 用来编译、调试Java程序的开发工具包。包括Java工具(javac/java/jdb等)和Java基础的类库(java API )。 JVM(Java Virtual Machine,虚拟机) JRE的一部分,

    2024年02月12日
    浏览(49)
  • java八股文面试[JVM]——垃圾回收

    参考:JVM学习笔记(一)_卷心菜不卷Iris的博客-CSDN博客 GC垃圾回收 面试题: JVM内存模型 以及分区,需要详细到每个区放什么 堆里面的分区:Eden,survival from to,老年代,各自的特点。 GC的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方 针

    2024年02月11日
    浏览(48)
  • 【Java虚拟机】JVM垃圾回收机制和常见回收算法原理

    1.垃圾回收机制 (1)什么是垃圾回收机制(Garbage Collection, 简称GC) 指自动管理动态分配的内存空间的机制,自动回收不再使用的内存,以避免内存泄漏和内存溢出的问题 最早是在1960年代提出的,程序员需要手动管理内存的分配和释放 这往往会导致内存泄漏和内存溢出等问

    2024年02月02日
    浏览(54)
  • java八股文面试[JVM]——垃圾回收器

    jvm结构总结   常见的垃圾回收器有哪些?     CMS(Concurrent Mark Sweep) 整堆收集器 : G1 由于整个过程中 耗时最长 的 并发标记 和 并发清除 过程中,收集器线程都可以与用户线程一起工作,所以 总体上来说 ,CMS收集器的内存回收过程是与用户线程一起并发地执行。老年代收

    2024年02月11日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包