JVM的基本知识

news2024/10/3 2:25:46

JVM

JVM是java的虚拟机,是一个十分复杂的东西,所以掌握的要求比较高.本文主要是研究JVM的三大话题

  1. JVM内存划分
  2. JVM类加载
  3. JVM的垃圾回收

JVM内存划分

java程序要执行的时候,JVM会先申请一块空间,这里就涉及到JVM的内存划分

  • 堆 : 放的是new 出来的对象
  • 栈: 放的是方法之间的调用关系(栈中可以分为虚拟机栈和本地方法栈)
    1. 虚拟机栈: java中用来保存方法调用关系的内存空间
    2. 本地方法栈: jvm中C++写的代码,算是本地的方法的调用关系的内存空间
  • 方法区: 放的是类对象(加载好的类)
  • 程序计数器: 放的是下一个要执行的指令的地址

以上的内存划分都是在java1.7的时候

代码中的局部变量: 栈上

代码中的成员变量: 堆上

代码中的静态变量: 方法区

一个JVM进程中,堆和方法区都是只有一份的,栈和程序计数器每个线程都有自己的一份

常常会结合代码来判断内存划分

image-20230225142549220

t是局部变量,所以在栈上

x是成员变量,在堆上

y是静态变量,是在类对象中,就是在方法区中

一个误区: t是一个引用类型,是不是在堆上?其实不是的!,变量在哪个部分,主要就是看变量是什么变量(局部 成员 静态)

image-20230225143116038

这里的t2 是静态的,所以实在方法区中,new 后面的 Test2是在堆上的, xx是成员变量,所以是在堆上的

JVM的类加载

类加载是什么的

java程序在 运行之前需要先编译,也就是.java --> .class二进制字节码文件

在运行的时候,JVM会读取对应的.class文件,并解析内容,在内存在构造对象并进行初始化

简单来说,类加载是将类从文件中加载到内存中

类对象描述了这个类是什么样子的,有哪些属性(属性的名字 类型 访问权限)

有哪些方法(方法名字 参数个数 类型 返回值 访问权限)

继承自那个父类,实现了哪些接口

类对象是创建实例的具体依据

类加载的步骤

类加载大致能分成3个步骤

  1. 加载
  2. 验证
  3. 准备
  4. 解析
  5. 初始化

加载: 找到.class文件,读取文件内容,并按照.class规范来解析

验证: 检查当前的.class里的内容格式是否符合要求

准备: 给类里的静态变量分配内存空间

解析: 初始化字符串常量,将符号引用替换成直接引用

所谓的符号应用就是占位符,直接引用就是内存地址

在.class文件中,会包含字符串常量,但是在类加载之前,字符串常量没有分配内存空间,得类加载之后才会有内存空间,没有内存空间也就没有字符创常量的真实地址,所以只能先使用一个占位符,等分配好内存之后再替换之前的占位符

初始化: 针对类进行初始化,初始化静态成员,并加载父类

何时触发一个类加载

使用到一个类的时候就会触发类加载(并不是程序一启动就会进行类加载,而是在使用的时候才会进行类加载) [类似于懒汉模式]

具体什么情况是使用呢?

  1. 创建这个类的实例
  2. 使用了这个类的静态属性或者静态方法
  3. 使用了类的子类(加载子类就会触发加载父类)

双亲委派模型

JVM加载类是由 类加载器 (class loader)来负责的

JVM自带了多个 类加载器的

Bootstrap ClassLoader

Extension ClassLoader

Application ClassLoader

这个三个类加载器负责不同的模块

Bootstrap ClassLoader 负责加载标准库中的类

Extension ClaaLoader 负责加载JVM拓展的库的类

Application ClassLoader 负责加载我们自己项目中的自定义类

描述上述三个类加载器如何相互配合的工作工程,就是双亲委派模型

  1. 上述的三个类加载器存在父子关系,其中Application ClassLoader是最小的子类
  2. 进行类加载时,输入的内容要是全限定类名(写完整的类名),比如: java.lang.Thread
  3. 加载的时候先从最小的子类Application ClassLoader开始,但是类加载器不会立刻扫描自己负责的路径,而是将任务委派给父 "类加载器"来处理

image-20230225162157597

image-20230225163324892

垃圾回收机制GC

GC是什么

在学习C语言的时候,创建内存有两种方式

直接定义变量,变量对应着内存空间,一旦出了作用域,就会释放

还有一种是malloc申请内存(动态内存申请),务必需要通过free来释放资源,要是真的忘了就会导致内存泄漏,十分危险

所以手动释放内存是很容易出事的

GC(垃圾回收机制)是一种主流的解决方案,在Java Python JS Go PHP中都存在GC垃圾回收机制

所谓的GC: 程序员只要负责申请内存,释放内存的工作直接交给JVM完成就行了

虽然GC很好用,但是GC也有问题,其中最大的问题就是GC会引入额外的开销(时间 + 空间)

时间上会存在STW问题(Stop The World),会导致卡顿

空间上会消耗额外的CPU和内存资源

GC回收哪部分

JVM的几个部分:

方法区: 类对象, 加载之后不太会卸载

栈: 出了作用域就会释放,所以不用回收

程序计数器: 固定的内存空间,不必回收

堆: GC主要的回收对象

image-20230225193453512

GC是怎么执行的

GC其实主要就是两步骤

  1. 先找出垃圾(看看谁是垃圾)
  2. 在回收垃圾(释放内存)

判定对象是不是垃圾

如果一个对象再也不会使用了,就算是垃圾了

在Java中,对象的使用,需要借助引用,要是一个对象,已经没有任何引用指向它了,说明了这个对象再也无法被使用了,就算是垃圾了

所以,判断是不是垃圾的最重要的就是,存在引用说明对象不是垃圾,要是引用不存在了就说明是垃圾

判断对象是否存在引用的两种方法是引用计数(不是JVM采用的方法,但是Python和PHP使用) 和 可达性分析(JVM中使用的方法)

所以要注意审题 : 确实问的是Java的垃圾回收机制还是"垃圾回收的机制",这里的两种方法是用来判断是不是垃圾

引用计数

给每个对象加上一个计数器,这个计数器表示当前的对象有几个引用

每当多一个引用指向该对象,计时器+1

每当少一个引用指向该对象,计数器-1(比如引用是一个局部变量,出了作用域 或者引用是一个成员变量,所在的对象被销毁了)

当计数器变成0的时候,说明此时已经没有引用指向这个对象了,所以就可以释放内存了

引用计数的优点: 简单 容易实现 执行效率也比较高

应用计数的缺点:

  1. 空间利用率地,尤其是对于小对象而言. 要是一个小对象中只有一个int的成员,结果还有拿出一个int的空间给计数器
  2. 可能会出现循环引用的情况

image-20230225200445408

这两个对象实现了循环相互调用,这样子最后计数器就是1了,但是也没有引用能指向它们,所以就不能被释放

可行性分析[JVM采用的方法]

约定一些特定的变量来作为GC roots

每个一段时间,从GC roots出发,进行遍历,看看哪些变量能被访问到,能访问到的变量就算是"可达"

这里的GC roots可以是栈上的变量 / 常量池引用的对象 / 方法区,引用类型的静态变量

image-20230225201246133

在找到 垃圾之后该怎么回收垃圾?

具体回收垃圾有4中方法

  • 标记清除
  • 复制算法
  • 标记整理
  • 分代回收

标记清除

image-20230225202546689

在发现哪些是垃圾之后,键对象对应的内存空间释放

简单粗暴,但是有一个最大的问题,就是会导致产生内存碎片,加上上图中的深色的垃圾每个占1KB,清除完之后我想要申请一个2KB的的空间都申请不到,因为此时内存都是碎片的,没有连在一起的空间

复制算法

image-20230225204242741复制算法虽然能解决内存碎片的问题,但是缺点也是很明显的

复制算法的缺点:

  • 空间利用率更低了(每次都是只用一半的内存)
  • 一轮GC下来,万一大部分对象都是要保留的,只有少部分的对象要回收,这个时候复制的开销就会很大

标记整理

保证整理的方法类似于顺序表 删除/覆盖元素 主要是搬运操作

image-20230225205202666

标记整理的空间利用率提高了,也能解决内存碎片的问题,但是搬运操作也是比较耗时的

分代回收

分代回收将上面的复制算法和标记整理综合了一下,根据 对象的不同特点来采取不同的回收方式,这里的对象特点主要是指对象的年龄

对象的年龄是根据GC的轮次来的

GC 就是一组线程,周期性扫描代码中的所有对象,要是一个对象经历了一次GC,没有被回收,它的年龄就要+1

一个基本的经验规律:

如果一个对象的寿命比较长,大概率就还会活的根据(要死早就死了,能活下来,说明生命力还比较旺盛)

针对以上的经验,将对象分成新生代(minor GC)(GC扫描的评率更高)和老年代(full GC / major GC)(GC扫描的频率更高)

image-20230225212354849

新生代(minor GC): 刚刚被创建出来的新对象,往往很容易朝生夕死,很多对象都熬不过一轮GC

新对象会进入到伊甸区,要是新对象能坚持过一轮GC, 没挂,就会通过复制算法,复制到生存区

进入到生存区之后,每熬过一次GC,就会通过复制算法拷贝到另一个拷贝去,要是这个对象能一直不消亡 , 就会在两个生存区栈反复拷贝,每次GC都会筛选掉很多的对象

要是一个对象能在生存区中坚持了很多轮GC,还不挂,则进入到老年代(full GC / major GC )

当对象来到老年代,GC也还是会有的,只是频率低很多,这里每轮GC使用的是标记整理的方式来处理老年代对象

分代回收的过程,非常像找工作的情况

总结一下:

  1. 判断垃圾
    • 引用计数
    • 可达性分析
  2. 进行回收
    • 标记清除–>内存碎片
    • 复制算法–>浪费空间大
    • 标记整理–>类似于顺序表搬运元素,时间比较长
    • 分代回收–>因地制宜完成回收

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

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

相关文章

rabbitmq集群-镜像模式

上文参考: rabbitmq集群-普通模式 1. 什么是镜像模式 它和普通集群最大的区别在于 Queue 数据和原数据不再是单独存储在一台机器上,而是同时存储在多台机器上。也就是说每个 RabbitMQ 实例都有一份镜像数据(副本数据)。每次写入…

3月8号作业

题目:题目一:vmlinux可执行文件如何产生题目二:整理内核编译流程:uImage,zImage,Image,vmlinux之间的关系答案一:在内核源码目录下vi Makefile,搜索vmlinux目标,vmlinux: scripts/li…

MongoDB学习(java版)

MongoDB概述 结构化数据库 ​ 结构化数据库是一种使用结构化查询语言(SQL)进行管理和操作的数据库,它们的数据存储方式是基于表格和列的。结构化数据库要求数据预先定义数据模式和结构,然后才能存储和查询数据。结构化数据库通常…

Android Camera SDK NDK NDK_vendor介绍

Android Camera JNI NDK NDK_vendor介绍前言主要有哪几种interface?Android SDKCamera API 1Camera API 2小结Android NDKNDK InterfaceNDK Vendor Interface小结Camera VTS Testcase总结Reference前言 本篇博客是想介绍Android camera从application layer到camera…

谷歌插件Fetch在不同页面之间Cookie携带情况详解

content script 和 script inject 表现情况 在碰到content script 注入和用script标签注入一样&#xff0c;即使服务端有写入Cookie到域名下在该tab标签应用下也不会被保存&#xff0c;所以在发送时也无法自动携带&#xff0c;所以通过content script和<script>这种方式…

微信小程序第二节 —— 自定义组件。

&#x1f449;微信小程序第一节 —— 自定义顶部、底部导航栏以及获取胶囊体位置信息。 一、前言 &#x1f4d6;&#x1f4d6;&#x1f4d6;书接上回 &#xff0c;dai ga hou啊&#xff01;我是 &#x1f618;&#x1f618;&#x1f618;是江迪呀。在进行微信小程序开发中&am…

多维数组的地址,通过指针引用多维数组详解

通过指针引用一维数组可以参考这篇文章&#xff1a; 通过指针引用数组的几种方法的原理和差异&#xff1b;以及利用指针引用数组元素的技巧_juechen333的博客-CSDN博客一个数组包含若干元素&#xff0c;每个数组元素都占用存储单元&#xff0c;所以他们都有相应的地址&#xf…

《Ansible模块篇:debug模块详解》

一、简介 平时我们在使用ansible执行playbook时&#xff0c;经常可能会遇到一些错误&#xff0c;有的时候不知道问题在哪里 &#xff0c;这个时候可以使用-vvv参数打印出来详细信息&#xff0c;不过很多时候-vvv参数里很多东西并不是我们想要的&#xff0c;这时候就可以使用官方…

python第四天作业~函数练习

目录 作业4、判断以下哪些不能作为标识符 A、a B、&#xffe5;a C、_12 D、$a12 E、false F、False 作业5&#xff1a; 输入数&#xff0c;判断这个数是否是质数&#xff08;要求使用函数 for循环&#xff09; 作业6&#xff1a;求50~150之间的质数是…

ReentrantLock 源码解读

一、ReentrantLock ReentrantLock 是 java JUC 中的一个可重入锁&#xff0c;在上篇文章讲解 AQS 源码的时候提到 ReentrantLock 锁是基于 AQS 实现的&#xff0c;那是如何使用的 AQS 呢&#xff0c;本篇文章一起带大家看下 ReentrantLock 的源码。 在 AQS 中&#xff0c;如果…

linux安装influxdb-rpmyum方式

一、influxdb的安装InfluxDB简介时序数据库InfluxDB版是一款专门处理高写入和查询负载的时序数据库&#xff0c;用于存储大规模的时序数据并进行实时分析&#xff0c;包括来自DevOps监控、应用指标和IoT传感器上的数据主要特点&#xff1a;专为时间序列数据量身订造高性能数据存…

uniapp——ucharts的使用

ucharts是一个类似于echarts的图表框架 引入 在Hbuilder的插件下载仓库中搜索ucharts &#xff01;&#xff01;值得一提的是&#xff0c;Hbuilder的版本必须是大于3.1.0的&#xff01;&#xff01; 这样就导入到了项目的uni_modules里了 ucharts下载完成后&#xff0c;可以…

Java——异常机制

前言 随着对java的不断深入学习&#xff0c;在对语法以及编程思想有了一定的了解之后&#xff0c;在编程的过程中有可能会因为用户的输入不正确或者逻辑错误而出现异常或者错误&#xff0c;因此如何去捕捉与避免不应该出现的异常或者错误就变得十分重要。本文就介绍了java的异…

C++实现的二叉树创建和遍历,超入门邻家小女也懂了

目录 二叉树 特点 性质 二叉树的创建 声明 创建 -> 成员运算符 批量创建 二叉树的遍历 先序遍历 中序遍历 后序遍历 层序遍历 树的相关术语 特殊二叉树 满二叉树 完全二叉树 二叉树 树&#xff08;Tree&#xff09;是n(n≥0)个节点的有限集。在任意一棵…

Idea工具单工程使用卡顿设置

一、开启Idea内存占比指示 1.1、勾选Idea 内存指示器 1.2、右下角展示当前项目&#xff1a;内存/总内存&#xff08;726/1024MB&#xff09; ​​​​​​​ 二、修改Idea自定义内存 2.1、打开help - Edit Custom VM Options 2.2、自定义虚拟内存设置 -Xms1024m // 初始内存分…

【前端八股文】浏览器系列:浏览器渲染、前端路由、前端缓存(HTTP缓存)、缓存存储(HTTP缓存存储、本地存储)

文章目录渲染步骤DOM树与render树回流与重绘前端路由hash模式history模式两种模式对比前端缓存HTTP缓存强缓存协商缓存一般哪些文件对应哪些缓存HTTP缓存总结缓存存储HTTP缓存存储本地存储参考本系列目录&#xff1a;【前端八股文】目录总结 是以《代码随想录》八股文为主的笔记…

TTS | 语音合成论文概述

综述系列2021_A Survey on Neural Speech Synthesis论文&#xff1a;2106.15561.pdf (arxiv.org)论文从两个方面对神经语音合成领域的发展现状进行了梳理总结&#xff08;逻辑框架如图1所示&#xff09;&#xff1a;核心模块&#xff1a;分别从文本分析&#xff08;textanalysi…

【Linux内核一】在Linux系统下网口数据收发包的具体流向是什么?

在TCP/IP网络分层模型里&#xff0c;整个协议栈被分成了物理层、链路层、网络层&#xff0c;传输层和应用层。物理层对应的是网卡和网线&#xff0c;应用层对应的是我们常见的Nginx&#xff0c;FTP等等各种应用。Linux实现的是链路层、网络层和传输层这三层。 在Linux内核实现中…

Linux(Centos)安装Minio集群

目录1&#xff1a;简介2&#xff1a;功能与集成3&#xff1a;架构4&#xff1a;搭建集群4.1&#xff1a;挂载磁盘4.1.1&#xff1a;要求4.1.2&#xff1a;创建挂载目录4.1.3&#xff1a;注意&#xff1a;需要将新建的目录挂在到对应的磁盘下,磁盘不挂载好&#xff0c;集群启动会…

Tomcat 并发达太大导致系统崩溃解决方案

当 Tomcat 并发达太大导致系统崩溃时&#xff0c;可以通过以下几个步骤来解决这个问题&#xff1a; 1、分析原因 首先需要分析系统崩溃的原因&#xff0c;是因为Tomcat的性能瓶颈还是因为代码的Bug&#xff0c;或者是系统资源不足等等。 2、优化代码 如果是代码的问题&…