JVM相关知识

news2024/12/24 10:10:57

JVM

  • 类加载过程
  • 类什么时候被加载
  • 什么情况下会发生栈内存溢出
  • JVM内存模型
  • 常量池
  • 回收方法区
  • 垃圾回收流程
  • 圾收集算法
      • 分代收集理论
      • 标记-清除算法
      • 标记-复制算法
      • 标记-整理算法

类加载过程

加载–验证–准备–解析–初始化–使用–卸载

加载:通过全类名获取类的二进制流,然后解析成静态数据结构存储在方法区,并在堆中生成一个便于用户调用的java.lang.Class类型的对象

验证:验证class文件的格式是够正确 。不会威胁到JVM安全

准备为类的静态变量分配内存,并初始化值

解析:将类,接口、字段和方法符号引用转为直接引用;假设一个类A被编译成class文件,并且A引用了B,在编译阶段,A就需要一个字符串去记录B的地址,字符串就叫符号引用,在运行时,A类被加载,但是B未被加载,那么将加载B,此时A的符号引用会被替换成B的实际地址

初始化:初始化就是类加载的最后一个阶段了。准备阶段变量可能已经被赋过初值了,初始化的时候会根据程序代码的要求来对类里的一些变量和数据进行操作。也可以说就是在调用<clinit()>方法

类什么时候被加载

  • 创建类的实例,也就是new一个对象
  • 访问某个类或接口的静态变量,或者对该静态变量赋值
    • (注:类中的static final 修饰的基本类型及String类型成员变量,被访问是不会触发类加载的;静态内部类也是被访问不会触发类加载)
  • 调用类的静态方法
  • 反射
  • 初始化一个类的子类(会首先初始化子类的父类)
  • JVM启动时标明的启动类,即文件名和类名相同的那个类

手动触发类加载 class.forname

如果两个线程同时class.forname,只会加载一次,第二个会阻塞,第二个会,他也就跟着返回的了

什么情况下会发生栈内存溢出

线程请求的栈深度超过了虚拟机允许的最大深度时,会抛出StackOverFlowError异常,方法递归调用可能会出现该问题;调整参数-xss去调整jvm栈的大小

JVM内存模型

思路: 给面试官画一下JVM内存模型图,并描述每个模块的定义,作用,以及可能会存在的问题,如栈溢出等。在这里插入图片描述
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。

这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。

创建对象会放在堆里面.

信息、类信息开始进入方法区,程序执行进入栈当中,在栈当中有new这些操作,引出堆的调用,栈的运行由程序计数器配合计时进行实现,需要操作系统执行的话就需要依赖本地方法栈翻译成计算机可以执行的语言,来进行操作。

线程共享:

  • 堆—存实例化的对象
  • 方法区—主要存放的是class(类信息)

线程独享:

  • 程序计数器—存下一行要执行的行号
  • 虚拟机栈—存栈帧(局部变量表)
  • 本地方法栈—调用本地方法的时候存的数据

1、程序计数器:

  • 线程私有,线程间会切换,为了让线程切换回来指定到准确的位置,需要有独立的程序计数器,记录这个线程执行到了哪里,下一行改执行哪个指令。
  • 程序计算器是唯一不会发生内存溢出的地方。如果正在执行的是Native 方法,由于不是JVM执行,则这个计数器值为空。

2、Java虚拟机栈:

  • 线程私有,每个方法执行的时候,在虚拟机里面同步创建一个栈帧,存储局部变量表、操作数栈、动态连接、方法出口等信息。方法从被调用到执行完毕就对应着一个栈帧。

  • 【编译的时候就能知道方法需要多少位空间,如果是字符串型(不确定位数),会存一个指向,这个指向占固定位数】

3、本地方法栈:

  • 和上面类似(java底层封装了c语言,操作系统不认识,需要翻译成本地的c语言)【将自己本身封装的方法,重新翻译成操作系统可识别的方法(C和汇编),本地方法栈和操作系统进行沟通。】
  • 本地方法栈是操作系统本地的方法
  • 本地方法栈是线程私有的,java虚拟机调用本地方法时,需要给本地方法提供的内存空间。存的是native信息,当一个JVM创建的线程调用native方法后,jvm不会在虚拟机栈中为该线程创建栈帧,而是简单的动态链接直接调用该方法。
    • 堆中的方法能不能被gc回收(可以)
    • 方法区中的东西可以被gc回收嘛(可以,类型卸载)
    • 栈中的东西不需要回收,方法执行完栈就销毁了。

4、Java堆:

  • **线程共享,**存对象实例(所有的对象实例以及数组都应当在堆上分配)(但是其中仍然有线程私有的区域,称为TLAB,它可以提高对象分配的效率)
  • Java堆是垃圾收集器管理的内存区域
  • 从分配内存的角度看,所有线程共享的Java堆中可以划分出多个线程私有的分配缓冲区
  • 将Java堆细分的目的只是为了更好地回收内存,或者更快地分配内存
  • 对于**基本数据类型对象,**在方法体内声明时,会直接分配在栈中,其它情况都会分配在堆中。
  • 对于**普通对象来说,**JVM 会首先在堆上创建对象,然后在其他地方使用它的引用。

5、方法区:

  • 线程共享,存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。
  • 在JDK 6的时候HotSpot开发团队就有放弃永久代,逐步改为采用本地内存来实现方法区的计划。
  • 到了JDK 7的HotSpot,已经把原本放在永久代的字符串常量池、静态变量等移出。
  • JDK 8,终于完全废弃了永久代的概念

6、运行时常量池:

  • 运行时常量池是方法区的一部分

7、直接内存:

  • 物理上内存条
  • JVM占的内存是我们自己写的C和汇编所占据的内存,不属于JVM数据区一部分,属于本机运行内存。执行JVM的时候还需要调用操作系统内核
  • 提供的方法,操作系统也需要消耗内存,这时就用到了直接内存,一般程序都消耗直接内存
  • 知识扩展:操作系统调取内核提供的方法,内核连接驱动,驱动硬件只识别汇编语言,硬件中加了存储功能有一套翻译,支持C语言的识别

常量池

JVM常量池主要分为Class文件常量池、运行时常量池,全局字符串常量池,以及基本类型包装类
对象常量池。

Class文件常量池。在java代码的编译期间, 我们编写的java文件就被编译为.class文件格式的二进制数据存放在磁盘中,其中就包括class文件常量池。
●**运行时常量池:**方法区内

字符串常量池:在HotSpot VM中,它是一个叫做StringTable的全局表。string”或通常所说的"进入了字符串常量池的字符串”。存放在堆中

基本类型包装类对象常量池: 这些类是Byte, Short,Integer,Long,Character,Boolean,。另外上面这5种整型的包装类也只是在对应值小于等于

127时才可使用对象池,也即对象不负责创建和管理大于127的这些类的对象。

回收方法区

方法区又称元空间或者永久代(HotSpot虚拟机中老版本的叫法)
主要回收内容

  • 废弃的常量

    已经没有任何字符串对象引用常量池中的“java”常量,且虚拟机中也没有其他地方引用这个字面量,如果在这时发生内存回收,而且垃圾收集器判断确有必要的话,这个“java”常量就将会被系统清理出常量池

  • 不再使用的类

    判定一个类型是否属于“不再被使用的类”的三个条件:

    1.该类所有的实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例。

    2.加载该类的类加载器已经被回收

    3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

垃圾回收流程

当一个对象被实例化后,首先先尝试存在eden区,若Eden区存不下,将触发一次minor GC,若空间不够,则看存活区的空间是否够,要是够的话,将Eden区的存活时间长的对象放到存活区,将新创建的对象分配到Eden区,要是不够的话,看老年代的空间是否够,要是够的话,将存活区的存活时间比较长的对象存到老年代,然后将Eden区的存活时间比较长的存到存活区,将新创建的分配搭到Eden区。若空间仍不够,触发一次full GC,重复操作,若最后空间仍不足,抛出异常。

圾收集算法

垃圾收集灵魂三问

  1. 判定哪些对象是垃圾
  2. 什么时候回收
  3. 怎么进行回收

分代收集理论

当前的垃圾收集器,大多数都遵循了“分代收集”的理论进行设计分代收集理论,实质是一套符合大多数程序运行实际情况的经验法则,建立在两个分代假说之上:

1)弱分代假说:绝大多数对象都是朝生夕灭的。(不用分代,全部死亡)

2)强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡。(活时间越长的越难死)

上面两个假说奠定了一个原则:收集器应该将Java堆划分出不同的区域,然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。

一般把Java堆划分为新生代老年代。

在新生代中,每次垃圾收集时都发现有大批对象死去,而每次回收后存活的少量对象,将会逐步晋升到老年代中存放。

一个问题:想将新生代的对象回收,但是这个对象可能是老年代的对象指向(引用)的它,为了找出该区域中的存活对象,不得不在固定的GC Roots之外,再额外遍历整个老年代中所有对象来确保可达性分析结果的正确性,反之也要把新生代全部遍历一遍。

—>会产生很大的性能负担,因此产生了跨代引用假说

3)跨代引用假说:跨代引用相对于同代引用来说仅占极少数。

  • 依据这条假说,我们就不应再为了少量的跨代引用去扫描整个老年代,也不必浪费空间专门记录 每一个对象是否存在及存在哪些跨代引用;
  • 只需在新生代上建立一个全局的数据结构(该结构被称 为“记忆集”),这个结构把老年代划分成若干小块,标识出老年代的哪一块内存会 存在跨代引用。
  • 此后当发生Minor GC时,只有包含了跨代引用的小块内存里的对象才会被加入到GC Roots进行扫描。

统一定义:

  • 部分收集:指目标不是完整收集整个Java堆的垃圾收集,其中又分为:

    • 新生代收集:指目标只是新生代的垃圾收集。
    • 老年代收集:指目标只是老年代的垃圾收集。目前只有CMS收集器会有单独收集老年代的行为。
    • 混合收集:指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收集器会有这种行为。
  • 整堆收集:收集整个Java堆和方法区的垃圾收集。

标记-清除算法

标记出哪些需要回收或哪些不需要回收,按照标记进行回收。(这是比较基础的算法,其他的垃圾回收算法是以它为基础改进的)

标记-清除算法缺点:

  • 1,是执行效率不稳定
  • 2,是内存空间的碎片化问题

标记-复制算法

为了解决标记-清除算法面对大量可回收对象时执行效率低的问题,提出了一种称为“半区复制”的垃圾收集算法—将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。两边循环往复。

标记-复制算法优点:

  • 实现简单,运行高效;

标记-复制算法缺点:

  • 可用内存缩小为了原来的一半;
  • 存活的对象比较多的时候,来回复制对象导致效率很低

标记-复制算法的优化:

Appel式回收—》

Appel式回收的具体做法是把新生代分为一块较大的Eden空间和两块较小的 Survivor空间,每次分配内存,先占用Eden空间,垃圾收集的时候,Eden存活的对象放入到Survivor1当中,清理掉Eden中的对象,Survivor1当中存活的放入到Survivor2当中,清理掉Survivor1当中的对象,Survivor2满了之后,将存活的放入到Eden中,清除Survivor2中所有的对象,永远Eden区域优先存储对象。以此类推,两块满了之后,回收的进入另外一块。

进一步优化——》

如果Eden区域存活下来的对象比较多,那么会出现两个Survived区域装不下的情况,所以需要依赖其他内存区域(实 际上大多就是老年代)进行分配担保。这些对象将通过分配担保机制直 接进入老年代

标记-复制算法缺点:在对象存活率较高时就要进行较多的复制操作,效率将会降低;如果 不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存 活的极端情况。—>老年代里面不能用这样的算法

【空间分配担保:新生代与老年代的占比一般是1:2;在新生代中,假如Eden区域30%的数据存活了下来,这样两个Survivor区域放不下这30%的对象,就将这三成数据直接放入老年代,叫做空间分配担保】

标记-整理算法

标记过程一样,后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。

标记整理算法是需要移动对象的,移动对象是相对来说比较消耗时间的。

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

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

相关文章

【C++】非类型的模板参数,特化

目录 1.类型模板参数和非类型模板参数 2.特化 3. 模板的分离编译 4.模板的优缺点 1.类型模板参数和非类型模板参数 之前写模板传的都是类型——类型模板参数 现在想定义两个静态数组&#xff0c;数组长度不同&#xff0c;就可以用模板参数传数值而不是传类型 非类型模板…

Docker与微服务实战2022

基础篇(零基小白)1.Docker简介1.1 是什么问题&#xff1a;为什么会有docker出现&#xff1f;您要如何确保应用能够在这些环境中运行和通过质量检测&#xff1f;并且在部署过程中不出现令人头疼的版本、配置问题&#xff0c;也无需重新编写代码和进行故障修复&#xff1f; 答案就…

Android源码分析 - View的触摸事件分发

0. 相关分享 Android源码分析 - InputManagerService与触摸事件 1. 接收Input系统发送来的事件 时序图源&#xff1a;稀土掘金 在注册Window的时候&#xff0c;来到ViewRootImpl&#xff0c;其中不仅发起窗口注册&#xff0c;还开启了输入事件的监听&#xff1a; //ViewRoo…

nuxt3使用总结

目录 背景 安装 项目配置 路由 Tailwindcss引入 全局样式配置 css预处理器 安装 Tailwindcss 项目的配置 部署上线 seo优化 背景 新入职了一家公司&#xff0c;刚进入公司第一个需求就是先做一个公司的官网&#xff0c;需要使用vue写&#xff0c;作为祖师爷的粉丝…

Java 电话号码的组合

电话号码的字母组合中等给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。示例 1&#xff1a;输入&#xff1a;digits "23…

案例学习--016 消息队列作用和意义

简介MQ全称为Message Queue, 是一种分布式应用程序的的通信方法&#xff0c;它是消费-生产者模型的一个典型的代表&#xff0c;producer往消息队列中不断写入消息&#xff0c;而另一端consumer则可以读取或者订阅队列中的消息。主要产品有&#xff1a;ActiveMQ、RocketMQ、Rabb…

【RV1126】RKMedia模块简介

文章目录简介源码与编译rkmedia log等级配置目录参考文档&#xff1a;【Rockchip RKMedia Development Guide】rkmedia的手册在sdk目录下/docs/RV1126_RV1109/Multimedia rkmedia的代码在sdk目录下/external/rkmedia rkmedia的demo在sdk目录下/external/rkmedia/examples&…

antlr4-maven-plugin简单学习

1. 序言 antlr4-maven-plugin的官方介绍为&#xff1a; The ANTLR 4 plugin for Maven can generate parsers for any number of grammars in your project.博客《 mac上的Antlr4环境搭建》&#xff0c;有介绍如何通过antlr4-maven-plugin实现.g4文件的编译 这里将介绍antlr4-…

弹性存储-对象存储OSS部分

对象存储介绍 对象存储&#xff08;object storage service&#xff0c;简称oss&#xff09;&#xff0c;具备与平台无关的rest api接口&#xff0c;可提供99.9999999999%&#xff08;12个9&#xff09;的数据持久性和99.995%的数据可用性。 OSS优势 功能介绍 存储空间bucke…

秒杀高并发解决方案

秒杀高并发解决方案 1.秒杀/高并发方案-介绍 秒杀/高并发 其实主要解决两个问题&#xff0c;一个是并发读&#xff0c;一个是并发写并发读的核心优化理念是尽量减少用户到 DB 来"读"数据&#xff0c;或者让他们读更少的数据, 并 发写的处理原则也一样针对秒杀系统需…

麒麟服务器V10 版本 安装 Anaconda教程,也就是安装Python环境的教程(亲测有效)

目录1 Anaconda 是什么2 安装1 Anaconda 是什么 你可以理解为一个软件&#xff0c;和QQ一样的软件&#xff0c;你安装之后&#xff0c;里面就有naconda包括Conda、Python以及一大堆安装好的工具包&#xff0c;比如&#xff1a;numpy、pandas等 1&#xff09;包含conda&#x…

【C++学习】类和对象(上)

前言&#xff1a; 由于之前电脑“嗝屁”了&#xff0c;导致这之前一直没有更新博客&#xff0c;今天才拿到电脑&#xff0c;在这里说声抱歉。接下来就进入今天的学习&#xff0c;在之前我们已经对【C】进行了初步的认识&#xff0c;有了之前的知识铺垫&#xff0c;今天我们将来…

初识BFC

初识BFC 先说如何开启BFC&#xff1a; 1.设置display属性&#xff1a;inline-block&#xff0c;flex&#xff0c;grid 2.设置定位属性&#xff1a;absolute&#xff0c;fixed 3.设置overflow属性&#xff1a;hidden&#xff0c;auto&#xff0c;scroll 4.设置浮动&#xf…

英雄算法学习路线

文章目录零、自我介绍一、关于拜师二、关于编程语言三、算法学习路线1、算法集训1&#xff09;九日集训2&#xff09;每月算法集训2、算法专栏3、算法总包四、英雄算法联盟1、英雄算法联盟是什么&#xff1f;2、如何加入英雄算法联盟&#xff1f;3、为何会有英雄算法联盟&#…

Linux系统安装mysql(rpm版)

目录 Linux系统安装mysql&#xff08;rpm版&#xff09; 1、检测当前系统中是否安装MySQL数据库 2、将mysql安装包上传到Linux并解压 3、按照顺序安装rpm软件包 4、启动mysql 5、设置开机自启 6、查看已启动的服务 7、查看临时密码 8、登录mysql&#xff0c;输入临时密…

C++ STL学习之【vector的使用】

✨个人主页&#xff1a; Yohifo &#x1f389;所属专栏&#xff1a; C修行之路 &#x1f38a;每篇一句&#xff1a; 图片来源 The power of imagination makes us infinite. 想象力的力量使我们无限。 文章目录&#x1f4d8;前言&#x1f4d8;正文1、默认成员函数1.1、默认构造…

STM32之SPI

SPISPI介绍SPI是串行外设接口(Serial Peripherallnterface)的缩写&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;并且在芯片的管脚上只占用四根线&#xff0c;节约了芯片的管脚&#xff0c;同时为PCB的布局上节省空间&#xff0c;提供方便…

蓝桥杯嵌入式(G4系列):定时器捕获

前言&#xff1a; 定时器的三大功能还剩下最后一个捕获&#xff0c;而这在蓝桥杯嵌入式开发板上也有555定时器可以作为信号发生器供定时器来测量。 原理图部分&#xff1a; 开发板上集成了两个555定时器&#xff0c;一个通过跳线帽跟PA15相连&#xff0c;最终接到了旋钮R40上&…

STM32F103CubeMX定时器

前言定时器作为最重要的内容之一&#xff0c;是每一位嵌入式软件工程师必备的能力。STM32F103的定时器是非常强大的。1&#xff0c;他可以用于精准定时&#xff0c;当成延时函数来使用。不过个人不建议这么使用&#xff0c;因为定时器很强大&#xff0c;这么搞太浪费了。如果想…

Zookeeper的Java API操作

Zookeeper的Java API操作一、先启动Zookeeper集群二、IDEA 环境搭建三、创建子节点四、获取子节点并监听节点变化五、判断 Znode 是否存在六、Watcher工作流程一、先启动Zookeeper集群 二、IDEA 环境搭建 1.创建一个Maven工程&#xff1a;ZookeeperProject 2.在pom.xml文件添…