【JavaEE初阶】-- JVM

文章目录

  • 1. JVM运行流程
  • 2. Java运行时数据区
    • 2.1 方法区(内存共享)
    • 2.2 堆(内存共享)
    • 2.3 Java虚拟机栈(线程私有)
    • 2.4 本地方法栈(线程私有)
    • 2.5 程序计数器(线程私有)
  • 3. JVM 类加载
    • 3.1 类加载的过程
      • 3.1.1 加载
      • 3.1.2 验证
      • 3.1.3 准备
      • 3.1.4 解析
      • 3.1.5 初始化
      • 3.1.6 使用
      • 3.1.8卸载
    • 3.2 双亲委派模型
      • 3.2.1 什么是双亲委派模型
      • 3.2.2 破坏双亲委派模型
  • 4. 垃圾回收
    • 4.1 死亡对象的判断方法
      • 4.1.1 引用计数算法
      • 4.1.2 可达性分析算法(JVM使用的算法)
    • 4.2 垃圾回收算法
      • 4.2.1 标记-清除算法
      • 4.2.2 复制算法
      • 4.2.3 标记-整理算法
        • 4.2.4 分代法
    • 4.3 垃圾收集器
      • 4.3.1 Serial收集器(新生代收集器,串行GC)
      • 4.3.2 ParNew收集器(新生代收集器,并行GC)
      • 4.3.3 CMS 收集器(标记-清楚算法)

JVM就是Java虚拟机。虚拟机是指通过软件虚拟出来的具有完整硬件功能的计算机系统,它的运行环境是完全隔离的。

我们知道Java是一个跨平台的语言,可以不加修改的在任何操作系统中运行,这就是依托于其运行在JVM中实现的。

1. JVM运行流程

  1. .java 文件 被编译成 .class 文件,。
  2. 通过 类加载子系统 将 .class 二进制字节码文件加载到内存中。
  3. 方法区保存类对象,类对象是new对象的模板。
  4. new出来的对象全都放在堆内存中。
  5. 每个线程都会在Java虚拟机栈中分配一个与之对应的内存空间,栈中存放是是线程对方法的调用层级。
  6. 本地方法栈中存放的是本地方法的调用层级。
  7. 程序计数器,保存的是当前线程执行的行号。
    在这里插入图片描述

2. Java运行时数据区

在这里插入图片描述

2.1 方法区(内存共享)

方法区保存的是类的类对象,这个类对象就是我们在new对象时的模板。由于存放的是类对象,是公共的数据,所以方法区是线程共享的,所以线程都可以访问这个区域。

在JDK7的实现中被称为永久代。
在JDK8的实现中被称为元空间。

2.2 堆(内存共享)

所有new出来的对象都存放在堆中。堆是JVM内存中使用最大的内存区域,默认占内存的八分之一,不过这个比例是可以JVM参数设置进行设置的。

2.3 Java虚拟机栈(线程私有)

每创建一个线程就会在内存中创建一个与之对应的Java虚拟机栈。Java虚拟机栈的生命周期和线程是相同的,线程结束,对应的Java虚拟机栈就会被垃圾回收掉。

在这个Java虚拟机栈中,调用一个方法就会将该方法压入栈,此时我们将其成为栈帧,当方法执行完之后就会出栈,知道这个栈中的栈帧全部出栈,此时就代表着栈空了,也就意味线程结束了。

在这里插入图片描述

2.4 本地方法栈(线程私有)

调用本地方法时使用的栈,记录本地方法的调用层级。

2.5 程序计数器(线程私有)

我们知道多个线程在CPU上是抢占式执行的。那么线程重新调度到CPU上怎么知道上一次执行到了什么地方呢,就是通过程序计数器来记录的。

3. JVM 类加载

3.1 类加载的过程

在这里插入图片描述

3.1.1 加载

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

3.1.2 验证

验证.class文件是否符合JVM规范。
JVM17规范
在这里插入图片描述

3.1.3 准备

比如我们定义了一个常量count。
java public static int count = 100;
在准备阶段仅仅是给count分配一个内存空间,并给设置其初始值,就想count,其设置的初始值是0。

3.1.4 解析

将常量池内的符号引用替换为直接引用的过程。也就是把字节码中的符号引用和真实的内存进行了解析关联。

3.1.5 初始化

执行代码中的真正的赋值操作。

3.1.6 使用

使用阶段就是new对象的过程,执行构造方法,以及父类的构造方法,初始化完成之后一个对象就创建出来了。

3.1.8卸载

程序停止时从jvm中卸载。

3.2 双亲委派模型

使用哪个类加载器进行加载类的过程。

3.2.1 什么是双亲委派模型

在这里插入图片描述

  1. 当我们创建一个类时,先从应用程序加载器开始向上转发,一直转发到启动类加载器。
  2. 类启动加载器在自己的路径下找,看有没有要创建的这个类,有则加载,没有就继续向下转发到扩展加载器。
  3. 扩展加载器在自己的路径下找,看有没有要创建的这个类,有则加载,没有就继续向下转发到应用程序加载器。
  4. 应用程序加载器在自己的路径中找到类并加载。

3.2.2 破坏双亲委派模型

JDBC就是一个典型的案例。
在这里插入图片描述

这段源码的说明的翻译:返回此线程的上下文类加载器。该上下文类加载器由线程的创建者提供,供在此线程中运行的代码在加载类和资源时使用。如果未设置,则默认值为父线程的类加载器上下文。原始线程的上下文类加载器通常设置为用于加载应用程序的类加载器。 返回值:此线程的上下文类加载器,若无则返回 null,表示系统类加载器(若上述情况均不成立,则返回引导类加载器) 异常情况:如果存在安全管理者,并且调用者的类加载器不为空且与上下文类加载器不同或不是其祖先,同时调用者未拥有“getClassLoader”这一运行时权限,则会抛出 SecurityException 异常。

Java平台里面自身定义了一套API访问接口,数据库厂商需要实现这个API,厂商实现了这个接口之后会自己提供的一些jar包,供用户来使用。

比如我们使用的是MySQL,DriveManager 调用getConnection,getConnection并不知道要使用MySQL,这里就自己指定了自己要用的加载器,使用的是当前线程的上下文问的加载器,此时就破坏了双亲委派模型机制,在加载类时并没有向上传递,而是直接指定了相应的加载器。

4. 垃圾回收

垃圾回收的是对象占用的内存空间,主要说的是堆内存。程序计数器、虚拟机栈、本地方法栈都是和线程同生同死。

4.1 死亡对象的判断方法

4.1.1 引用计数算法

给每一个对象增加一个引用计数器,每当一个地方引用了该对象时,引用计数器就加一,引用失效时,就减一。当引用计数器为0时,就代表该对象死亡了,是可被回收的。

引用计数法实现较简单,判断效率也比较高,但是引用计数法无法解决循环引用的问题 会导致出现内存泄露的情况。

循环依赖的例子:

public class Test {public Object instance = null;private static int _1MB = 1024 * 1024;private byte[] bigSize = new byte[2 * _1MB];public static void testGC() {Test test1 = new Test();Test test2 = new Test();test1.instance = test2;test2.instance = test1;test1 = null;test2 = null;
// 强制jvm进⾏垃圾回收System.gc();}public static void main(String[] args) {testGC();}
}

JVM并未采用这种方法,但是python使用的是这种算法。

4.1.2 可达性分析算法(JVM使用的算法)

通过以一系列的GC-root的对象作为起始点,从起始点开始向下进行搜索,搜索时走过等我路线,就是引用链,当一个对象没有在任何一个引用链上,就表示该对象是不可用的,就会被标记为可回收。那么在下次垃圾回收的时候就会被回收掉。
在这里插入图片描述
在Java语言中,可作为GC Roots的对象包含下面几种:

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

4.2 垃圾回收算法

4.2.1 标记-清除算法

  1. JVM会根据可达性分析算法来标记可回收的内存区域。
  2. 对标记可回收的内存区域进行回收。
    在这里插入图片描述

但是标记-清除算法会使内存区域变得很分散 如果此时进来了一个大对象,将会没有足够的空间进行存储,此时就会触发垃圾回收,如果垃圾回收之后还没有足够的空间进行存储,还会再次进行垃圾回收,一直这样操作,直到有足够大的空间能够存储这个大对象。

当进行垃圾回收的时候,会停止所有的线程STW,这个停止的时间是没有办法控制的,这是非常危险的。

4.2.2 复制算法

这种算法会将内存区域分成两个部分,我们称为内存区域一和内存区域二。程序运行的时候只使用一个内存区域,另一个内存区域是空闲的。

在这里插入图片描述

  1. 把内存区域一中存储的对象,复制到内存区域二中。
  2. 在内存区域二中把对象按内存地址顺序排列好,相当于对内存进行了整理。
  3. 把内存区域一的空间全部清空。
  4. 每次回收都重复上述工作。

但是这种算法的内存利用率不高。

4.2.3 标记-整理算法

标记整理算法和复制算法一样,但是标记整理算法不是将可回收对象进行清理,而是将存活对象向内存的一边缘移动,然后清除掉边缘以外的内存区域。

在这里插入图片描述

4.2.4 分代法

分代法是将内存分为两个区域:新生代 和 老年代,这两个区域的默认比例是1:2。

在这里插入图片描述

新生代使用的复制算法,老年代使用的是标记整理算法。

新生代中存放的是刚new出来的对象,老年代中存放的是经过多次GC(默认是15次),也没有被回收掉的对象。

新生代的内存区域又被分为Eden区和s1区、s2区 这个比例默认是8:1:1。

垃圾回收的过程:

  1. 所有的新 new的对象都会存放在新生代的Eden区。
    在这里插入图片描述

  2. 当Eden区满了之后,就会触发一轮GC,如果对象没有被回收将会被复制到FROM区,然后把Eden区内存全部清空。
    在这里插入图片描述

  3. 当触发下一次GC时,会将FROM区和Eden区仍然存活的对象复制到TO区。

  4. 接下来就是s1 和 s2进行呼唤from 和 to区,重复上面的步骤。

  5. 如果经历了一轮GC对象没有被回收掉,年龄 +1,如果年龄超过15岁(默认是15 ,但是可以进行设置),年龄保存在对象头中。

  6. 如果对象的年龄超过15岁就会被移入老年代。

如果一个很大的对象被创建,Eden区放不下怎么办??
会尝试将该对象直接放入老年代。

以上的所有比例都是可以设置的!!!

4.3 垃圾收集器

垃圾收集器是对垃圾回收算法的具体实现。

在这里插入图片描述

4.3.1 Serial收集器(新生代收集器,串行GC)

Serial收集器是虚拟机运行在Client模式下的默认新生代收集器。

与其他收集器相比,该收集器简单而高效(原因:由于是单线程的,没有线程交互的开销)。
在这里插入图片描述

4.3.2 ParNew收集器(新生代收集器,并行GC)

它是Serial收集器的多线程版本。

ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器。

在这里插入图片描述

4.3.3 CMS 收集器(标记-清楚算法)

CMS(Concurrent Mark Sweep)收集器是⼀种以获取最短回收停顿时间为目标的收集器。

CMS的运行过程:

  1. 初始标记:标记一下GC Roots 能直接关联到的对象,速度很快,需要STW(为什么?因为保证在标记开始时,引用关系不会发生变化)。
  2. 并发标记:从“初始标记”的对象开始,并发的便利整个对象图,标记所有可达对象。整个操作是和用户线程并发进行的,所以对象的引用关系可能会发生变化。
  3. 重新标记:会STW,修正并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分。
  4. 并发清除:和用户进程并发进行,清除掉在标记阶段判断为死亡的对象。
    在这里插入图片描述

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

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

相关文章

第十四届蓝桥杯青少组C++选拔赛[2023.1.15]第二部分编程题(4 、移动石子)

参考程序1&#xff1a;#include <bits/stdc.h> using namespace std; int main() {int N;cin >> N;vector<int> stones(N);int sum 0;for (int i 0; i < N; i) {cin >> stones[i];sum stones[i];}int target sum / N; // 每个篮子的平均值int a…

Spring Boot 的注解是如何生效的

在 Spring 中&#xff0c;Configuration、ComponentScan、Bean、Import 等注解的扫描、解析和 BeanDefinition 注册是一个分层处理的过程。下面我们以 Configuration 类为例&#xff0c;结合代码流程详细说明其从扫描到注册的完整逻辑。 1. 整体流程概览 以下是核心步骤的流程图…

Django REST Framework响应类Response详解

概述 Response 类是一个智能的 HTTP 响应类&#xff0c;能够根据客户端请求的内容类型&#xff08;Content-Type&#xff09;自动将数据渲染成合适的格式&#xff08;JSON、XML、HTML等&#xff09;。 基本用法 from rest_framework.response import Response# 最简单的用法 de…

# 小程序 Web 登录流程完整解析

登录流程完整小白解析&#xff08;小程序 & Web&#xff09; 在开发中&#xff0c;登录是每个系统最基础的功能。为了让小白也能理解&#xff0c;我们用通俗类比和流程讲解 小程序登录、Web 登录、Token 刷新、安全存储等整个过程。1️⃣ 小程序登录流程&#xff08;小白理…

安装vcenter6.7 第二阶段安装很慢 或卡在50%

DNS、FQDN配置的问题采用VCSA安装vCenter时&#xff0c;第一步安装还算顺利&#xff0c;第二步就会安装失败&#xff0c;而且还特别慢&#xff0c;这是因为部署时需要DNS服务器&#xff0c;下面就是不采用DNS服务器的部署方案。第一步&#xff1a;正常安装&#xff0c;DNS就写本…

第十六届蓝桥杯软件赛 C 组省赛 C++ 题解

大家好&#xff0c;今天是 2025 年 9 月 11 日&#xff0c;我来给大家写一篇关于第十六届蓝桥杯软件赛 C 组省赛的C 题解&#xff0c;希望对大家有所帮助&#xff01;&#xff01;&#xff01; 创作不易&#xff0c;别忘了一键三连 题目一&#xff1a;数位倍数 题目链接&…

项目帮助文档的实现

项目帮助文档的实现 代码如下&#xff1a; #ifndef __M_HELPER_H__ #define __M_HELPER_H__ #include <iostream> #include <fstream> #include <string> #include <vector> #include <sqlite3.h> #include <random> #include <sstream…

python逆向-逆向pyinstaller打包的exe程序反编译获取源代码

python逆向-逆向pyinstaller打包的exe程序反编译获取源代码 Pyinstaller pyinstaller 是一个用于将 Python 程序打包成独立可执行文件的工具&#xff0c;能够在没有 Python 解释器的情况下运行。 Python 脚本转换为 Windows、macOS 和 Linux 操作系统上的可执行文件。 把Python…

【SQL】-- sql having 和 where 的 区别

HAVING 和 WHERE 都是用来筛选数据的&#xff0c;但它们的应用场景有所不同。WHERE&#xff1a;用于筛选行数据&#xff0c;通常在 FROM 子句之后执行。它在分组操作 (GROUP BY) 之前应用&#xff0c;用来筛选出符合条件的记录。示例&#xff1a;SELECT name, age FROM employe…

MySQL,SQL Server,PostgreSQL三种数据库各自的优缺点,分别适用哪些场景

MySQL的优缺点及适用场景优点开源免费&#xff0c;社区版可商用&#xff0c;成本低。轻量级&#xff0c;安装配置简单&#xff0c;适合中小型项目。读写性能优异&#xff0c;尤其在OLTP&#xff08;在线事务处理&#xff09;场景下表现突出。支持主从复制、分片等扩展方案&…

Java 类加载机制双亲委派与自定义类加载器

我们来深入解析 Java 类加载机制。这是理解 Java 应用如何运行、如何实现插件化、以及解决一些依赖冲突问题的关键。一、核心概念&#xff1a;类加载过程一个类型&#xff08;包括类和接口&#xff09;从被加载到虚拟机内存开始&#xff0c;到卸载出内存为止&#xff0c;它的整…

Kaggle项目实践——Titanic: Machine Learning from Disaster

泰坦尼克号沉船事件是机器学习领域最经典的入门项目之一。Kaggle 上的 Titanic: Machine Learning from Disaster 竞赛&#xff0c;被无数人称为“机器学习的 Hello World”。 一、数据导入与清洗&#xff1a;让数据从 “杂乱” 变 “干净” 机器学习模型就像 “挑食的孩子”…

Qt C++ 复杂界面处理:巧用覆盖层突破复杂界面处理难题​之二

接上一篇&#xff0c;继续探索“覆盖层”的使用方法。 五、覆盖层进阶交互&#xff1a;从 “能绘制” 到 “好操作”​ 基础的绘制功能只能满足 “看得见” 的需求&#xff0c;实际开发中还需要 “能操作”—— 比如选中线条修改颜色、按 Delete 键删除线条、鼠标 hover 时高亮…

神经网络构成框架-理论学习

一、神经网络的基本组成与分类 1.1 神经网络的核心组成部分 神经网络是现代人工智能的基石&#xff0c;其设计灵感来源于生物神经系统的信息处理方式。作为工程师&#xff0c;了解神经网络的基本组成部分对于构建和优化模型至关重要。一个典型的神经网络主要由以下几个关键部分…

从0开始开发app(AI助手版)-架构及环境搭建

架构选择 前端React Native 后端Firebase 原因 环境准备 安装node 安装JDK 命令行工具&#xff1a;Node.js command prompt命令行查询Javav版本&#xff1a;javac -version使用nrm工具切换淘宝源&#xff1a;npx nrm use taobao安装yarn&#xff0c;替代npm下载工具&#x…

【性能测试】Jmeter工具快速上手-搭建压力测试脚本

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【Java】登神长阶 史诗般的Java成神之路 概念 性能测试是软件测试的重要分支&#xff0c;核心目标是通过模拟真实业务场景和负载压力&#xff0c;评估系统在不同条件下的性能表现&#xff0c;发现系统性…

oracle里的int类型

oracle里的int类型 在 ANSI SQL 标准 中&#xff0c;INTEGER 和 SMALLINT 是定义好的精确数值类型&#xff0c;但它们的 “长度”或“大小”并不是通过 (N) 括号来指定的&#xff08;如 INT(4)&#xff09;&#xff0c;这一点与 MySQL 等数据库的非标准扩展完全不同。 SMALLI…

前端学习之后端java小白(二)-sql约束/建表

一、约束SQL约束&#xff08;Constraints&#xff09;是用于限制表中数据的规则&#xff0c;确保数据的完整性和准确性。以下是主要的SQL约束类型&#xff1a; 主要约束类型&#xff1a; 1. NOT NULL 约束: 确保列不能包含空值 CREATE TABLE users (id INT NOT NULL,name VARC…

OpenCV:图像金字塔

文章目录一、什么是图像金字塔&#xff1f;二、图像金字塔的核心操作&#xff1a;采样与逆采样1. 向下采样&#xff08;pyrDown&#xff09;&#xff1a;从高分辨率到低分辨率步骤1&#xff1a;高斯滤波步骤2&#xff1a;删除偶数行与偶数列OpenCV实战代码效果特点2. 向上采样&…

LVS与Keepalived详解(一)负载均衡集群介绍

文章目录前言一、什么是LVS&#xff1f;二、四层&#xff08;L4&#xff09;负载均衡的最佳解决方案是什么&#xff1f;2.1解决方案分类与对比&#xff08;负载均衡的三种方式介绍&#xff09;2.1.1 硬件负载均衡 (Hardware Load Balancer)2.1.2 软件负载均衡 (Software Load B…