JVM基础【Java】

JVM基础

JVM:Java Virtual Machine(Java虚拟机)

1.Java文件的执行流程

首先认识Java文件的运行规则
在这里插入图片描述

  • 对字节码文件进行解释成机器码,让计算机执行
  • 内存管理
    • 自动为对象、方法等分配内存
    • 自动垃圾回收机制,回收不再使用的对象
  • 即时编译
    • 实时解释:主要为了支持跨平台特性
      在这里插入图片描述
    • 导致的问题: 实时解释class指令,使得效率较低
      • 优化:
        在这里插入图片描述

对热点代码进行优化,提升执行效率


在这里插入图片描述
在这里插入图片描述


2.字节码文件

1.基本信息
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 这里一般采用第二种方法,避免其他代码产生兼容问题。
    **2.字节码的组成部分-方法
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3.类的生命周期

类的生命周期描述了一个类的加载、使用、卸载的过程。

  • 加载
    类加载器根据类的全限定名通过不同的渠道以二进制流的方式获取字节码信息,
    类加载器在加载完类之后,Java虚拟机会将字节码中的信息保存到内存的方法区中。

  • 连接

    • 验证:验证字节码内容是否满足规范

      • 文件格式验证,比如文件是否以0xcafebabe开头,以及主次版本号是否满足当前虚拟机版本要求
        在这里插入图片描述

      • 元信息验证,例如类必须有父类(super不能为空)

      • 验证程序执行指令的语义,比如方法内的指令执行到一半强行跳转到其他方法

      • 符号引用验证,例如是否访问了其他类中的private方法等

    • 准备:给静态变量(static)分配内存并设置初始值(初始值均为0,不会直接将代码中的值进行赋值,比如public static int x = 1,在准备阶段x的值会被赋值成0,在初始化阶段才被赋值成1)
      对于final修饰的基本数据类型的静态变量,则会直接将代码中的值进行赋值(public static final int y = 2,会直接将y赋值成2)
      在这里插入图片描述

    • 解析:将常量池中的符号引用替换成指向内存的直接引用
      符号引用是在字节码文件中使用编号来访问常量池中的内容
      直接引用则是直接使用内存中地址进行访问具体的数据

  • 初始化: 执行静态代码块中的代码,并为静态变量赋值
    同时会执行字节码文件中clinit部分的字节码指令
    在这里插入图片描述

1)类初始化的四种方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2)总结

在这里插入图片描述

在这里插入图片描述

4.类加载器

Java虚拟机提供给应用程序去实现获取类和接口字节码数据的技术

在这里插入图片描述

1)应用场景

在这里插入图片描述

2)类加载器的分类

  • Java代码中实现
  • Java虚拟机底层源码实现
    在这里插入图片描述
    在这里插入图片描述使用arthas查看类加载器
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3)双亲委派机制

1.概述:

双亲委派机制指的是:当一个类加载器接收到加载类的任务时,会自底向上查找是否加载过,如果都没有加载过,那么会自顶向下尝试加载
在这里插入图片描述
详细描述:

  • 每个类加载器有一个父类加载器,在类加载的过程中,类加载器会先检查是否已经加载了该类,如果加载过则直接返回,否则会将加载请求委派给父类加载器
  • 如果所有的父类加载器都无法加载该类(判断是否在自身的加载目录中),那么则由当前类加载器尝试加载
  • 第二次再去加载相同类,仍然会向上进行委派,如果某个类加载器加载过就会直接返回

作用

  • 保证类加载的安全性:
    避免恶意代码替换JDK中的核心类库,比如java.lang.String,确保核心类库的完整和安全性
  • 避免重复加载:
    双亲委派机制可以避免同一个类被多次加载
    在这里插入图片描述
2.打破双亲委派机制

应用场景: 当需要加载相同限定名的类时,需要打破双亲委派机制来使得两个类都能加载

在这里插入图片描述
在这里插入图片描述

1)自定义类加载器并重写loadClass方法

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

当两个相同限定名类的被加载时,并不会产生冲突,因为JVM中只有相同类加载器去加载相同限定名的类才会被认为是同一个类
而正常情况下相同限定名的类是由不同的加载器去加载的。
在这里插入图片描述

实现的自定义加载器会默认将其父加载器赋值为ApplicationClassLoader
在这里插入图片描述

2)通过SPI机制以及上下文线程类加载器实现

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • SPI机制是如何拿到应用程序类加载器的
    在这里插入图片描述

  • 总结:

在这里插入图片描述

  • 扩展
    在这里插入图片描述

4) 热部署

在这里插入图片描述

5) 类加载器的变化

  • JDK8以及之前:
    在这里插入图片描述
  • JDK8之后的类加载器
    在这里插入图片描述
    在这里插入图片描述

6)总结

1.类加载器作用

类加载器负责在类加载过程中获取类的字节码并加载到内存。
通过加载字节码数据放入内存转换成byte[],接下来调用虚拟机底层方法将byte[]转换成方法区和堆中的数据。

2.类加载器的种类

在这里插入图片描述

3. 双亲委派机制

指的是每个Java实现的类加载器保存了一个成员变量为’父‘类加载器。
当接收到一个类的加载任务时,首先会自底向上查找该类是否被加载过(防止重复加载),如果均没有加载过,那么则会自顶向下尝试加载(从自身的加载目录中查找是否属于自己的加载范围),到最后都没有能够加载成功则会抛出类加载异常。

4.打破双亲委派机制

1.重写loadClass方法,不再实现双亲委派机制
2.JDBC、JNDI等框架使用了SPI机制+上下文线程类加载器
3.OSGi实现了一整套类加载机制,允许同级类加载器之间相互调用


5.运行时数据区

在这里插入图片描述

1.需要弄懂的问题:

  • Java的内存分成哪几部分
  • Java内存中哪些部分会产生内存溢出问题
  • JDK7和JDK8中在内存结构上的区别是什么

内存溢出:指的是程序在使用某一块内存区域时,存放的数据需要占用的内存大小超过了虚拟机能提供的内存上限。

2.需要掌握的知识

  • 了解运行时内存结构
    • 了解JVM运行过程中每一部分的内存结构以及哪些部分容易出现内存溢出
  • 掌握内存问题产生的原因
    • 学习代码中常见的几种内存泄漏、性能问题的常见原因
  • 掌握内存调优的基本方法
    • 学习内存泄漏、性能问题等常见JVM问题的常见解决方案

3.运行时数据区详解

1)程序计数器

程序计数器(Program Counter Register)也叫PC寄存器,每个线程会通过程序计数器记录当前要执行的字节码指令的地址。

  • 案例:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

在CPU处理多线程时则会根据每个线程中的程序计数器来获取当前线程下一步需要执行的字节码指令,从而完成不同线程切换下每个线程字节码指令的顺序执行。

思考:程序技术器在运行过程中会出翔内存溢出吗?

不会的。
因为每个线程的程序计数器只存储一个固定长度的内存地址,所以不会发生内存溢出。
同时程序员无需对程序计数器做任何处理。


2)栈
1.Java虚拟机栈

采用栈数据结构来管理方法调用中的基本数据,先进后出,每一个方法的调用使用一个栈帧来保存。
在这里插入图片描述
Java虚拟机栈随着线程的创建而创建,而回收则会在线程销毁时进行。每个线程都会包含一个自己的虚拟机栈。

1)局部变量表

作用:在方法执行过程中存放所有的局部变量,编译成字节码文件时可以确定局部变量表的内容。

在这里插入图片描述

  • 栈帧中局部变量表是一个数组,数组中每一个位置称为槽,longdouble分别占用两个槽,其他类型数组占用一个槽。
    在这里插入图片描述

  • 实例方法中的序号为0的位置存放的是this,当前实例对象的引用地址,其顺序与方法中参数定义的顺序一致。

  • 局部变量表保存的内容有: 实例方法的this对象、方法的参数、方法体中声明的局部变量


** 思考**

  • 为了节省空间,局部变量表中的槽是可以复用的,一旦某个局部变量不再生效,当前槽就可以再次被使用。

在这里插入图片描述


2)操作数栈

操作数栈是栈帧中虚拟机在执行指令过程中用来存放中间数据的一块区域,它是栈式的数据结构,如果一条指令将一个值压入操作数栈,后面的指令可以弹出并使用该值。

  • 在编译期就可以确定操作数栈的最大深度,从而在执行时准确分配内存大小。

在这里插入图片描述

在这里插入图片描述

3) 帧数据

1)动态链接

  • 1.当前类的字节码指令引用了其他类的属性或者方法时,需要将符号引用转换成对应的运行时常量池中的内存地址。
  • 动态链接就保存了编号到运行时常量池的内存地址的映射关系。
    在这里插入图片描述

2)方法出口

  • 指的是方法在正确或者异常结束时,当前栈帧会被弹出,同时程序计数器应该指向上一个栈帧中的下一条指令的地址
  • 所以在当前栈帧中需要存放此方法出口的地址(也就是上一个栈帧中的下一条指令地址)。
    在这里插入图片描述
    3) 异常表
  • 存放代码中异常的处理信息,包含了异常捕获的生效范围以及异常发生后跳转到的字节码指令位置。在这里插入图片描述
4)栈内存溢出
  • Java虚拟机栈如果栈帧过多,占用内存超过栈内存可分配的最大空间就会出现内存溢出
  • Java虚拟机栈内存溢出时会出现StackOverflowError

默认大小
在这里插入图片描述


1M内存大小的栈内存可以存放多少个方法栈帧
在这里插入图片描述
在这里插入图片描述
设置Java虚拟机栈的内存大小
在这里插入图片描述
Java虚拟机栈内存注意事项
在这里插入图片描述

2.本地方法栈
  • Java虚拟机栈存储了Java方法调用时的栈帧,而本地方法栈存储的是native本地方法的栈帧。
  • 在Hotspot虚拟机中,Java虚拟机栈和本地方法栈实现上使用了同一个栈空间。本地方法栈会在栈馁存上生成一个栈帧,临时保存方法的参数,同时方便出现异常时也把本地方法的栈信息打印出来。
    在这里插入图片描述

3)堆
  • 一般来说Java程序中堆内存是空间最大的一块内存区域,创建出来的对象都存放在堆上
  • 栈的局部变量表中可以存放堆上对象的引用,静态变量也可以存放堆对象的引用,通过静态变量就可以实现对象在线程之间共享
    在这里插入图片描述
1.模拟堆内存的溢出
  • 通过new关键字不停创建对象,放入集合,模拟堆内存的溢出。
    结论:
    堆内存大小是有上限的,当对象一直向堆中放入,达到上限之后,就会抛出OutOfMemory错误。
    在这里插入图片描述
2.堆空间的数值
  • used, total, max

  • used指的是当前已使用的堆内存,total是Java虚拟机已经分配的可用堆内存,max是Java虚拟机可以分配的最大堆内存。

  • 当前还可以存放的内存大小为:total-used,如果还不够,那么则会向max延展
    **通过arthas查看堆内存占用: dashboard(默认5秒刷新一次)
    在这里插入图片描述

  • 当total可以使用的内存不足时,java虚拟机会在max范围内继续分配内存给堆

3.堆内存大小的设置
  • 如果不设置任何的虚拟机参数,total的默认大小是系统内存的1/64,max的默认大小是系统内存的1/4。实际应用中一般需要设置total和max的值。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • Java服务端程序开发时,建议将-Xmx和-Xms设置为相同的值,这样程序启动之后可使用的总内存就是最大内存,而不需要向Java虚拟机再次申请,减少了申请并分配内存的时间开销,同时也不会出现内存过剩之后堆收缩的情况。
4)方法区
  • 用于存储每个类的基本信息(元信息),一般称之为InstanceKlass对象,在类的加载阶段完成
    在这里插入图片描述
  • 方法区除了存储类的元信息之外,还存放了运行时常量池。常量池中存放的是字节码中的常量池内容。
  • 字节码文件通过编号查表的方式找到常量,这种常量池称为静态常量池。
  • 当常量池加载到内存中之后,可以通过内存地址快速定位到常量池中的内容,这种常量池称为运行时常量池。
  • 在这里插入图片描述

方法区:

在这里插入图片描述

是Java中设计的虚拟概念,在Hotspot设计中:

  • JDK7以及之前版本将方法区存放在堆空间的永久代空间,堆的大小由虚拟机参数来控制。
  • JDK8及之后的版本将方法区存放在元空间中,元空间位于操作系统维护的直接内存中,默认情况下只要不超过操作系统承受的上限就能一直分配。
    在这里插入图片描述
    使用arthas查看不同JDK版本的方法区实现:
    在这里插入图片描述
1.方法区溢出

在这里插入图片描述

2.字符串常量池
  • 字符串常量池存储在代码中定义的常量字符串内容,比如"123"这个123就会被放入字符串常量池。
    在这里插入图片描述
    早期的字符串常量池是运行时常量池的一部分,存储位置也是一致的,后续将字符串常量池和运行时常量池做了拆分。
    在这里插入图片描述

练习题1:
在这里插入图片描述
解答:
这里c是直接从字符串常量池中获取引用地址,而d则是通过拿到两个string在字符串常量池中的引用地址,然后重新创建对象进行拼接,而新建对象会存放在堆内存中,于是d指向的是堆内存中"12"的引用地址,所以二者是不相同的。返回false

练习题2:

在这里插入图片描述
第二题在编译阶段会直接将“1”和“2”拼接得到“12”,从而直接在字符串常量池中找到该字符串的引用地址,所以二者是相同的地址。返回true


  • 补充:intern
    在这里插入图片描述
    在这里插入图片描述
  • JDK7及之后,字符串常量池在堆内存上,intern()方法会把第一次遇到的字符串的引用放入字符串常量池。
5)直接内存

在这里插入图片描述
在这里插入图片描述


6) 总结

运行时数据区包括:程序计数器、Java虚拟机栈、本地方法栈、堆、方法区。

在这里插入图片描述

  • 程序计数器(不会出现内存溢出):
    每个线程会通常程序计数器记录当前要执行的字节码指令的地址,程序计数器可以控制程序指令的进行,实现分支、跳转、异常等逻辑。
  • Java虚拟机栈(内存溢出:由于一些方法调用导致的内存溢出):
    采用栈的数据结构来管理方法调用中的基本数据(局部变量、操作数等),每一个方法的调用使用一个栈帧来保存。
  • 本地方法栈:
    保存的是native(c++实现)的方法调用的栈帧。
  • 堆(最容易内存溢出):
    存放创建出来的对象
  • 方法区(也会出现内存溢出):
    主要存放类的元信息(基本信息),同时保存了常量池。

不同版本运行时数据区的区别:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


6.自动垃圾回收

  • 内存管理:一个对象如果不再使用,需要手动释放,否则会出现内存泄漏。
  • 我们称这种释放对象的过程为垃圾回收,需要程序员手动编写代码实现回收的方式为手动回收
  • 内存泄漏: 指的是不再使用的对象在系统中未被回收,内存泄漏的积累会导致内存溢出。比如c中的内存泄漏:
  • 在这里插入图片描述
  • Java中的内存管理:
    为了简化对象的释放,引入了自动垃圾回收(Garbage Collection简称GC)机制。
    通过垃圾回收器来对不再使用的对象完成自动的回收,垃圾回收器主要负责对堆上的内存进行回收。

在这里插入图片描述

1.应用场景

  • 1.解决系统僵死: 频繁的垃圾回收会导致系统处理僵死
  • 2.性能优化: 对垃圾回收器合理设置可以优化系统性能
  • 3.高频面试题

2.方法区的回收

  • 线程不共享的部分,是随着线程的创建而创建,线程的销毁而销毁的。并且方法的栈帧在执行完方法之后就会自动弹出栈并释放掉对应的内存。所以无需担心这些区域的内存泄漏。
    在这里插入图片描述
    类生命周期:
    在这里插入图片描述

  • 方法区中回收的内存主要是不再使用的类
    判定一个类可以被卸载,需要同时满足以下三个条件:

  • 1.此类的所有实例对象都已经被回收,在堆中不存在任何该类的实例对象以及子类对象

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

  • 3.该类对应的java.lang.Class对象没有在任何地方被引用

  • 注:开发中很少出现方法区的回收,但是在比如JSP等应用场景中,每个jsp文件对应一个唯一的类加载器,当一个jsp文件修改了,就直接卸载这个jsp类加载器,重新创建类加载器,重新加载jsp文件。
    在这里插入图片描述

3.堆回收

1)引用计数法和可达性分析法
  • Java中对象是否能够回收,是根据对象是否被引用来决定的,如果对象被引用了,说明该对象还在使用,不允许被回收。
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

1.引用计数法

为每个对象维护一个引用计数器,当对象被引用时加1,取消引用减1。

  • 优点:实现简单
  • 缺点:
    • 1.每次引用和取消引用都需要维护计数器,对系统性能有一定影响。
    • 2.存在循环引用问题,当A引用B,B引用A时会出现对象无法回收的问题。

在这里插入图片描述

查看垃圾回收日志:
在这里插入图片描述

2.可达性分析法
  • Java中使用的是可达性分析算法来判断对象是否可以被回收。
  • 可达性分析将对象分为两类:垃圾回收的根对象(GC Root)和普通对象,对象与对象之间存在引用关系。
    在这里插入图片描述
  • 图中:B是被GC Root对象引用了,所以对象B、C、D都不会被回收。
  • 如去除GC Root对象A对对象B的引用,那么对象B、C、D都是可以被回收的。
    **

案例分析:

在这里插入图片描述

  • 以A实例对象为例,A被栈帧中a1所引用,而该栈内存是属于当前main线程的于是会在堆内存的main线程对象中存放该栈内存的引用,于是当a1不再引用A对象时,A对象不可达,会被回收。
GC Root对象

哪些对象被称之为GC Root对象:

  • 线程Thread对象(引用线程栈帧中的方法参数、局部变量等是可达的)
    在这里插入图片描述
    比如这里的线程对象会保存栈内存的引用地址,而栈帧中局部变量指向的对象也就变成可达的了。

  • 系统类加载器加载的java.lang.Class对象 (引用类中的静态变量是可达的)
    在这里插入图片描述
    这里自定义的类是通过应用程序加载器来加载的,而应用程序加载器包含在sun.misc.Launcher中,并且在自定义的类中的class对象中是包含静态变量a2的,所以a2是可达的。

  • 监视器对象 (用来保存同步锁synchronized关键字持有的对象)
    在这里插入图片描述

  • 本地方法调用时使用的全局对象

查看GC Root

通过arthas和eclipse Memory Analyzer(MAT)工具查看GC Root,MAT工具是eclipse推出的Java堆内存检测工具。
操作步骤:
1.使用arthas的heapdump命令将堆内存快照保存到本地磁盘中。
2.使用MAT工具打开堆内存快照文件。
3.选择GC Roots功能查看所有的GC Root。

在这里插入图片描述

  • 使用arthas的heapdump命令保存堆内存快照:
    在这里插入图片描述
2)五种对象引用
1.强引用:

就是GC Root对象对普通对象有引用关系,只要有这层关系存在,普通对象就不会被回收。

2.软引用:

相对于强引用是比较弱的一种引用关系,
如果一个对象只有软引用关联时,当程序内存不足时,就会将软引用中的数据进行回收。
JDK1.2版本之后提供了SoftReference类来实现软引用,软引用常用用于缓存中。

在这里插入图片描述

软引用的执行过程如下:

  • 将对象使用软引用包装起来: new SoftReference<对象类型>(对象)
  • 内存不足时,虚拟机尝试进行垃圾回收
  • 如果垃圾回收仍不能解决内存不足问题,则会回收软引用的对象
  • 如果依然内存不足,则会抛出OutOfMemort异常

软引用示例:
在这里插入图片描述

  • 设置堆内存为200M,先是创建100M的字节数组,然后将该字节数组保存到一个软引用对象中,
  • 第一次打印软引用对象:可以得到软引用对象的地址
  • 再次放入100M的字节数组,这时由于内存只有200M,先前放入了100M,并且程序其他变量也会占用一定内存,再次放入100M就会导致内存不足,此时会回收软引用的内存,于是再次打印软引用时就会返回null

软引用中的对象如果在内存不足时回收,SoftReference对象本身也需要回收。
如何知道哪些SoftReference对象需要回收?

SoftReference的队列机制:

  • 软引用创建时,通过构造器传入引用队列
  • 在软引用中包含的对象被回收时,该软引用对象会被放入引用队列
  • 通过代码遍历引用队列,将对应的SoftReference强引用删除

在这里插入图片描述


案例:
在这里插入图片描述

3.弱引用:

弱引用的整体机制和软引用基本一致,区别在于弱引用包含的对象在垃圾回收时,不管内存够不够都会直接被回收。

  • JDK1.2版本之后提供了WeakReference类来实现弱引用,弱引用主要在ThreadLocal中使用。
  • 弱引用对象本身也可以使用引用队列进行回收。
4.虚引用:
  • 常规开发一般不使用
  • 虚引用唯一的用途是当对象被垃圾回收器回收时可以接收到对应的通知。
  • Java中使用PhantomReference实现了虚引用,直接内存中为了及时知道直接内存对象不再使用,从而回收内存,使用了虚引用来实现。
  • 不能通过虚引用对象获取到包含的对象。
5.终结器引用:
  • 常规开发一般不使用
  • 终结器引用指的是在对象需要被回收时,终结器引用会关联对象并放置在Finalizer类中的因哟个队列中,在稍后由一条由FinalizerThread线程从队列中获取对象,然后执行finalize方法,在对象第二次被回收时,该对象才真正被回收。
3)垃圾回收算法
1.主核心思想
  • Java如何实现垃圾回收的?
    • 找到内存中存活的对象
    • 释放不再存活对象的内存,使得程序能再次利用这部分空间

在这里插入图片描述

  • 垃圾回收算法的评价标准
    Java垃圾回收过程会通过单独的GC线程来完成,不管使用哪一种GC算法,都会有部分阶段需要停止所有的用户线程。这个过程被称之为Stop The World简称STW。
    • 如果STW时间过长会影响用户的使用。

在这里插入图片描述

  • 判断GC算法是否优秀:

    • 1.吞吐量:指的是CPU用于执行用户代码的时间与CPU总执行时间的比值
      • 吞吐量= 执行用户代码时间 / (执行用户代码时间 + GC时间)。
      • 吞吐量数值越高,代表垃圾回收的效率越高。比如虚拟机运行了100分钟,其中GC花费1分钟,那么吞吐量就是99%。
    • 2.最大暂停时间:指的是所有在垃圾回收过程中的STW时间最大值。
      • 比如图中:黄色部分的STW就是最大暂停时间。
      • 明显的是上面的区域比下图拥有更少的最大暂停时间。
      • 最大暂停时间越短,用户使用系统时受到的影响就越短。
        在这里插入图片描述
    • 3.堆使用效率
      • 不同垃圾回收算法,对堆内存的使用方法是不同的。
      • 比如标记清除算法可以完整使用堆内存,而复制算法会将堆内存一分为二,每次只能使用一半内存。从堆使用效率来说,标记清除算法要优于复制算法。
  • 三种评价标准: 堆使用效率、最大暂停时间、吞吐量不可兼得。

  • 正常来说,堆内存越大,最大暂停时间越长。想要减少最大暂停时间,就想要将堆内存划分为多块区域,这样每次去清理时重复的准备工作就会增加,导致吞吐量降低。

  • 不同的垃圾回收算法,适用于不同场景。

2.标记清除算法
1)核心思想
  • 1.标记阶段:将所有存活的对象进行标记。Java中使用可达性分析算法,从GC Root开始通过引用链遍历出所有存活对象
  • 2.清除阶段:从内存中删除没有被标记也就是非存活对象
    在这里插入图片描述
2)优缺点

优点:实现简单,只需要在第一阶段给每个对象维护标志位,第二阶段删除对象即可
缺点:

  • 1.碎片化问题:
    由于内存是连续的,所以在对象被删除之后,内存中会出现很多细小的可用内存单元。
    如果我们需要的是一个比较大的空间,这些零散的内存单元可能无法进行分配。
    比如:
    进行垃圾回收,回收了9个字节的对象,但是不是连续的,现在需要创建5个字节的对象,内存空间中没有连续的5个字节大小的空间
    在这里插入图片描述

  • 2.分配速度慢:
    由于内存碎片的存在,需要维护一个空间链表来维护已经回收掉现在可用的空间地址。
    那么在创建对象时,可能每次都需要遍历到链表的最后才能获得合适的内存空间来创建对象。
    比如:
    在这里插入图片描述

3.复制算法
1)核心思想:
  • 1.准备两块空间:From空间和To空间,每次在对象分配阶段,只能使用其中一块空间(From空间)
  • 2.在垃圾回收GC阶段,将From中存活对象复制到To空间。
  • 3.将GC Root关联的对象搬运到To空间
  • 4.清理From空间,并把名称互换在这里插入图片描述
    在这里插入图片描述

就是将存活的对象复制到To空间,然后清理掉From空间,最后把名字互换,来回往复。

生动描述就是:用两个一样的杯子将一杯热水变凉,在两个杯子里来回倒,每次倒完之后的杯子重置一下温度

2)优缺点:
  • 1.优点:

    • 吞吐量高:
      复制算法只需要遍历一次存活对象复制到To空间即可,比标记整理算法少了一次遍历的过程。
      性能没有标记清除算法好,因为标记清除算法不需要进行对象的移动。
    • 不会发生碎片胡:
      复制算法在复制之后就会将对象按顺序放到To空间中,所以对象以外的区域都是可用空间,不存在碎片化空间。
  • 2.缺点:
    内存使用效率低:
    每次只能使用一半的内存空间来为创建对象使用。

4.标记整理算法
1)核心思想:
  • 也叫标记压缩算法,是对标记清理算法中容易产生内存碎片问题的一种解决方案
  • 核心思想:
    • 1.标记阶段:将所有存活对象进行标记。(Java中使用可达性分析算法,从GC Root开始通过引用链遍历出所有存活对象
    • 2.整理阶段:将存活对象移动到堆的一端,然后清理掉非存活对象的内存空间
      在这里插入图片描述
2)优缺点:
  • 1.优点:
    • 内存使用效率高:整个堆内存均可使用,不像复制算法一样只能使用半个堆内存
    • 不会发生碎片化:在整理阶段将对象往内存的一侧进行移动,剩下的空间都是可用的连续空间。
  • 2.缺点:
    • 整理阶段的效率不高:这里需要使用整理算法进行优化,比如Two-Finger、表格算法、ImmixGC等高效的整理算法优化。
5.分代GC算法
  • 目前应用最广的垃圾回收算法

分代垃圾回收算法将整个内存区域划分为年轻代和老年代:
在这里插入图片描述

使用arthas查看分代之后的内存情况:

  • 在JDK8中,添加-XX:+UseSerialGC参数使用分代回收的垃圾回收器,运行程序
  • 在arthas中使用memory命令查看内存,显示出三个区域的内存情况
    在这里插入图片描述

在这里插入图片描述

1)核心思想:

伊甸园区:对象刚创建时存放的区域
survivor区(S0和S1):是跟复制算法一样的模式
老年代区:当年轻代To中的对象年龄达到阈值之后则会存放到老年代中

  • 1.对象刚创建时,首先会被放入Eden区

  • 2.随着对象在Eden区越来越多,然后Eden区满,新创建的对象无法放入,就会触发年轻代的GC,称为Minor GC 或者 Young GC

  • 3.MinorGC会根据Java中的可达性分析算法把Eden区中和From区中需要回收的对象回收,然后,将存活的对象放入To区
    在这里插入图片描述
    在这里插入图片描述

  • 4.根据复制算法,接下来S0会变成To区,S1会变成From区,当Eden区满时,再有创建的对象则又继续发生Minor GC,执行步骤3的流程:回收Eden区和S1(From)中的对象,并把这两个区域中存活的对象放入到S0(To)区域。
    注意:每次Minor GC 中都会为对象记录他的年龄,初始值为0,每次GC完年龄加1

  • Minor GC后,对象的年龄达到阈值(最大15,默认值和垃圾回收器有关),对象就会被晋升到老年代。

  • 当老年代中空间满了,无法再放入新的对象时,先会尝试Minor GC,如果回收之后还是不足,就会触发Full GC,Full GC会对整个堆进行垃圾回收。

  • 特殊情况:如果年轻代内存满了,在继续创建对象,则会发生有些对象的年龄并没有达到阈值(默认15)也会被放到老年代中。

  • 如果Full GC依然无法回收掉老年代的对象,那么当对象继续放入老年代时,就会抛出OutOfMemory异常。

在这里插入图片描述

2)为什么要把堆分成年轻代和老年代
  • 系统中的大部分对象,都是创建出来之后很快就不再使用,可用被回收(新生代就回收)。比如用户获取订单数据,订单数据返回给用户之后就可以释放了
  • 老年代中会存放长期存活的对象,比如Spring的大部分bean对象,在程序启动之后就不会被回收了。
  • 在虚拟机的默认设置中,新生代的大小要远小于老年代的大小。

主要原因:

  • 1.可以通过调整年轻代和老年代的比例来适应不同类型的应用程序,提高内存的利用率和性能。
    比如:
    短时间内会产生大量对象,但是对象的生命周期比较短,这时候就需要将新生代大小分配的大一些,可以避免频繁的Minor GC以及减少不足年龄的老年代产生。
  • 2.新生代和老年代使用不同的垃圾回收算法,新生代一般使用复制算法,老年代(内存较大)可用选择标记清除和标记整理算法,灵活度较高。
  • 3.分代的设计允许只回收新生代(Minor GC ),如果能满足对象分配的要求就不需要对整个堆进行回收(Full GC),STW时间会减少。(尽可能只做Minor GC,不做Full GC)
4) 垃圾回收器

在这里插入图片描述

1.垃圾回收器的种类
1)组合1:年轻代Serial与老年代Serial Old

适用于CUP资源比较匮乏的情况
在这里插入图片描述
在这里插入图片描述

2)组合2:年轻代ParNew与老年代CMS(Concurrent Mark Sweep)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

3)组合3:年轻代Parallel Scavenge与老年代Parallel Old (JDK8默认)

在这里插入图片描述

4)G1:JDK9及之后使用
  • JDK9之后默认的垃圾回收器是G1(Garbage First)垃圾回收器
  • Parallel Scavenge关注吞吐量,允许用户设置最大暂停时间,但是会减少年轻代可用空间的大小
  • CMS关注暂停时间,但是吞吐量方面会下降
  • 而G1的设计目标就是将上述两种垃圾回收器的优点融合:
    • 1.支持巨大的堆空间回收,并有较高的吞吐量
    • 2.支持多CPU并行垃圾回收
    • 3.允许用户设置最大暂停时间

在这里插入图片描述
在这里插入图片描述
G1的两种回收方式:

  • 1.年轻代回收(Young GC):
    回收Eden区和Survivor区不用的对象,会导致STW,G1中可以通过参数-XX:MaxGCPauseMillis=n(默认200),设置每次垃圾回收时的最大暂停时间毫秒数,G1垃圾回收器会尽可能地保证暂停时间。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

  • 2.老年代回收(Mixed GC)
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

*注:上述内容基本黑马程序员B站视频学习整理的学习笔记,仅用于学习交流,不作为商业用途,如有侵权,联系删除。

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

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

相关文章

ISL9V3040D3ST-F085C一款安森美 ON生产的汽车点火IGBT模块,绝缘栅双极型晶体管ISL9V3040D3ST汽车点火电路中的线圈驱动器

ISL9V3040D3ST-F085C 是一款 安森美 &#xff08;ON&#xff09;生产的汽车点火 IGBT模块&#xff08;绝缘栅双极型晶体管&#xff09;&#xff0c;主要用于汽车点火电路中的线圈驱动器&#xff0c;具有内部二极管电压箝位功能&#xff0c;可减少外部组件需求。‌ 核心用途 该…

用Python实现Excel转PDF并去除Spire.XLS水印

最近业务需要&#xff0c;成功用Python原生代码实现了原本需要付费的Spire.XLS库的Excel转PDF功能&#xff0c;并彻底去除了转换后PDF中的评估水印"Evaluation Warning: The document was created with Spire.XLS for Python"。该解决方案完全开源免费&#xff0c;不…

论文学习22:UNETR: Transformers for 3D Medical Image Segmentation

代码来源 unetr 模块作用 具有收缩和扩展路径的全卷积神经网络 (FCNN) 在大多数医学图像分割应用中表现出色&#xff0c;但卷积层的局部性限制了其学习长距离空间依赖性的能力。受 Transformer 在自然语言处理 (NLP) 领域近期在长距离序列学习方面取得的成功的启发&#xff…

Jmeter使用第一节-认识面板(Mac版)

常用的基础元件&#xff08;10个&#xff09;1、测试计划&#xff1a;总体项目容器&#xff0c;其他元件需要建立在这个目录下面2、线程组&#xff1a;可以设置线程数、循环次数等参数来模拟用户行为。一个用户可用于接口测试&#xff0c;多个用户则可用于性能压测。“线程数”…

微软披露Exchange Server漏洞:攻击者可静默获取混合部署环境云访问权限

微软近日发布安全公告&#xff0c;披露一个影响本地版Exchange Server的高危漏洞&#xff08;编号CVE-2025-53786&#xff0c;CVSS评分为8.0&#xff09;。该漏洞在特定条件下可能允许攻击者提升权限&#xff0c;Outsider Security公司的Dirk-jan Mollema因报告此漏洞获得致谢。…

大模型中的反向传播是什么

反向传播&#xff08;Backpropagation&#xff09;是大模型&#xff08;如GPT、BERT等&#xff09;训练过程中的核心算法&#xff0c;用于高效计算损失函数对神经网络中所有参数的梯度。这些梯度随后被用于优化器&#xff08;如Adam&#xff09;更新参数&#xff0c;使模型逐渐…

数集相等定义凸显解析几何几百年重大错误:将无穷多各异点集误为同一集

数集相等定义凸显解析几何几百年重大错误&#xff1a;将无穷多各异点集误为同一集 黄小宁 本文据中学生就应熟悉的数集相等概念推翻了直线公理和平面公理表明“举世公认”不能是检验真理的唯一标准。“真理往往在少数人手里”。 请看图片举世公认&#xff1a;因数学是严密精确的…

container_of函数使用

用于根据结构体成员的地址反推整个结构体地址的宏定义。其核心作用是通过成员变量地址定位到其所属的结构体实例。struct panel_tm145{struct drm_panel base;}static inline struct panel_tm145 * to_panel_tm145(struct drm_panel *panel){return container_of(panel, struct…

【MySQL基础篇】:MySQL索引——提升数据库查询性能的关键

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨ 文章所属专栏&#xff1a;MySQL篇–CSDN博客 文章目录索引一.MySQL与存储二.索引的理解1.Page页模式理解单个Page理解…

TD-IDF的一些应用

TF-IDF&#xff08;词频 - 逆文档频率&#xff09;作为经典的文本特征提取算法&#xff0c;在自然语言处理&#xff08;NLP&#xff09;领域应用广泛。它能将文本转化为可量化的数值特征&#xff0c;为后续的数据分析和建模提供基础。本文结合实际场景&#xff0c;介绍如何用 P…

Redis 缓存问题详解及解决方案

一、缓存击穿 (Cache Breakdown) 原理&#xff1a; 某个热点 Key 突然过期&#xff0c;同时大量并发请求该 Key&#xff0c;导致请求直接穿透缓存击穿到数据库。 解决方案&#xff1a; 互斥锁 (Mutex Lock) 当缓存失效时&#xff0c;仅允许一个线程重建缓存&#xff0c;其他线程…

一周一个数据结构 第一周 --- 顺序表(下)

文章目录一、ArrayList的构造二、ArrayList常见操作三、ArrayList的遍历四、ArrayList练习1.【小练习】2.杨辉三角3.简单的洗牌算法五、ArrayList小结在上一章节中&#xff0c;我们通过代码示例以及画图的方式详细了解了顺序表&#xff0c;并模拟实现了它。那么&#xff0c;是不…

OpenCV的关于图片的一些运用

一、读取图片通过cv2库中的imread&#xff08;&#xff09;方法读取图片代码&#xff1a;import cv2 a cv2.imread(1.png) cv2.imshow(tu,a) b cv2.waitKey(4000) # 图片执行时间 cv2.destroyAllWindows() # 关闭所有端口 print("图像形状(shape):",a.shape) print…

【数据结构——并查集】

引入 并查集&#xff08;Disjoint Set Union&#xff0c;DSU&#xff09;是一种用于管理元素分组的数据结构。 合并&#xff08;Union&#xff09;&#xff1a;将两个不相交的集合合并为一个集合。 查找&#xff08;Find&#xff09;&#xff1a;确定某个元素属于哪个集合&…

在 Vue 中使用 ReconnectingWebSocket实现即时通讯聊天客服功能

在 Vue 中使用 ReconnectingWebSocketReconnectingWebSocket 是一个自动重连的 WebSocket 实现&#xff0c;非常适合在 Vue 项目中使用。下面是如何在 Vue 中集成和使用它的方法&#xff1a;搜索 "程序员老狼"安装 ReconnectingWebSocket首先&#xff0c;你需要安装…

智能体革命:网络安全人的角色重塑与突围指南

AI赋能千行百业的趋势不可逆转&#xff0c;当AI学会渗透测试&#xff0c;安全工程师的出路在哪里&#xff1f; 2025年8月7日&#xff0c;OpenAI正式发布GPT-5的消息刷屏科技圈。这个达到博士生水平的“统一”人工智能模型&#xff0c;将AI幻觉率降低60%&#xff0c;成本下降45%…

用于水T1值和脂肪分数量化的上半身自由呼吸磁共振指纹成像|文献速递-医学影像算法文献分享

Title题目Upper-body free-breathing Magnetic Resonance Fingerprinting applied tothe quantification of water T1 and fat fraction用于水T1值和脂肪分数量化的上半身自由呼吸磁共振指纹成像 01文献速递介绍磁共振指纹成像&#xff08;MRF&#xff09;是十年前推出的一种高…

Apache RocketMQ:消息可靠性、顺序性与幂等处理的全面实践

Apache RocketMQ 是一个高性能、高可靠的分布式消息中间件&#xff0c;广泛应用于异步通信、事件驱动架构和分布式系统中。本文深入探讨 RocketMQ 的消息可靠性、顺序性和幂等处理机制&#xff0c;结合 Redisson 分布式锁实现幂等消费&#xff0c;提供详细的代码示例和实践建议…

无服务器日志分析由 Elasticsearch 提供支持,推出新的低价层

作者&#xff1a;来自 Elastic Log Analytics Elastic Observability Logs Essentials 在 Elastic Cloud Serverless 上提供成本效益高、无麻烦的日志分析。 SREs 可以摄取、搜索、丰富、分析、存储和处理日志&#xff0c;而无需管理部署的运营开销。[](https://www.elastic.co…

(Arxiv-2025)Phantom-Data:迈向通用的主体一致性视频生成数据集

Phantom-Data&#xff1a;迈向通用的主体一致性视频生成数据集 paper是字节发布在Arxiv2025的工作 paper title&#xff1a;Phantom-Data: Towards a General Subject-Consistent Video Generation Dataset Code&#xff1a;链接 Abstract 近年来&#xff0c;主体到视频&#…