JVM(java虚拟机 详解三个主要的话题:1.JVM 中的内存区域划分2.JVM 的类加载机制3.JVM 中的垃圾回收算法)

news2024/11/13 9:40:53

jdk  java开发工具包

jre  java运行时环境

jvm java虚拟机

JDK、JRE、JVM之间的关系?
JDK(Java Development Kit):Java开发工具包,提供给Java程序员使用,包含了JRE,同时还包含了编译器javac与自带的调试工具Jconsole、jstack等。
JRE(Java Runtime Environment):Java运行时环境,包含了JVM,Java基础类库。是使用Java语言编写程序运行的所需环境。
JVM:Java虚拟机,运行Java代码

java语言的顺序:

1.使用记事本或者IDEA(集成开发环境)编写Java源程序
2.使用javac.exe编译器编译Java源程序,生成xxx.class的字节码文件 语法格式:javac xxx.java

3.使用java运行xxx.class字节码文件 语法格式:java xxx

因此, 我们编写和发布一个 java 程序,其实就只要发布 .class 文件即可jvm 拿到 .class 文件,就知道该如何转换.windows 上的 jvm 就可以把 .class 转成 windows 上能支持的可执行指令了linux 上的 jvm 就可以把 .class 转成 linux 上可以支持的可执行指令了!

三个主要的话题:

1.JVM 中的内存区域划分
2.JVM 的类加载机制
3.JVM 中的垃圾回收算法


JVM 中的内存区域划分

JVM其实也是进程

在任务管理器中可以看到

进程运行的前期,需要向系统申请一些资源(内存就是其中典型的资源)

这些内存就支撑了后续的java程序的执行.

比如:在java中定义变量所消耗的内存,就是jvm从系统中申请的内存.

jvm从系统中要了一些内存之后,又会根据实际的内存来分批不同的空间来存放不同的数据.(这个就是区域划分).

此处谈到的堆和栈和数据结构中谈到的堆和栈是不同的.

堆:

代码中new出来的对象都是在这里,对象中持有的非静态变量也在这里.

栈:

1.本地方法栈:jvm内部通过c++写的底层逻辑,调用关系和局部变量(一般不会谈到这,说到栈都是说的是栈的虚拟机栈)

2.虚拟机栈:记录了java中方法的调用关系,java代码的局部变量.

每个线程都有自己的栈和计数器

程序计数器:

这个区域比较小的空间,专门存储下一条java指令要执行的地址.(每个线程都有自己的程序计数器和栈)

元数据区(以前java中叫做方法区,从1.8开始改名字):

指的是一些有辅助性质的,描述性质的属性.

咱们写的 java 代码, if, while, for, 各种逻辑运算..这些操作最终都会被转换成 java 字节码
(javac 就会完成上述代码=>字节码)
此时这些字节码在程序运行的时候就会被jvm 加载到内存中放到 元数据区(方法区) 里头
此时,当前程序要如何执行,要做哪些事情,就会按照上述元数据区里记录的字节码依次执行了.

就类似硬盘一样


JVM的类加载机制

类加载大体的过程可以分成5 个步骤(也有资料上说是 3 个,这个情况就是把 2,3,4 合并成一个了)

1.加载

把硬盘上的.class文件找到,然后打开文件,然后读取文件内容.(认为读到的是二进制数据)

2.验证

需要验证当前内容是合法的,是.class(字节码文件)格式

3.给类对象申请空间

此处申请的内存空间.值都是默认0

4.解析

主要是针对字符串常量处理.

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

将符号偏移量转换为直接引用hello的地址就是解析

5.初始化

还要执行静态代码块的逻辑.还可能触发父类的加载.


加载环节(双亲委派模型):描述了如何查找.class的文件策略.

JVM 中进行类加载的操作,是有一个专门的模块,称为"类加载器"(ClassLoader)JM 中的类加载器默认是有 三个 的.(也可以自定义)

上述这三个类型存在父子关系.从下到上.

双亲委派模型工作过程:
1.从 ApplicationClassLoader 作为入口,先开始工作
2.ApplicationClassLoader 不会立即搜索自己负责的目录会把搜索的任务交给自己的父亲,
3.代码就进入到 ExtensionClassLoader 范畴了ExtensionClassLoader 也不会立即搜索自己负责的目录也要把搜索的任务交给自己的父亲
代码就进入到 BootstrapClassLoader 范時了BootstrapClassLoader 也不想立即搜索自己负责的目录也要把搜索的任务交给自己的父亲
5. BootstrapClassLoader 发现自己没有父亲才会真正搜索负责的目录(标准库目录)
通过全限定类名,尝试在标准库目录中找到符合要求的 .class 文件
如果找到了,接下来就直接进入到打开文件/读文件等流程中,如果没找到,回到孩子这一辈的类加载器中,继续尝试加载,
6.ExtensionClassLoader 收到父亲交回给他的任务之后,
自己进行搜索负责目录(扩展库的目录)
如果找到了,接下来进入到后续流程.
如果没找到,也是回到孩子这一辈的类加载器中继续尝试加载,
7.ApplicationClassLoader 收到父亲交回给他的任务之后自己进行搜索负责的目录(当前项目目录/第三方库目录)
如果找到了,接下来进入后续流程.
如果没找到,也是回到孩子这一辈的类加载器中继续尝试加载.由于默认情况下 ApplicationClassLoader 没有孩子了,此时说明类加载过程失败了!就会抛出 ClassNotFoundException 异常

JVM垃圾回收机制(GC)

c++就没有这样的机制,不是因为这个机制很难实现,而是因为这个东西会额外的付出系统开销.影响程序的执行性能.

java发展多年,可以把垃圾回收处理控制在1ms内.一个请求或者接受时间一般是几ms到几十ms.

如果一个引用没有任何指向,就说明所有的指向都结束了,就是荒废的引用.就可以进行回收了.

如果代码比较复杂,这样就解决不了了,就有了以下两中办法:

1.引用计数

这种方法没有在java中使用,但是在别的语言中广泛使用(比如python,PHP等等)

给每个对象安排一个小空间,用来记录当前对象有几个引用.

2.可达性分析(JVM用的是这个)

本质上是用"时间 换 空间”相比于引用计数,需要消耗更多的额外的时间, 但是总体来说,还是可控的.
不会产生类似于"循环引用"这样的问题.
在写代码的过程中,会定义很多的变量.
比如,栈上的局部变量/方法区中的静态类型的变量/常量池中引用的对象!....
就可以从这些变量作为起点,出发,尝试去进行"遍历"所谓的遍历就是会沿着这些变量中持有的引用类型的成员,再进一步的往下进行访问...
所有能被访问到的对象,自然就不是垃圾了.剩下的遍历一圈也访问不到的对象,自然就是垃圾~~


识别出垃圾以后,还需要释放垃圾:

主要的释放方式有三种:

a.标记-清除(一般不会用这个方法,因为内存碎片问题,比较致命)

比如黑色的就是垃圾,此时我们直接释放,就会产生很多小的内存碎片,就会导致后续的内存申请失败

因为内存申请是一片连续的空间.

b.复制算法

c.标记-整理

类似于顺序表删除中间元素.(搬运)


分代回收(依据不同种类的对象,采取不同的方式)
引入概念, 对象的年龄,
JM 中有专门的线程负责周期性扫描/释放,一个对象,如果被线程扫描了一次,可达了(不是垃圾),年龄就 +1(初始年龄相当于是 0)
JVM 中就会根据对象年龄的差异,把整个堆内存分成两个大的部分新生代(年龄小的对象)/ 老年代(年龄大的对象)

1)当代码中 new 出一个新的对象,这个对象就是被创建在伊甸区的.伊甸区中就会有很多的对象
.个经验规律: 伊甸区中的对象,大部分是活不过第一轮 GC这些对象都是"朝生夕死”,生命周期非常短!!

2)第一轮 GC 扫描完成之后,少数伊甸区中幸存的对象, 就会通过复制算法, 拷贝到 生存区后续 GC 的扫描线程还会持续进行扫描,不仅要扫描伊甸区,也要扫描生存区的对象.生存区中的大部分对象也会在扫描中被标记为垃圾.少数存活的,就会继续使用复制算法,拷贝到另外一个生存区中!!只要这个对象能够在生存区中继续存活,就会被复制算法继续拷贝到另一半的生存区中.每次经历一轮 GC 的扫描,对象的年龄都会 +1

3)如果这个对象在生存区中,经过了若干轮 GC 仍然健在~~JVM 就会认为,这个对象生命周期大概率很长, 就把这个对象从生存区,拷贝到老年代~~


4)老年代的对象,当然也要被 GC 扫描,但是扫描的频次就会大大降低了
老年代的对象,要 G 早 G 了~~ 既然没 G 说明生命周期应该是很长的,频繁 GC 扫描意义也不大,白白浪费时间.不如放到老年代,降低扫描频率,


5)对象在老年代寿终正寝,此时 JM 就会按照标记整理的方式,释放内存!

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

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

相关文章

Pytorch的下载安装

本文为自己整理的Pytorch下载相关的内容笔记,以便日后查阅 一. 基本命令 1.查看conda版本 conda --version2.创建conda新环境 conda create –n 名称 python版本3.查看已经创建的conda环境 conda info --envs4.进入虚拟环境 conda activate 环境名称 为了避免…

Elasticsearch 开放 inference API 增加了对 OpenAI chat completions 的支持

作者:Tim Grein 我们很高兴地宣布在 Elasticsearch 中推出的最新创新:在 Elastic 的 inference API 中集成了 OpenAI Chat Completions 功能。这一新特性标志着我们在整合尖端人工智能能力至 Elasticsearch 的旅程中又迈出了一步,提供了生成类…

新时代电子包浆!安利一款 PDF 办公利器,瞬间扫描效果,2.2k star开源了【文末带私活源码】

在日常工作学习中,我们经常需要处理各种 PDF 文档。有时,我们需要将电子版文档转换成扫描版,以满足特定需求,例如模拟真实签字、增加文件可信度等。传统的扫描方法需要借助打印机和扫描仪,不仅操作繁琐,而且…

OpenHarmony网络协议通信—kcp

kcp 是一种 ARQ 协议,可解决在网络拥堵情况下 tcp 协议的网络速度慢的问题 下载安装 直接在 OpenHarmony-SIG 仓中搜索 kcp 并下载。 使用说明 准备一套完整的 OpenHarmony 3.1 Beta 代码 库代码存放路径:./third_party/kcp 修改添加依赖的编译脚本 在/develo…

牛客NC251 多数组第 K 小数【 中等 双指针 C++、Java、Go】

题目 题目链接: https://www.nowcoder.com/practice/41796daa4c7e4e5ab984b2c16c24a1de 思路 双指针参考答案C class Solution {public:/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可*** param arr1 int整…

【python】深度解剖!一文搞懂字符串常用功能

【python】深度解剖!一文搞懂字符串常用功能 【先赞后看养成习惯】求点赞+关注+收藏😀 目录 【python】深度解剖!一文搞懂字符串常用功能字符串的创建:字符串的格式化——占位符.format方法字符串的分割字符串的连接字符串的重复字符串的大小写转换字符串的判断字符串的…

STL分解

效果图 ref:时序数据分析的利器——STL分解 - 知乎 (zhihu.com)

linux18:进程等待

进程等待的必要性 1:子进程创建的目的是要完成父进程指派的某个任务,当子进程运行完毕退出时,父进程需要通过进程等待的方式,回收子进程资源,获取子进程退出信息(子进程有无异常?没有异常结果是…

Cpp_SDay02

空山新雨后,天气晚来春 文章目录 前言一、No File!!!二、preProcessing三、static的用意:四、signed number /unsigned number五、函数六、#pragma once总结 前言 看视频令人犯困, 一、No File&#xff01…

Vue3基本功能介绍

文章目录 Vue3组件中的模板结构可以没有根标签div组合式APIRefReactive函数回顾Vue2响应式Vue3实现响应式对比reactive和refSetup注意点计算属性与监听computedWatchWatchEffectVue3生命周期自定义hook函数toRef其他组合APIshallowReactiveshallowRefreadonly和shallowOnlyToRa…

SRIO系列-时钟逻辑与复位逻辑

一、前言 上一篇讲述了SRIO协议的基本概念,传输的HELLO帧格式、事务类型等,本篇说一下SRIO IP核的时钟关系。 基本的IP设置可以参考此篇文章:【高速接口-RapidIO】Xilinx SRIO IP 核详解-CSDN博客 二、时钟关系 PHY可以在两个时钟域上运行…

ubuntu下boa服务器编译运行

一.下载boa源码并解压 官网网站:BOA源码 点击箭头所指的位置即可下载 解压: tar -xvf boa-0.94.13.tar.gz 解压完成得到目录: 二.安装环境所缺依赖,否则编译会报错 sudo apt install bison sudo apt install flex 三.编译 1…

vue快速入门(三十五)组件通信-父传子

注释很详细&#xff0c;直接上代码 上一篇 新增内容 父组件传值子组件接收父组件传来的数据 源码 App.vue <template><div id"app"><!-- :item"item"为将item的值传递给MyTest组件 --><MyTest v-for"item in roles" :key&q…

【Linux开发 第七篇】权限

权限 Linux组权限修改权限 Linux组 在linux中的每个用户必须属于一个组&#xff0c;不能独立于组外 文件/目录 所有者 一般为文件的创建者&#xff0c;谁创建了该文件&#xff0c;就自然的成为了该文件的所有者 这一列即为文件的所有者 修改文件的所有者&#xff1a; chown…

【已解决】win10系统 Docker 提示Docker Engine stopped解决全过程记录

【已解决】win10系统 Docker 提示Docker Engine stopped解决全过程记录 一、检查服务是否开启 找到 【Docker Desktop Service】&#xff0c;然后&#xff0c;启动他&#xff1b; 你也可以直接设置为“自动” 找到服务&#xff0c;右键》属性》启动类型&#xff1a;自动》点击…

经典目标检测YOLOV1模型的训练及验证

1、前期准备 准备好目录结构、数据集和关于YOLOv1的基础认知 1.1 创建目录结构 自己创建项目目录结构&#xff0c;结构目录如下&#xff1a; network CNN Backbone 存放位置 weights 权重存放的位置 test_images 测试用的图…

Java多线程并发八股问题总结

目录 (1).Java中实现线程的方法(2).如何停止一个正在运行的线程(3).notify和notifyAll有什么区别(4).sleep()和wait()有什么区别(5).volatile是什么&#xff1f;可以保证有序性吗&#xff1f;(6).Thread类中的start()和run方法有什么区别&#xff1f;(7).为什么wait、notify、n…

面试经典150题——跳跃游戏 II

面试经典150题 day10 题目来源我的题解方法一 动态规划方法二 贪心 题目来源 力扣每日一题&#xff1b;题序&#xff1a;45 我的题解 方法一 动态规划 动态规划&#xff0c;当j位置可达i位置时&#xff1a;dp[i]Math.min(dp[i],dp[j]1); 时间复杂度&#xff1a;O( n 2 n^2 n…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 4月21日,星期日

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年4月21日 星期日 农历三月十三 1、 商务部等10部门联合发文&#xff1a;进一步支持境外机构投资境内科技型企业。 2、 第二艘国产大型邮轮下坞搭载建造&#xff0c;预计2026年底前交付&#xff0c;中国邮轮开启批量化建造…

Python-VBA函数之旅-hash函数

目录 一、hash函数的定义&#xff1a; 二、hash函数的工作方式&#xff1a; ​三、hash函数的优缺点&#xff1a; 四、hash函数的常见应用场景&#xff1a; 1、hash函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&…