JVM类加载和垃圾回收算法详解

文章目录

  • JVM
    • 一、JVM运行流程
      • 1. JVM执行流程
    • 二、JVM运行时数据区
      • 1. 程序计数器(线程私有)
      • 2. 虚拟机栈 (线程私有)
      • 3. 本地方法栈(线程私有)
      • 4. 堆(线程共享)
      • 5. 元空间(线程共享)
        • 运行时常量池
    • 三、JVM类加载
      • 1. 类加载过程
        • 1)加载
        • 2)验证
        • 3)准备
        • 4)解析
        • 5)初始化
      • 双亲委派模型
      • 双亲委派模型优点
    • 四、垃圾回收
        • 1. 死亡对象的判断算法
          • 1)引用计数算法
          • 2)可达性分析
        • 2. 垃圾回收算法
          • 1)标记清除法
          • 2)复制算法
          • 3)标记整理算法
          • 4)分带算法

JVM

一、JVM运行流程

JVM是 Java 运行的基础,也是实现一次编译到处执行的关键,那么JVM是如何执行的呢?

1. JVM执行流程

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

在这里插入图片描述

小结,JVM 主要通过以下四个部分来执行 Java 程序的:

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

二、JVM运行时数据区

JVM运行时数据区也叫做内存布局,他由以下五个部分组成:

1. 程序计数器(线程私有)

程序计数器保存了下一条要执行的指令的地址。(不是 CPU 的寄存器,而是内存空间)

“下一条指令”指的是 Java 的字节码。(不是 CPU 的二进制机器语言)

注记: 什么是线程私有?

由于 JVM 的多线程是线程轮流切换并分配处理器执行时间的方式来实现,因此在任何一个确定的时刻,一个处理器都只会执行一条线程中的指令。因此为了切换线程后能够恢复到正确的执行位置,每条线程都需要独立的程序计数器,各线程之间计数互不影响,独立存储。我们就把这类区域成为 ”线程私有“ 的内存。

2. 虚拟机栈 (线程私有)

Java 虚拟机栈的生命周期和线程相同,Java 虚拟机栈描述的是 Java 方法执行的内存模型:每个方法执行时的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

JVM 虚拟机栈中包含了以下四部分:

  • 局部变量表:存放了编译器可知的各种基本数据类型、对象引用。(存放方法参数和局部变量)
  • 操作栈:每个方法会生成一个先进后出的操作栈。
  • 动态链接:指向运行时常量池的方法引用。
  • 放法返回地址:PC 寄存器的地址。

3. 本地方法栈(线程私有)

本地方法栈和虚拟机栈类似,只不过 Java 虚拟机栈是给 JVM 使用的,而本地方法栈是给本地方法使用的 。

4. 堆(线程共享)

作用:程序中所有创建的对象都保存在堆中。

我们常见的 JVM 参数设置 -Xms 10m最小启动内存是针对堆的,-Xmx 10m最大运行内存也是针对堆的。

ms 是 memory start 的简称,mx 是 memory max 的简称。

堆里面分为两个区域,新生代和老生代。新生代存放新建的对象,当经过一定 GC 次数之后还存活的对象会放入老生代。

新生代又细分为三个区域:一个Eden + 两个Survivor(S0/S1)。

在这里插入图片描述

垃圾回收时会将 Eden 中存活的对象放到没有使用的 Survivor 中,同时把当前的 Eden 和正在使用中的 Servivor 中的对象清除掉。

垃圾回收的细节部分会在下文着重讲述。

5. 元空间(线程共享)

作用:用来存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

运行时常量池

是方法区的一部分,存放字面量与符号引用。

字面量:字符串(Java8之后 移动到堆中)、final常量、基本数据类型的值。

符号引用:类和结构的完全限定名、字段的名称和描述符、方法的名称和描述符。

注记:

因为在7之前的版本,方法区又被称为“永久代”,他有固定的大小,容易发生内存溢出,8之后元空间取代方法区,元空间使用的是本地内存,而不是JVM堆内存,这样就可以避免永久代相关的内存溢出的问题。

小结:

在这里插入图片描述

三、JVM类加载

1. 类加载过程

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

我们分别来看每个步骤的具体内容:

1)加载

“加载” (Loading)阶段是整个“类加载”(Class Loading)过程中的第一个阶段,他和类加载 Class Loading是不同的,一个是加载 Loading 一个是类加载 Class Loading。

在加载 Loading 阶段,Java虚拟机需要完成以下三件事情:

  1. 通过一个类的全限定名来获取此类的二进制字节流。
  2. 将这个字节流的静态存储结构转化为元空间的运行时数据结构。
  3. 在内存中生成一个代表这个类的 java.lang.Class 对象,作为元空间这个类的各种区域的访问入口。

加载简单来说就是,JVM要读取.class中的内容,并执行里面的命令。

2)验证

验证是连接阶段的第一步,这一阶段的目的是确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机自身的安全。

3)准备

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

例如:

public static int value = 123;

他初始化 value 的 int 值为0,而非 123。

4)解析

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

5)初始化

初始化阶段,Java虚拟机真正开始执行类中编写的 Java 程序代码,将主导权交给应用程序。初始化阶段就是执行类构造器方法的过程。

双亲委派模型

提到类加载机制就不得不提的一个概念就是“双亲委派模型”。

什么是双亲委派模型?

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

JVM默认有三个类加载器:

  • BootstrapClassLoader (引用类加载器) 负责加载标准库的类
  • ExtensionClassLoader (扩展类加载器) 负责加载扩展类
  • ApplicationClassLoader (应用程序类加载器) 负责加载第三方库的类/自己写的代码中的类

双亲委派模型优点

  • 避免重复加载类:比如A类和B类都有一个父类C类,那么当A类启动时就会将C类加载起来,那么B类在加载时就不需要在重复加载C类了。
  • 安全性:可以保证 Java 的核心 API 不被篡改,如果没有使用双亲委派模型,而是每个类加载器自己加载的话就会出现一些问题,例如我们编写一个称为 java.lang.Object 类的话,那么程序运行的时候,系统就会出现多个不同的 Object 类,而有些 Object 类又是用户自己提供的,因此安全性就不能得到保证了。

四、垃圾回收

1. 死亡对象的判断算法
1)引用计数算法

定义:给对象增加一个引用计数器,每当有地方引用他时,计数器就+1;当引用失效时,计数器就-1;任何时刻引用计数器为0的对象都是不能再被使用的了,就是对象已“死”。

主流的 JVM 中没有选用引用计数算法来管理内存,最主要的原因是无法解决对象的循环引用问题。

假设下面是我们写的代码:

Test a = new Test();
Test b = new Test();

a.t = b;
b.t = a;

a = null;
b = null;

最后造成了两个new出来的对象互相计数,而真正的引用已经置空,引用不到这两个对象了,但是无法把他们标记称“垃圾”,因为他们的引用计数器都还不为0。

(和死锁有点类似)

2)可达性分析

Java采用此方法来判断对象是否存活。

该算法的核心思想为:通过一系列称为 “GC Roots“ 的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为”引用链“,当一个对象到 GC Root 没有任何任何的引用链相连时(就是不可达),证明此对象是不可用的。

例如:

在这里插入图片描述

对象Object5-Object7之间虽然彼此还有关联,但是它们到GC Roots是不可达的,因此他们会被判定为可回收对象。

在 Java 中,可以作为 GC Root 的对象包含下面几种:

  • 虚拟机栈中引用的对象;
  • 元空间中类静态属性引用的对象;
  • 元空间中常量引用的对象;
  • 本地方法栈中 JNI(Native方法)引用的对象。
2. 垃圾回收算法
1)标记清除法

算法分为 “标记” 和 “清除” 两部分,先是找到所有需要回收的对象,在标记完成后统一回收所有被标记的对象。

问题有两个:

  • 效率问题:标记和清除两个过程的效率都不高
  • 空间问题:标记清除后会产生大量的内存碎片,空间碎片太多就会导致后续程序需要分配较大对象时,无法找到足够连续的内存来存储,不得已提前出发下一次的垃圾清除。

在这里插入图片描述

2)复制算法

复制算法是为了解决 “标记清理” 的效率问题。他将可用内存分为相等大小的两等块,每次只使用其中的一块,当这块内存需要垃圾回收时,就将此区域内还存活的对象复制到另一块区域上面,然后将使用过区域的内存一次清理掉。这样做的好处是对整个半区进行内存回收,内存分配时就不需要考虑内存碎片的情况,只需移动堆顶指针,按顺序分配即可。

3)标记整理算法

是对 “标记清除” 算法的升级,标记过程一致,但清理不是对对象直接回收,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。

4)分带算法

分带算法是通过区域划分,对不同的区域采用不同的垃圾回收策略,从而更好的实现垃圾回收。

当前的 JVM 垃圾回收 都是采用的“分代收集”算法。一般是把 Java 堆分为新生代和老生代,在新生代中,又分为一个Eden + 两个Survivor(S0/S1);新创建的对象会放到 Eden 区中,一轮 GC 过后会将 Eden 区和当前使用的 Survivor 区中还存活的对象放到未使用的 Survivor 区中,经过多轮 GC 后还存活的对象,会把他放到老生代,老生代的垃圾回收算法是上面讲到的 “标记整理算法”。在新生代中,每次垃圾回收都有大批对象死去,只有少量存活,因此我们采用复制算法;而老年代中对象存活率高、没有额外空间对它进行分配担保,就必须采用"标记整理"算法。

问题:

Minor GC 和 Full GC 这两种 GC 有什么不一样吗 ?

  1. Minor GC又称为新生代GC : 指的是发生在新生代的垃圾收集。因为Java对象大多都具备朝生夕灭的特性,因此Minor GC(采用复制算法)非常频繁,一般回收速度也比较快。

  2. Full GC 又称为 老年代GC或者Major GC : 指发生在老年代的垃圾收集。出现了Major GC,经常会伴随至少一次的Minor GC(并非绝对,在Parallel Scavenge收集器中就有直接进行Full GC的策略选择过程)。Major GC的速度一般会比Minor GC慢10倍以上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/923711.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1、正则表达式

grep匹配 grep用来过滤文本内容,以匹配要查询的结果。 grep root /etc/passwd:匹配包含root的行 -m 数字:匹配几次后停止 -v:取反-i:忽略字符的大小写,默认的,可以不加-n&#xff1a…

Java学习笔记--继承的介绍,基本使用,成员变量和成员方法访问特点

目录 一,继承 1.什么是继承 2.怎么去继承: 3.注意: 4.继承怎么学 二,继承基本使用 三,成员变量和成员方法访问特点 1.成员变量访问特点 1,子类和父类中的成员变量不重名: 总结: 2,子类和父类中的成员变量重…

初级数据结构——二叉树题库(c++)

这里写目录标题 前言[1.——965. 单值二叉树](https://leetcode.cn/problems/univalued-binary-tree/)[2.——222. 完全二叉树的节点个数](https://leetcode.cn/problems/count-complete-tree-nodes/)[3.——144. 二叉树的前序遍历](https://leetcode.cn/problems/binary-tree-…

redmi 12c 刷机

刷机历程 一个多月前网购了redmi 12c这款手机, 价格只有550,用来搞机再适合不过了, 拆快递后就开始倒腾,网上有人说需要等7天才能解锁,我绑定了账号过了几天又忍不住倒腾,最后发现这块手机不用等7天解锁成功了,开始我为了获取root权限, 刷入了很火的magisk,但是某一天仍然发现/…

Python 爬虫入门教程:从零构建你的第一个网络爬虫

网络爬虫是一种自动化程序,用于从网站抓取数据。Python 凭借其丰富的库和简单的语法,是构建网络爬虫的理想语言。本文将带你从零开始学习 Python 爬虫的基本知识,并实现一个简单的爬虫项目。 1. 什么是网络爬虫? 网络爬虫&#x…

计算机毕业设计Hadoop+Spark音乐推荐系统 音乐预测系统 音乐可视化大屏 音乐爬虫 HDFS hive数据仓库 机器学习 深度学习 大数据毕业设计

温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…

JAVA题目笔记(二十)Stream流综合练习+方法引用

一、数据过滤 import java.util.*; import java.util.stream.Collectors;public class Co {public static void main(String[] args) {List<Integer> listnew ArrayList<>();Collections.addAll(list,1,2,3,4,5,6,7,8,9,10);List<Integer> newlist list.str…

Python学习34天

import random class Game: peo0 rob0 # # def __init__(self,peo,rob): # self.peopeo # self.robrob def Play(self): """ 石头剪刀布游戏&#xff0c;0代表石头&#xff0c;1代见到&#xff0c;2代表石头 …

MATLAB支持的距离度量

距离度量是用于量化两个点或样本之间差异的一种方法。在不同的领域和应用场景中&#xff0c;距离度量的选择可能会有所不同。 欧几里得距离&#xff08;Euclidean Distance&#xff09;&#xff1a;这是最直观的距离定义&#xff0c;适用于n维空间中的两点。对于二维空间中的点…

Jmeter中的测试片段和非测试原件

1&#xff09;测试片段 1--测试片段 功能特点 重用性&#xff1a;将常用的测试元素组合成一个测试片段&#xff0c;便于在多个线程组中重用。模块化&#xff1a;提高测试计划的模块化程度&#xff0c;使测试计划更易于管理和维护。灵活性&#xff1a;可以通过模块控制器灵活地…

【1.2 Getting Started--->Installation Guide】

NVIDIA TensorRT DOCS 此 NVIDIA TensorRT 10.6.0 安装指南提供安装要求、TensorRT 包中包含的内容列表以及安装 TensorRT 的分步说明。 安装指南 摘要&#xff1a; 本 NVIDIA TensorRT 10.3.0 安装指南提供了安装要求、TensorRT 软件包中包含的内容列表以及安装 TensorRT 的…

ubuntu设置程序开机自启动

文章目录 1、概述2、图形界面设置3、设置为Systemd服务 1、概述 测试环境&#xff1a;ubuntu22.04 带图形界面 实现方式1&#xff1a;通过图形界面的【启动应用程序】设置开机自启动&#xff1b; 实现方式2&#xff1a;通过配置为服务实现开机自启动。 2、图形界面设置 优点&am…

IDEA2024创建一个spingboot项目

以下是创建一个基本的 Spring Boot 项目的步骤和示例&#xff1a; 初始化一个springboot工程其实有许多方法&#xff0c;笔者这里挑了一个最快捷的方式搭建一个项目。我们直接通过官方平台&#xff08;start.spring.io&#xff09;进行配置&#xff0c;然后下载压缩包就可以获取…

商用密码应用安全性评估,密评整体方案,密评管理测评要求和指南,运维文档,软件项目安全设计相关文档合集(Word原件)

一、 密码应用安全性评估方案 &#xff08;一&#xff09; 密码应用测评工作思路 1.1.1. 测评准备活动的主要任务 1.1.2. 测评准备活动的输出文档 1.2. 方案编制活动 1.2.1. 方案编制活动的主要任务 1.2.2. 方案编制活动的输出文档 1.3. 现场预评估活动 1.3.1. 现场测评…

音视频技术扫盲之预测编码的基本原理探究

预测编码是一种数据压缩技术&#xff0c;广泛应用于图像、视频和音频编码等领域。其基本原理是利用数据的相关性&#xff0c;通过对当前数据的预测和实际值与预测值之间的差值进行编码&#xff0c;从而实现数据压缩的目的。 一、预测编码的基本概念 预测编码主要包括预测器和…

标定系列——关于cv::calibrateHandEye的介绍

关于cv::calibrateHandEye的介绍 介绍函数原型所在头文件原理说明 介绍 函数原型 void cv::calibrateHandEye ( InputArrayOfArrays R_gripper2base, InputArrayOfArrays t_gripper2base, InputArrayOfArrays R_target2cam, InputArrayOfArrays t_target2cam, OutputArra…

uname -m(machine) 命令用于显示当前系统的机器硬件架构(Unix Name)

文章目录 关于 arm64 架构检查是否安装了 Rosetta 2其他相关信息解释&#xff1a;命令功能&#xff1a;示例&#xff1a; dgqdgqdeMac-mini / % uname -m arm64您运行的 uname -m 命令显示您的系统架构是 arm64。这意味着您的 Mac Mini 使用的是 Apple 的 M1 或更新的芯片&…

代码随想录算法训练营day46|动态规划09

买卖股票的最佳时机四 之前是最多只能完成两笔交易&#xff0c;现在是至多可以买卖k次&#xff0c;那么状态数需要定为2*k1种&#xff0c;此时&#xff0c;就要分析多种情况的递推式 找到奇偶数交替的规则即可 class Solution { public:int maxProfit(int k, vector<int&g…

qt QDateTime详解

1. 概述 QDateTime 是 Qt 框架中用于处理日期和时间的类。它将 QDate 和 QTime 组合在一起&#xff0c;提供了日期时间的统一处理方案。QDateTime 可以精确到毫秒&#xff0c;并支持时区处理。 2. 重要方法 构造函数: QDateTime() 构造无效的日期时间 QDateTime(const QDa…

[Docker-显示所有容器IP] 显示docker-compose.yml中所有容器IP的方法

本文由Markdown语法编辑器编辑完成。 1. 需求背景: 最近在启动一个服务时&#xff0c;突然发现它的一个接口&#xff0c;被另一个服务ip频繁的请求。 按理说&#xff0c;之前设置的是&#xff0c;每隔1分钟请求一次接口。但从日志来看&#xff0c;则是1秒钟请求一次&#xff…