浅谈 JVM

JVM 内存划分

JVM 内存划分为 四个区域,分别为 程序计数器、元数据区、栈、堆

程序计数器是记录当前指令执行到哪个地址

元数据区存储存储的是当前类加载好的数据,包括常量池和类对象的信息,.java 编译之后产生 .class 文件,运行代码的时候会把了类对象的信息保存在内存里,其中类元信息包括:类名、类的父类,实现哪些接口,类的权限public/private…
方法元信息:方法名,参数类型,返回值…
类元信息和方法元信息都是类对象里面包含的。

栈:分为本地方法栈和虚拟机栈,本质上都是存储方法的栈帧,每次调用方法的时候,会进行压栈,方法调用完会执行出栈操作,本地方法栈执行的是C++方法,虚拟机栈执行的是 Java方法。

堆:存放 new 出来的对象的实例。
Test t = new Test();
new Test() 这个对象的实例一定是存储在堆上的

t 需要分类讨论,如果 t 是一个局部变量,t 就是在栈上的;如果 t 是 成员变量,t 就是在堆上的;如果 t 是 静态成员变量,t 就是在元数据区里的。

在这里插入图片描述

元数据区 和 堆,整个Java进程共用一份
程序计数器和栈,每个线程有一份

在面试时,如果遇到谈一下栈和堆的话, 要注意是数据结构里的栈和堆, 还是JVM的栈和堆

类加载

1)加载:根据类的全限定名(包名+类名)找到对应的 .class 文件,然后打开文件,将文件的内容读取到内存中里。
2)验证:解析和校验 .class 文件的内容是否合法,如果合法就把这里的内容转化成结构化的数据
3)准备:给类对象申请 全0 的内存空间
4)解析:针对字符串常量进行初始化,将 .class 文件中的字符串常量放入元数据区的常量池中
5)初始化:将类对象进行最终的初始化,包括对类对象各种属性的填充,类中的静态成员,父类,如果父类没有被加载过,会触发父类的类加载。

类加载触发的时机为懒加载,当Java代码需要用到哪个类才会触发哪个类的加载

双亲委派模型

双亲委派模型的根据JVM实现类加载的源码提出的,也就是说是先有JVM的类加载源码,才有后面的双亲委派模型。

JVM 默认提供了三种类加载器,分别为 BootstrapClassLoader、ExtensionClassLoader、ApplicationClassLoader

你可以认为这三个类加载器的关系如下:
在这里插入图片描述
它们之间的联系,你可以认为是这三个类加载器都有一个引用指向上一个类加载器,ApplationClassLoader 的父亲的 ExtensionClassLoader,ExtensionClassLoader 的父亲是 BootstrapClassLoader,BootstrapClassLoader 的父亲为 null。

BootstrapClassLoader 负责加载Java 标准库的 .class 文件
ExtensionClassLoader 负责加载 Java 扩展库的 .class 文件
ApplicationClassLoader 负责加载 Java第三方库的.class 文件

随着时代的发展,Java的扩展库我们很少使用,取而代之的是Java第三方库,例如使用maven 加载的库就是第三方库

双亲委派模型的执行流程
进行类加载,通过全限定类名寻找 .class 文件,先从 ApplicationClassLoader 最为入口开始,然后将类加载任务委托上一级 ExtensionClassLoader ,再将类加载任务委托给上一级 BootstrapClassLoader,由于 BootstrapClsssLoader 没有上一级,开始执行类加载任务,将需要的Java标准库里的 .class 文件加载到内存里,然后将任务下放给下一级 ExtensionClassLoader ,ExtensionClassLoader 将所需要的Java扩展库的 .class 文件加载到内存里,最后将任务交给下一级 ApplicationClassLoader,ApplicationClassLoader 负责加载Java第三方库的 .class 文件。

如果没有找到对应的 .class 文件,就会抛出异常 ClassNotFoundException

在这里插入图片描述

垃圾回收 GC

在Java中你不需要手动释放内存,这是因为JVM 内部实现了 GC,也就是垃圾回收机制,这个机制可以自动回收内存空间,这使得Java程序员不需要考虑内存泄漏的问题。

垃圾回收有两个步骤:首先就是找到垃圾,然后再回收垃圾

如何找到垃圾???

找到垃圾这里介绍两个算法,一个是引用计数,另一个是可达性分析

引用计数

在创建的对象旁边开辟一个空间,用来存放该对象被多少个引用引用着,当被引用的数量为 0 的时候就会进行垃圾回收:

在这里插入图片描述

但这也有缺点:
1)内存消耗多,尤其是对象本身比较小的时候,引用计数占据的空间的比例就会很大,例如:假设对象自身占8个字节,引用计数可能占 4 个字节

2)可能出现 “ 循环引用” 的问题
假设一个类里面还定义了一个引用,用这个类创建的两个对象的引用相互指向对方,那这两个对象是不可能被垃圾回收掉的:
在这里插入图片描述
当我们将 a 和 b 两个引用置为 null 的时候,我们创建的两个对象的实例是不会被回收掉的,因为此时两个对象都被对方引用着,双方的引用计数均为 1,这就是 “循环引用”

可达性分析(JVM 使用)

为了解决上述引用计数产生的两个弊端(空间浪费和循环引用),这里提出了可达性分析算法。

可达性分析算法采用时间换空间的策略,这也是 JVM 寻找垃圾使用的算法

首先对代码的一些特定对象作为遍历的 “起点”:这些特定的对象包括 栈上的局部变量(引用变量)、常量池引用指向的对象、静态成员(引用类型的)【这些对象在程序运行到任何时刻都容易被捕获到的】
【因为不会扫描堆上的对象,所以不会存在因为 “循环引用” 而影响垃圾判断的现象】

然后进行遍历:判断某个对象是否能被访问到,在能访问到的对象标记为 “可达”。

当完成遍历后,把未标记为 “可达” 的对象标记为 “不可达”

JVM 通过上述操作,就知道哪些对象是 可达的,哪些是 不可达的,接下来就是回收垃圾的操作了。

举个例子:

class Node {
    Node left;
    Node right;
}

public class Test2 {
    public Node build() {
        Node a = new Node();
        Node b = new Node();
        Node c = new Node();
        Node d = new Node();
        Node e = new Node();
        Node f = new Node();
        Node g = new Node();
        a.left = b;
        a.right = c;
        b.left = d;
        b.right = e;
        f.right = g;
        return a;
    }
}

在这里插入图片描述
由于方法最后只返回 a 这个节点,这时候 f 和 g 节点就会被回收掉。

如何处理找到的垃圾???

这里介绍四种处理方式:标记–清除 算法,复制算法,标记整理 算法,分代算法,其中最后一个算法 分代算法是 JVM 真正使用的。

标记–清除 算法

在需要回收的内存上做上标记,然后进行回收清除操作,这是最直接的算法,但是存在一个缺点就是会产生内存碎片。

举个例子:假设一共有 7 块内存块,经过可行性分析算法得出 2、4、6 这三块内存为需要回收的内存,先做上标记,然后进行回收释放,但是空出来的内存碎片可能难以得到利用。
在这里插入图片描述

标记整理算法

由于标记清除算法会产生大量的内存碎片标记整理算法直接将垃圾回收设计为类似顺序表删除的形式, 将非垃圾的对象排到一起, 这样就可以空出大块的空闲内存块了.

但是缺点也是显而易见的, 就是系统开销比较大, 尤其是移动较大的对象的时候.
在这里插入图片描述

复制算法

将内存空间一分为二,当需要进行垃圾回收的时候,将非垃圾的内存块依次紧凑地复制到另一半内存中。

但是也存在两个缺点
1)内存消耗大,内存的空间利用率低
2)一旦非垃圾的内存块较多的时候,复制的成本就会较高,尤其是复制包含较大的对象的时候。

举个例子:要回收 1 和 4 号内存块。
在这里插入图片描述

分代算法 (JVM 使用)

分代算法是结合了上面两种算法的优点,取长补短的综合算法

JVM 将对象划分为 新生代和老年代,新生代又划分了三个区域分别是伊甸区、两个幸存区【空间占比为8:1:1】

分代算法的代指的是对象的年龄大小, 年龄的大小和 GC 轮次相关

在这里插入图片描述

命名的来源于圣经,人类发源地伊甸园,在这里指新生成的对象的存放位置,由于灭世洪水的降临,人类打造了诺亚方舟,成功躲避天灾的人门就是幸存者,这里的幸存区是指对象经过GC之后还能存留下来的存储位置

我们认为对象一开始生成的时候,是最容易被 GC 掉的,所以新生成的对象保存在 伊甸区里,经过 一轮GC 之后剩下的对象不会很多,将这些幸存的对象放到幸存区里,两个幸存区就是为了执行复制算法,经过多轮 GC 之后,还能在幸存区里存活的对象将放入到老年代。

一般来说,如果内存占用空间比较大的对象我们一般是直接放入老年代, 减少系统开销

新生代的 GC 频次较高
老年代的 GC 频次较低

因为我们认为要 GC 掉的早就 GC 掉了,之所以能存活在老年代,说明这个对象经常被使用,也就是说老年代的对象不需要高频次的 GC 次数,GC 的周期可以长一点,减少系统开销。

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

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

相关文章

HTTP / 2

序言 在之前的文章中我们介绍过了 HTTP/1.1 协议,现在再来认识一下迭代版本 2。了解比起 1.1 版本,后面的版本改进在哪里,特点在哪里?话不多说,开始吧⭐️! 一、 HTTP / 1.1 存在的问题 很多时候新的版本的…

使用vscode在本地和远程服务器端运行和调试Python程序的方法总结

1 官网下载 下载网址:https://code.visualstudio.com/Download 如下图所示,可以分别下载Windows,Linux,macOS版本 历史版本下载链接: https://code.visualstudio.com/updates 2 安装Python扩展工具 打开 VS Code,安装 Microsoft 提供的官…

免费为企业IT规划WSUS:Windows Server 更新服务 (WSUS) 之快速入门教程(一)

哈喽大家好,欢迎来到虚拟化时代君(XNHCYL),收不到通知请将我点击星标!“ 大家好,我是虚拟化时代君,一位潜心于互联网的技术宅男。这里每天为你分享各种你感兴趣的技术、教程、软件、资源、福利…

Ubuntu 24.04 LTS 安装 tailscale 并访问 SMB共享文件夹

Ubuntu 24.04 LTS 安装 tailscale 安装 Tailscale 官方仓库 首先,确保系统包列表是最新的: sudo apt update接下来,安装 Tailscale 所需的仓库和密钥: curl -fsSL https://tailscale.com/install.sh | sh这会自动下载并安装 …

基于python+Django+mysql鲜花水果销售商城网站系统设计与实现

博主介绍:黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育、辅导。 所有项目都配有从入门到精通的基础知识视频课程&#xff…

“AI人工智能内容辅助创作平台:让创意不再“卡壳”

在如今这个信息爆炸的时代,内容创作成了每个人的“必修课”。无论是自媒体大V、文案策划,还是普通学生写作文,大家都会遇到一个让人抓狂的问题——“创意枯竭”。有时候,脑袋里空空如也,一个字都写不出来,那…

【计算机网络】传输层协议TCP与UDP

传输层 传输层位于OSI七层网络模型的第四层,主要负责端到端通信,可靠性保障(TCP),流量控制(TCP),拥塞控制(TCP),数据分段与分组,多路复用与解复用等,通过TCP与UDP协议实现…

基础入门-传输加密数据格式编码算法密文存储代码混淆逆向保护安全影响

知识点: 1、传输格式&传输数据-类型&编码&算法 2、密码存储&代码混淆-不可逆&非对称性 一、演示案例-传输格式&传输数据-类型&编码&算法 传输格式 JSON XML WebSockets HTML 二进制 自定义 WebSockets:聊天交互较常…

DenseNet-密集连接卷积网络

DenseNet(Densely Connected Convolutional Network)是近年来图像识别领域中一种创新且高效的深度卷积神经网络架构。它通过引入密集连接的设计,极大地提高了特征传递效率,减缓了梯度消失问题,促进了特征重用&#xff…

记一次数据库连接 bug

整个的报错如下: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server. Attempted reconnect 3 times. Giving up. at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Metho…

Docker:基于自制openjdk8镜像 or 官方openjdk8镜像,制作tomcat镜像

一、制作openjdk8基础镜像【基于自定义alpine-3.18.0:v1 】 docker pull maven:3.5.0-jdk-8-alpine 78.56 MB https://hub.docker.com/_/maven/tagspage8&namealpine openjdk二进制下载地址 https://blog.csdn.net/fenglllle/article/details/124786948 https://adoptope…

vue 入门到实战 一

目录 第1章 初始Vue.js 1.1 网站交互方式 1.2 MVVM模式 1.3 Vue.js是什么 1.4 安装Vue.js 1.5 第一个Vue.js程序 1.6 插值与表达式 第1章 初始Vue.js 1.1 网站交互方式 Web网站有单页应用程序(SPA,Single-page Application)和多页应用…

【2024 年度总结】从小白慢慢成长

【2024 年度总结】从小白慢慢成长 1. 加入 CSDN 的契机2. 学习过程2.1 万事开头难2.2 下定决心开始学习2.3 融入技术圈2.4 完成万粉的目标 3. 经验分享3.1 工具的选择3.2 如何提升文章质量3.3 学会善用 AI 工具 4. 保持初心,继续前行 1. 加入 CSDN 的契机 首次接触…

Apache SeaTunnel 2.3.9 正式发布:多项新特性与优化全面提升数据集成能力

近日,Apache SeaTunnel 社区正式发布了最新版本 2.3.9。本次更新新增了Helm 集群部署、Transform 支持多表、Zeta新API、表结构转换、任务提交队列、分库分表合并、列转多行 等多个功能更新! 作为一款开源、分布式的数据集成平台,本次版本通过…

mybatis的多对一、一对多的用法

目录 1、使用VO聚合对象(可以解决这两种情况) 多对一: 一对多: 2、非聚合的多对一做法: 3、非聚合的一对多做法: 1、使用VO聚合对象(可以解决这两种情况) 当我需要多对一、一对…

SpringCloud系列教程:微服务的未来(十四)网关登录校验、自定义过滤器GlobalFilter、GatawayFilter

前言 在微服务架构中,API 网关扮演着至关重要的角色,负责路由请求、执行安全验证、流量控制等任务。Spring Cloud Gateway 作为一个强大的网关解决方案,提供了灵活的方式来实现这些功能。 本篇博客将重点介绍如何在 Spring Cloud Gateway 中…

服务化架构 IM 系统之应用 MQ

在微服务化系统中,存在三个最核心的组件,分别是 RPC、注册中心和MQ。 在前面的两篇文章(见《服务化架构 IM 系统之应用 RPC》和《服务化架构 IM 系统之应用注册中心》)中,我们站在应用的视角分析了普适性的 RPC 和 注…

Linux-C/C++--深入探究文件 I/O (上)(文件的管理、函数返回错误、exit()、_Exit()、_exit())

经过上一章内容的学习,相信各位读者对 Linux 系统应用编程中的基础文件 I/O 操作有了一定的认识和理解了,能够独立完成一些简单地文件 I/O 编程问题,如果你的工作中仅仅只是涉及到一些简单文件读写操作相关的问题,其实上一章的知识…

内网渗透测试工具及渗透测试安全审计方法总结

1. 内网安全检查/渗透介绍 1.1 攻击思路 有2种思路: 攻击外网服务器,获取外网服务器的权限,接着利用入侵成功的外网服务器作为跳板,攻击内网其他服务器,最后获得敏感数据,并将数据传递到攻击者&#xff0…

豆包MarsCode:小C点菜问题

问题描述 思路分析 这道题的核心任务是找出所有不超过给定价格 m 的菜肴中,最常见的菜肴价格,最后返回该价格的出现次数。 1. 题意理解: 给定一个最大价格 m,小C只会选择价格不超过 m 的菜。菜单上有 n 道菜,每道菜…