JVM、JVM中的垃圾回收、类加载、IoC和DI

news2025/2/26 23:17:37

一、JVM 

1、概念

JVM:Java Virtual Machine 的简称,意为 Java虚拟机,可以运行Java代码,是整个Java实现跨平台的最核心的部分;所有的Java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行,也就是说class并不直接与机器的操作系统相对应,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行。支持多种语言,理论上无论哪种编程语言,只要能将源代码编译为*.class代码,都能在JVM上解释运行。

 PS:JDK=JRE+开发工具集;JRE=JVM+JavaSE标准类库

虚拟机:是指通过软件模拟的具有完整硬件功能的、运行在一个完全隔离的环境中的完整计算机系统。常见的虚拟机:JVM、VMwave、Virtual Box
PS(有关JVM、编译与解释阶段): http://t.csdn.cn/yApO5

2、JVM 运行时数据区,也叫内存布局

PS:可参考:

JVM内存划分_jvm内存换分_走在小路的博客-CSDN博客

JVM中的五大内存区域划分详解_jvm内存划分_Archie_java的博客-CSDN博客

 2.1、堆:主要存的是程序里新建的所有对象。

堆内存里还分为新生代和老生代。新生代放新建的对象,当经过一定 GC 次数之后还存活的对象
会放入老生代。新生代还有 3 个区域:一个 Endn + 两个 Survivor(S0/S1)。垃圾回收的时候会将 Endn 中存活的对象放到一个未使用的 Survivor 中,并把当前的 Endn 和正在使用的 Survivor 清楚掉。

HotSpot实现的复制算法流程如下:
1. 当Eden区满的时候,会触发第一次Minor gc,把还活着的对象拷贝到Survivor From区;当
Eden区再次触发Minor gc的时候,会扫描Eden区和From区域,对两个区域进行垃圾回收,经过
这次回收后还存活的对象,则直接复制到To区域,并将Eden和From区域清空。
2. 当后续Eden又发生Minor gc的时候,会对Eden和To区域进行垃圾回收,存活的对象复制到
From区域,并将Eden和To区域清空。
3. 部分对象会在From和To区域中复制来复制去,如此交换15次(由JVM参数
MaxTenuringThreshold决定,这个参数默认是15),最终如果还是存活,就存入到老年代

2.2、程序计数器:存的是CPU下一条要执行的指令的地址,用来记录当前线程执行的行号的。

如果当前线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是一个Native方法,这个计数器值为空。
程序计数器内存区域是唯一一个在JVM规范中没有规定任何OOM(OutOfMemoryError)情况的区域。

2.3、栈:主要存局部变量、方法调用相关的信息。

2.3.1、Java虚拟机栈(给JVM使用)

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

JVM栈包含有四个部分:

1. 局部变量表: 存放了编译器可知的各种基本数据类型(8大基本数据类型)、对象引用。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在执行期间不会改变局部变量表大小。简单来说就是存放方法参数和局部变量。
2. 操作栈:每个方法会生成一个先进后出的操作栈。
3. 动态链接:指向运行时常量池的方法引用。
4. 方法返回地址:PC 寄存器的地址。

2.3.2、本地方法栈(给本地方法使用)

和虚拟机栈类似。

2.4、方法区:类对象(.class 加载到内存里就是类对象)、静态成员等。

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

在《 Java 虚拟机规范中》把此区域称之为 方法区 ,而在 HotSpot 虚拟机的实现中,在 JDK 7 时此区域叫做永久代 JDK 8 中叫做元空间
PS:JDK8 中将字符串常量池移动到了堆中。
运行时常量池是方法区的一部分,存放字面量与符号引用。
字面量 : 字符串 、final常量、基本数据类型的值。
符号引用 : 类和结构的完全限定名、字段的名称和描述符、方法的名称和描述符。

二、JVM 中的垃圾回收

1、垃圾回收

1.1、概念:

Java垃圾回收是Java程序执行自动内存管理的过程。Java程序编译为字节码,可以在Java虚拟机(简称JVM)上运行。当Java程序在JVM上运行时,将在堆上创建对象,这是专用于该程序的内存的一部分。最终,将不再需要某些对象。垃圾收集器找到这些未使用的对象并将其删除以释放内存。

有一些对象在使用之后就不再使用了,就称之为“垃圾”。

1.2、死亡对象的判断算法

1.2.1、引用计数法

给对象增加一个引用计数器,每当有一个地方引用它时,计数器就+1;当引用失效时,计数器就-1;任何时刻计数器为0的对象就是不能再被使用的,即对象已"死"。

优点:简单。

缺点:会浪费一部分内存空间;不能回收循环引用的对象。

1.2.2、可达性分析算法

通过一系列称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称之为"引用链",当一个对象到GC Roots没有任何的引用链相连时(从GC Roots到这个对象不可达)时,证明此对象是不可用的。

Java语言中,可作为 GC Roots 的对象:

1. 虚拟机栈 ( 栈帧中的本地变量表 ) 中引用的对象;
2. 方法区中类静态属性引用的对象;
3. 方法区中常量引用的对象;
4. 本地方法栈中 JNI(Native 方法 ) 引用的对象。

PS:以下图为例:(5~7之间虽然还有关联,但是它们到GC Roots是不可达的,因此会被判定为可回收对象)

优点:避免了空间浪费;解决了循环引用的问题。

缺点:系统开销大,遍历一次可能比较慢。

1.3、垃圾回收算法

1.3.1、标记-清除:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。

 缺点:效率都不高;会产生大量不连续的内存碎片。

1.3.2、复制:为了解决标记-清除法效率不高的问题。

它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这块内存需要进行垃圾回收时,会将此区域还存活着的对象复制到另一块上面,然后再把已经使用过的内存区域一次清理掉。

 优点:解决了内存碎片问题;算法简单、高效。

缺点:内存空间利用率低;若保留的对象很多,要释放的对象较少,此时复制的开销就会较大。

1.3.3、标记-整理

标记过程仍与 " 标记 - 清除 " 过程一致,但后续步
骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的 内存。流程图如下:

 

1.3.4、分代 

分代算法是通过区域划分,实现不同区域和不同的垃圾回收策略,从而实现更好的垃圾回收。
是根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代(一般创建的对象都会进入新生代)和老年代(大对象和经历了15次(默认情况下)垃圾回收仍存活下来的对象会从新生代移动到老年代)。在新生代中,每次垃圾回收都有大批对象死去,只有少量存活,因此我们采用复制算法;而老年代中对象存活率高、没有额外空间对它进行分配担保,就必须采用"标记-清理"或者"标记-整理"算法。

PS: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倍以上。 

1.4、垃圾收集器:垃圾回收的具体体现

作用:是为了保证程序能够正常、持久运行的一种技术,它是将程序中不用的死亡对象也就是垃圾对象进行清除,从而保证了新对象能够正常申请到内存空间。

 PS:存在连线表示两者之间可以搭配使用。

  • 串行:所有垃圾回收事件都在一个线程中串行进行。在每个垃圾回收之后执行压缩。
  • 并行:多个线程用于次要垃圾回收。单线程用于大型垃圾回收和旧式压缩。另外,Parallel Old变量使用多个线程进行主要垃圾收集和Old Generation压缩。

1.5、一个对象的一生

我是一个普通的 Java 对象,我出生在 Eden 区,在 Eden 区我还看到和我长的很像的小兄弟,我们在 Eden 区中玩了挺长时间。有一天 Eden 区中的人实在是太多了,我就被迫去了 Survivor 区的 “From” 区( S0 区),自从去了 Survivor 区,我就开始漂了,有时候在 Survivor “From” 区, 有时候在 Survivor “To” 区( S1 区),居无定所。直到我 18 岁的时候,爸爸说我成人了,该去社会上闯闯了。于是我就去了年老代那边,年老代里,人很多,并且年龄都挺大的,我在这里也认识了很多 人。在老年代里,我生活了很多年(每次GC 加一岁)然后被回收了。

2、分代回收的工作过程

PS:可参考:

(1条消息) java -- 分代回收_java 分代回收_404QAQ的博客-CSDN博客

分代垃圾回收过程_JuHootin的博客-CSDN博客

三、JVM 中的类加载的过程以及双亲委派模型

1、类加载

类的生命周期

1.1、加载:主要是把 .class 文件加载到内存中。

在加载 Loading 阶段,Java虚拟机需要完成以下三件事情:
1)通过一个类的全限定名来获取定义此类的二进制字节流。==》根据雷鸣找到 class文件
2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。==》把 class文件加载到内存中
3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。==》创建一个类对象

1.2、连接

1.2.1、验证:验证 class文件是否符合JVM标准、是否会危害虚拟机。

1.2.2、准备:给静态变量赋初始值(初始化值为0)。

1.2.3、解析:JVM 将常量池内的符号引用替换为直接引用的过程,也就是初始化常量的过程。

1.3、初始化:执行构造方法的过程。

PS:如果在自己的代码中创建一个java.lang.String的类,是不能被类加载器加载的。因为虚拟机会抛出 ‘java.lang.SecurityException:Prohibited package name:java.lang’ 异常。

PS:一个类加载例子:

public class Test extends B{
    public static void main(String[] args) {
        new Test();//362154
        new Test();//2154
//        结果:
//        A 的静态代码块
//        B 的静态代码块
//        A 的构造代码块
//        A 的构造方法
//        B 的构造代码块
//        B 的构造方法
//        A 的构造代码块
//        A 的构造方法
//        B 的构造代码块
//        B 的构造方法
    }
}
//创建类实例时,先进行类加载
//类加载时,先执行静态方法
//在执行构造代码块,再执行构造方法
//先执行父类,再执行子类
//类加载只需要执行一次,所以静态方法也只执行一次
class A{
    public A(){
        System.out.println("A 的构造方法");//1
    }
    {
        System.out.println("A 的构造代码块");//2
    }
    static {
        System.out.println("A 的静态代码块");//3
    }
}
class B extends A{
    public B(){
        System.out.println("B 的构造方法");//4
    }
    {
        System.out.println("B 的构造代码块");//5
    }
    static {
        System.out.println("B 的静态代码块");//6
    }
}

2、双亲委派模型

2.1、概念

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

2.2、优点

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

2.3、缺点

双亲委派模型虽然有其优点,但在某些情况下也存在一定的问题,比如 Java 中 SPI(Service Provider Interface,服务提供接口,是 Java 提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。 SPI 的作用就是为这些被扩展的 API 寻找服务实现。)机制中的 JDBC 实现。

四、IoC 和 DI 

1、概念

IoC: Inversion of Control,控制反转。通过将对象交给 Spring 中 IoC 容器管理,在其他类中不直接 new 对象,而是通过将对象传递到当前类的方式来实现解耦的(耦合是指:两个或两个以上对象存在依赖,当一方修改之后会影响另一方,那么就说这些对象间存在耦合。而解耦就是解除两个或两个以上对象,修改之后影响另一方的问题。)。这样,控制权由应用代码转移带了容器,控制权发生了反转,这就是控制反转,它是spring框架的核心思想之一。

DI:Dependency Injection,依赖注入。由 IoC 容器在运⾏期间,动态地将某种依赖关系注⼊到对象之中。

PS:可参考:

http://t.csdn.cn/4vRKA

面试突击73:IoC 和 DI 有什么区别?-阿里云开发者社区 (aliyun.com)

2、区别

IoC:是一个思想,也就是一种指导原则,最终还是要有可行的落地方案。强调的是将对象实例的创建控制权由spring容器来统一管理,需要的时候从容器中取出,而不是由调用者自身去创建,从而达到降低代码耦合性与硬代码的目的。

DI:是一种具体的实现。强调的是当调用者需要使用对象实例时,spring容器为调用者提供对象实例这个过程。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022-ISCC信息安全与对抗竞赛wp-misc(详解,有脚本( •̀ ω •́ )y)

前言 没想到不知不觉一年时间就这样过去了,又到了一年一度的ISCC信息对抗大赛,不知道去年打比赛的小伙伴今年还能不能再碰到,期待与君再相见( •̀ ω •́ )y 所以今天就把去年的题目再复现一遍供师傅们参考 嘻嘻 目录 misc &#xff08…

2023MathorCup数模B题思路数据代码论文【全网最全分享】

文章目录赛题思路赛题详情参赛建议(个人见解)选择队友及任务分配问题(重要程度:5星)2023MathorCup数模B题思路数据论文代码【最新】赛题思路 (赛题出来以后第一时间在CSDN分享) 最新进度在文章最下方卡片,…

STM-32:I2C外设总线—硬件I2C读写MPU6050

目录一、I2C外设简介二、I2C框图三、I2C基本结构四、主机发送五、主机接收六、I2C的中断请求七、软件/硬件波形对比八、应用实例:硬件I2C读写MPU60508.1接线图8.2程序代码一、I2C外设简介 STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、…

Blender安装最新版本

目录1. Blender下载1.1 Blender硬件要求1.2 下载Blender1.3 或者下载LTS版本2. 安装向导2.1 在Windows上安装2.1.1安装msi文件(这里演示案例)2.1.2 点击“Next”2.1.3 勾选,点击“Next”2.1.4 点击“Next”2.1.5 点击“Install”2.1.6 安装进度2.1.7 点击“Finish”…

来了,简单宣告下 Compose for iOS Alpha 正式发布

来了来了,盼星星盼月亮,广大 Compose 开发者期待许久的 Jetpack Compose Multiplatform for iOS 近期终于正式发布了 Alpha 支持,其实在此之前,我在 《一文快速带你了解 KMM 、 Compose 和 Flutter 的现状》 等文章里说了很多次 C…

十三款MySQL可视化管理工具

文章目录一、DBeaver二、DataGrip三、phpMyAdmin四、MySQLDumper五、Navicat六、MySQL GUI Tools七、MySQL ODBC Connector八、MySQL Workbench九、SQLyog十、MySQL-Front十一、dbForge Studio for MySQL十二、HeidiSQL十三、Beekeeper StudioMySQL 的管理维护工具非常多&#…

【C++ 二】选择结构、循环结构、跳转语句

选择结构、循环结构、跳转语句 文章目录选择结构、循环结构、跳转语句前言1 选择结构1.1 if语句1.2 三目运算符1.3 switch 语句2 循环结构2.1 while 循环语句2.2 do...while 循环语句2.3 for 循环语句2.4 嵌套循环3 跳转语句3.1 break 语句3.2 continue 语句3.3 goto语句总结前…

Unity | Video Player的使用方法和原理

讲解逻辑为Unity | 基础逻辑_菌菌巧乐兹的博客-CSDN博客 一、把素材仓库里的视频放进Game界面 1.我们需要在素材仓库中放一个视频(其他文件夹不用管,我只是懒得删了) 2.在Unity里建一个能接收视频的地方VideoPlayer 备注:视频和…

【Python基础入门学习】Python背景知识及介绍

一、背景知识 主流编程语言:java/python/c(c) Python主要应用领域:人工智能、大数据 云计算领域的openstack 框架就是Python写的 测试发展方向:测开、安全、性能 1.1 发展过程 机器语言(二进制)-- 汇编语言 – 高级汇…

招募妙记多 Mojidoc 体验官| 我们准备了诚意大礼,就等你来

在以往的推文和妙记多 Mojidoc 的官方社群里,我们分享、见证了很多妙记多 Mojidoc 用户在产品使用过程中的故事。 我们欣喜于大家对 妙记多 Mojidoc 的信任,也惊喜于在这个过程中,大家对于产品的优化和升级,不遗余力地提出建议和…

IDEA虚拟机参数配置【自我总结】

idea内存溢出问题 最近自己在跑一个大一点的微服务项目时候,发生了这样的一个现象,就是启动idea的时候,首先发现电脑的CPU小风扇急速的飞转,电脑的运行内存也快要飙升到100%了,而且最重要的是光是加载项目就加载了几分钟啊(我当时就炸锅锅)像下图一样,一直加载一直加载 想了想,…

一天吃透计算机网络八股文

网络分层结构 计算机网络体系大致分为三种,OSI七层模型、TCP/IP四层模型和五层模型。一般面试的时候考察比较多的是五层模型。最全面的Java面试网站 五层模型:应用层、传输层、网络层、数据链路层、物理层。 应用层:为应用程序提供交互服务…

MySQL:开窗函数

当查询条件需要用到复杂子查询时,聚合函数操作起来非常麻烦,因此使用开窗函数能够轻松实现。 注意:在Oracle中称为分析函数。 在MySQL中称为开窗函数,使用于MySQL8.0以上版本,sql sever、hive、Oracle等。 1 开窗函数…

Redis为什么能抗住10万并发?揭秘性能优越的背后原因

1. Redis简介 Redis是一个开源的,基于内存的,高性能的键值型数据库。它支持多种数据结构,包含五种基本类型 String(字符串)、Hash(哈希)、List(列表)、Set(集…

Win10的两个实用技巧系列之华硕电脑设置面部识别的技巧、删除背景图片的方法

Win10系统的华硕电脑怎么使用人脸解锁? 华硕电脑设置面部识别的技巧 Win10系统的华硕电脑怎么使用人脸解锁?华硕电脑想要添加面部识别,方便人脸解锁,下面我们就来看看华硕电脑设置面部识别的技巧 有些使用Win10系统的华硕电脑的用户&#…

X509证书以及相关java常用接口

二、X509证书 X.509证书是一种数字证书标准,用于验证在计算机网络中的身份认证。它们是由权威机构(例如CA)发行,包含有关证书持有者身份信息的数字签名。X.509证书通常用于SSL / TLS协议,以确保客户端和服务器之间的安…

Word转PDF:一键转换,快速高效

在现代的工作和学习中,我们经常需要将Word文档转换成PDF文件以便于分享和保留格式。虽然Word软件自身也可以保存为PDF格式,但在某些情况下,我们需要一个更快捷、高效的转换工具来帮助我们完成任务。下面将介绍一款快速高效的Word转PDF在线转换…

机器学习与深度学习——通过SVM线性支持向量机分类鸢尾花数据集iris求出错误率并可视化

线性支持向量机 先来看一下什么叫数据近似线性可分,如下图所示,蓝色圆点和红色圆点分别代表正类和负类,显然我们不能找到一个线性的分离超平面将这两类完全正确的分开;但是如果将数据中的某些特异点(黑色箭头指向的点)去除之后&a…

华为ensp配置实验大全(免费持续更新)

点开一篇文章 ,“分享”要收费。 点开一篇文章, "大全"但就两个实验。 点开一篇文章, “详细"但全截图。 我忽略了最重要的东西"产品说明书" 产品说明书优势 "规范" "详细" 此文寻找官方手册中…

BUUCTF-PWN-[第五空间2019 决赛]PWN5

这题考到 格式化字符串的方法 我之前没有学过 根据wp写完这题去看看原理 下载打开环境 checksec看看 发现有三个保护 nx打开 所以无法写入shellcode 现在看看ida32 发现/bin/sh 进去看 发现就在主函数里面 我们进行代码审计 发现输入名字 他会返回名字 然后再输入密码 如果…