当前位置: 首页 > news >正文

JVM垃圾回收笔记01-垃圾回收算法

文章目录

  • 前言
  • 1. 如何判断对象可以回收
    • 1.1 引用计数法
    • 1.2 可达性分析算法
      • 查看根对象
      • 哪些对象可以作为 GC Root ?
      • 对象可以被回收,就代表一定会被回收吗?
    • 1.3 引用类型
      • 1.强引用(StrongReference)
      • 2.软引用(SoftReference)
      • 3.弱引用(WeakReference)
      • 4.虚引用(PhantomReference)
      • 5.终结器引用(FinalReference)
    • 如何判断一个常量是废弃常量
    • 如何判断一个类是无用的类
  • 2. 垃圾回收算法
    • 2.1 标记清除
    • 2.2 标记整理
    • 2.3 复制
  • 3. 分代垃圾回收
    • 3.1分代收集算法是什么?
    • 3.2 相关 VM 参数
    • 代码示例1(启动空程序)
    • 代码示例2(触发新生代垃圾回收)
    • 代码示例3(触发两次垃圾回收)
    • 代码示例4(大对象,当新生代放不下时)
    • 代码示例5(大对象,当新生代和老年代都放不下时)
    • 代码示例6(线程报错OOM会不会影响主线程)


前言

Java 的自动内存管理主要是针对对象内存的回收和对象内存的分配。同时,Java 自动内存管理最核心的功能是堆内存中对象的分配与回收
Java 堆是垃圾收集器管理的主要区域,因此也被称作 GC 堆(Garbage Collected Heap)。
从垃圾回收的角度来说,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆被划分为了几个不同的区域,这样我们就可以根据各个区域的特点选择合适的垃圾收集算法。
在 JDK 7 版本及 JDK 7 版本之前,堆内存被通常分为下面三部分:

  1. 新生代内存(Young Generation)
  2. 老生代(Old Generation)
  3. 永久代(Permanent Generation)

JDK 8 版本之后 PermGen(永久) 已被 Metaspace(元空间) 取代,元空间使用的是直接内存 。
详情可以看这篇文章:JVM内存结构-堆

1. 如何判断对象可以回收

堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断哪些对象已经死亡(即不能再被任何途径使用的对象)。

1.1 引用计数法

在这里插入图片描述

1.2 可达性分析算法

Java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象
扫描堆中的对象,看是否能够沿着 GC Root对象 为起点的引用链找到该对象,找不到,表示可以回收

这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。
下图中的 Object 6 ~ Object 10 之间虽有引用关系,但它们到 GC Roots 不可达,因此为需要被回收的对象。
在这里插入图片描述

查看根对象

代码示例
用到的工具:https://eclipse.dev/mat/
注意:Memory Analyzer 1.14 及更高版本需要 Java 17 VM 或更高版本的虚拟机才能运行。 Memory Analyzer 1.12 及更高版本需要 Java 11 VM 或更高版本的 VM 才能运行。 Memory Analyzer 1.8 至 1.11 需要 Java 1.8 VM 或更高版本的 VM 才能运行

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;/*** 演示GC Roots*/
public class Demo2_2 {public static void main(String[] args) throws InterruptedException, IOException {List<Object> list1 = new ArrayList<>();list1.add("a");list1.add("b");System.out.println("mark 1");System.in.read();list1 = null;System.out.println("mark 2");System.in.read();System.out.println("end...");}
}

操作以下步骤

  1. 运行程序,停留在mark 1
  2. jps查看进程pid
  3. 使用以下命令执行mark 1和mark 2时的堆对象快照
    jmap -dump:format=b,live,file=mark1.bin 进程id
    如:jmap -dump:format=b,live,file=D:\qf\mark1.bin 2448
    解释:生成在当前应用程序堆内存中对象的快照(mark.bin二进制文件)
  4. 程序继续向下执行到mark 2,使用上面命令执行mark 2时的堆对象快照
    在这里插入图片描述
  5. 打开Memory Analyzer查看
    在这里插入图片描述选择刚刚创建的mark1.bin和mark2.bin
    在这里插入图片描述
    选择刚刚创建的mark1.bin和mark2.bin
    在这里插入图片描述
    选择以下选项
    在这里插入图片描述
    mark1时:
    在这里插入图片描述
    mark2时:
    在这里插入图片描述
    可以看到在将 list1 = null 后通过指令中的live关键字触发了垃圾回收后,这里的ArrayList被垃圾回收了。

哪些对象可以作为 GC Root ?

  • 虚拟机栈(栈帧中的局部变量表)中引用的对象
  • 本地方法栈(Native 方法)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 所有被同步锁持有的对象
  • JNI(Java Native Interface)引用的对象

对象可以被回收,就代表一定会被回收吗?

即使在可达性分析法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑阶段”,要真正宣告一个对象死亡,至少要经历两次标记过程;
可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize 方法。
当对象没有覆盖 finalize 方法,或 finalize 方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行。
被判定为需要执行的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关联,否则就会被真的回收。

1.3 引用类型

软引用、弱引用、虚引用在 JDK 中定义的类分别是 SoftReference、WeakReference、PhantomReference。

1.强引用(StrongReference)

  • 只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收
  • 当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

2.软引用(SoftReference)

  • 如果内存空间足够,垃圾回收器就不会回收软引用,如果内存空间不足了,就会回收这些软引用对象的内存。软引用可用来实现内存敏感的高速缓存。
  • 可以配合引用队列(ReferenceQueue)来释放软引用自身,如果软引用所引用的对象被垃圾回收,JAVA 虚拟机就会把这个软引用加入到与之关联的引用队列中。

3.弱引用(WeakReference)

  • 在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
  • 注意:垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。
  • 可以配合引用队列来释放弱引用自身,如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。

4.虚引用(PhantomReference)

  • 如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。
  • 虚引用主要用来跟踪对象被垃圾回收的活动。
  • 当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
  • 场景:在直接内存时,使用到了虚引用,当ByteBuffer回收后,虚引用会放入引用队列,此时会有线程定时的执行寻找引用队列中新的虚引用,找到了会调用Cleaner.clean(),进行直接内存的回收。
  • 虚引用必须配合引用队列使用。主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队,由 Reference Handler 线程调用虚引用相关方法释放直接内存

5.终结器引用(FinalReference)

  • 终结器引用用于标记那些重写了finalize()方法的对象,确保这些对象在被垃圾回收之前能够执行finalize()方法。
  • 无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 **finalize()**方法,第二次 GC 时才能回收被引用对象

注意,在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速 JVM 对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。
在这里插入图片描述

如何判断一个常量是废弃常量

运行时常量池主要回收的是废弃的常量。那么,我们如何判断一个常量是废弃常量呢?
假如在字符串常量池中存在字符串 “abc”,如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 “abc” 就是废弃常量,如果这时发生内存回收的话而且有必要的话,“abc” 就会被系统清理出常量池了。

如何判断一个类是无用的类

方法区主要回收的是无用的类,那么如何判断一个类是无用的类的呢?
判定一个常量是否是“废弃常量”比较简单,而要判定一个类是否是“无用的类”的条件则相对苛刻许多。类需要同时满足下面 3 个条件才能算是 “无用的类”:

  • 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
  • 加载该类的 ClassLoader 已经被回收。
  • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

虚拟机可以对满足上述 3 个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样不使用了就会必然被回收。

2. 垃圾回收算法

2.1 标记清除

定义: 标记-清除(Mark-and-Sweep)算法分为“标记(Mark)”和“清除(Sweep)”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。
它是最基础的收集算法,后续的算法都是对其不足进行改进得到。

  • 优点:速度较快
  • 缺点:会造成内存碎片
    在这里插入图片描述
    释放内存时,不是将内存数据进行清零操作,是将对象占用的起始、结束内存地址记录下来,放在空闲地址列表中,下次给新对象分配内存地址时在该列表中寻找进行分配。

2.2 标记整理

定义:标记-整理(Mark-and-Compact)算法是根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。

  • 速度慢
  • 没有内存碎片
    在这里插入图片描述
    由于多了整理这一步,因此效率也不高,适合老年代这种垃圾回收频率不是很高的场景。

2.3 复制

定义:为了解决标记-清除算法的效率和内存碎片问题,复制(Copying)收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。

  • 不会有内存碎片
  • 需要占用双倍内存空间
  • 不适合老年代(如果存活对象数量比较大,复制性能会变得很差)
    在这里插入图片描述
    将被引用的内存复制到To,然后将from和to对调
    在这里插入图片描述

3. 分代垃圾回收

实际上jvm不是只使用以上一种算法,根据情况使用。具体使用的是分代垃圾回收。
在这里插入图片描述

  1. 新的对象首先分配在伊甸园区域
  2. 当新生代空间不足时,触发 minor gc(新生代垃圾回收,通过可达性分析算法标记),采用复制算法,将伊甸园存活的对象使用 copy 复制到 to 中,存活的对象年龄加1,并且交换 from和to区域的位置。
  3. 当第二次新生代空间不足时,触发 minor gc,将伊甸园和 from 存活的对象使用 copy 复制到 to 中,存活的对象年龄加 1并且交换 from to
  4. minor gc 会引发 stop the world,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行
  5. 当对象寿命超过阈值时,会晋升至老年代,阈值的最大寿命是15(因为是保持在对象头中,所以为4bit)
  6. 当老年代空间不足,会先尝试触发 minor gc,如果之后空间仍不足,那么触发 full gc,stop the world的时间更长(新生代回收算法采用复制算法,而老年代采用标记清除或标记整理算法。

3.1分代收集算法是什么?

当前虚拟机的垃圾收集都采用分代收集算法,这种算法是根据对象存活周期的不同将内存分为几块。一般将 Java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。

  • 如在新生代中,每次收集都会有大量对象死去,所以可以选择“复制”算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。
  • 而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。

3.2 相关 VM 参数

含义参数
堆初始大小-Xms
堆最大大小-Xmx 或 -XX:MaxHeapSize=size
新生代大小-Xmn 或 (-XX:NewSize=size + -XX:MaxNewSize=size )
幸存区比例(动态)-XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy
幸存区比例-XX:SurvivorRatio=ratio
晋升阈值-XX:MaxTenuringThreshold=threshold
晋升详情-XX:+PrintTenuringDistribution
GC详情-XX:+PrintGCDetails -verbose:gc
FullGC 前 MinorGC-XX:+ScavengeBeforeFullGC

以下通过代码触发垃圾回收,观察新生代和老年代区域变化

代码示例1(启动空程序)

/***  演示内存的分配策略*/
public class Demo {public static void main(String[] args) throws InterruptedException {}
}

配置参数

-Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:-ScavengeBeforeFullGC

参数解释:
用于配置Java虚拟机(JVM)的垃圾收集(GC)行为和内存分配

  • -Xms20M:设置 JVM 堆内存的初始大小为 20MB。也就是说,当 JVM 启动时,就会立即分配 20MB 的堆内存空间给 Java 应用程序使用。堆内存用于存放 Java 对象实例,提前分配足够的初始内存能减少后续内存动态扩展带来的性能开销。
  • -Xmx20M:设定 JVM 堆内存的最大可使用大小为 20MB。JVM 在运行过程中,堆内存会随着对象的创建和销毁而动态变化,但无论如何,堆内存占用量都不会超过这个上限值。将-Xms和-Xmx设置成相同的值,可以避免堆内存动态扩容与缩容带来的额外性能损耗,让 JVM 运行更加稳定。
  • -Xmn10M:用于指定新生代(Young Generation)的大小为 10MB。新生代是堆内存的一部分,主要用于存放新创建的 Java 对象,对象在经过多次垃圾回收后依然存活,就会被移到年老代(Old Generation)。通过合理设置新生代大小,可以优化垃圾回收效率,因为新生代的垃圾回收频率通常远高于年老代。
  • -XX:+UseSerialGC:这是一个垃圾回收器相关的参数,它指定 JVM 使用串行垃圾回收器。串行垃圾回收器是最基本的一种回收器,在进行垃圾回收时,只有一个线程参与垃圾回收工作,进行垃圾回收时会暂停整个 Java 应用程序(即 “Stop-the-World” 现象),直到垃圾回收完毕。这种回收器适合单核 CPU 环境或者对内存使用量要求不高、应用程序暂停时间不太敏感的简单场景。
  • -XX:+PrintGCDetails:启用该参数后,JVM 每次进行垃圾回收时,都会打印出详细的垃圾回收信息,例如回收前后各代内存的使用情况、回收了多少对象、花费了多长时间等。这些详细信息对于分析 JVM 内存使用效率、排查内存泄漏等问题非常有帮助。
  • -verbose:gc:这个参数同样用于输出垃圾回收相关的信息,不过相较于-XX:+PrintGCDetails,它输出的信息更为简洁,只是简单告知垃圾回收事件的发生,像是何时进行了新生代垃圾回收、何时进行了年老代垃圾回收。
  • -XX:-ScavengeBeforeFullGC:默认情况下,在进行一次完整的堆垃圾回收(Full GC)之前,会先执行一次新生代的垃圾回收(Minor GC,也叫 Scavenge),这一操作旨在尽量减少进入年老代的对象数量,降低 Full GC 的压力。添加此参数并设置为负号(-),表示禁用这个默认行为,不再提前进行新生代垃圾回收。

启动项目输出

Heapdef new generation   total 9216K, used 2318K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)eden space 8192K,  28% used [0x00000000fec00000, 0x00000000fee43960, 0x00000000ff400000)from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)tenured generation   total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)the space 10240K,   0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)Metaspace       used 3290K, capacity 4564K, committed 4864K, reserved 1056768Kclass space    used 361K, capacity 388K, committed 512K, reserved 1048576K

观察def new generation(新生代)和tenured generation(老年代)

代码示例2(触发新生代垃圾回收)

public static void main(String[] args) throws InterruptedException {ArrayList<byte[]> list = new ArrayList<>();list.add(new byte[_7MB]);
}

输出

[GC (Allocation Failure) [DefNew: 2154K->687K(9216K), 0.0014977 secs] 2154K->687K(19456K), 0.0015450 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heapdef new generation   total 9216K, used 8265K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)eden space 8192K,  92% used [0x00000000fec00000, 0x00000000ff366840, 0x00000000ff400000)from space 1024K,  67% used [0x00000000ff500000, 0x00000000ff5abea8, 0x00000000ff600000)to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)tenured generation   total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)the space 10240K,   0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)Metaspace       used 3292K, capacity 4564K, committed 4864K, reserved 1056768Kclass space    used 361K, capacity 388K, committed 512K, reserved 1048576K

可以看到只有新生代中发生变化,触发垃圾回收时将伊甸园(eden)存活的对象使用 copy 复制到 to 中,存活的对象年龄加1,并且交换 from和to区域的位置。

代码示例3(触发两次垃圾回收)

	private static final int _512KB = 512 * 1024;private static final int _1MB = 1024 * 1024;private static final int _6MB = 6 * 1024 * 1024;private static final int _7MB = 7 * 1024 * 1024;private static final int _8MB = 8 * 1024 * 1024;// -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:-ScavengeBeforeFullGCpublic static void main(String[] args) throws InterruptedException {ArrayList<byte[]> list = new ArrayList<>();list.add(new byte[_7MB]);list.add(new byte[_512KB]);}

输出

[GC (Allocation Failure) [DefNew: 2154K->713K(9216K), 0.0013532 secs] 2154K->713K(19456K), 0.0013897 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heapdef new generation   total 9216K, used 8639K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)eden space 8192K,  96% used [0x00000000fec00000, 0x00000000ff3bd8d0, 0x00000000ff400000)from space 1024K,  69% used [0x00000000ff500000, 0x00000000ff5b25a0, 0x00000000ff600000)to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)tenured generation   total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)the space 10240K,   0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)Metaspace       used 3293K, capacity 4564K, committed 4864K, reserved 1056768Kclass space    used 361K, capacity 388K, committed 512K, reserved 1048576K

代码示例4(大对象,当新生代放不下时)

当放入的对象过大,超过伊甸园的剩余空间时,会直接放入老年代,不会触发垃圾回收。

    private static final int _512KB = 512 * 1024;private static final int _1MB = 1024 * 1024;private static final int _6MB = 6 * 1024 * 1024;private static final int _7MB = 7 * 1024 * 1024;private static final int _8MB = 8 * 1024 * 1024;// -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:-ScavengeBeforeFullGCpublic static void main(String[] args) throws InterruptedException {ArrayList<byte[]> list = new ArrayList<>();list.add(new byte[_8MB]);}

输出

Heapdef new generation   total 9216K, used 2318K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)eden space 8192K,  28% used [0x00000000fec00000, 0x00000000fee439b8, 0x00000000ff400000)from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)tenured generation   total 10240K, used 8192K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)the space 10240K,  80% used [0x00000000ff600000, 0x00000000ffe00010, 0x00000000ffe00200, 0x0000000100000000)Metaspace       used 3292K, capacity 4564K, committed 4864K, reserved 1056768Kclass space    used 361K, capacity 388K, committed 512K, reserved 1048576K

代码示例5(大对象,当新生代和老年代都放不下时)

    private static final int _512KB = 512 * 1024;private static final int _1MB = 1024 * 1024;private static final int _6MB = 6 * 1024 * 1024;private static final int _7MB = 7 * 1024 * 1024;private static final int _8MB = 8 * 1024 * 1024;// -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:-ScavengeBeforeFullGCpublic static void main(String[] args) throws InterruptedException {ArrayList<byte[]> list = new ArrayList<>();list.add(new byte[_8MB]);list.add(new byte[_8MB]);}

输出

[GC (Allocation Failure) [DefNew: 2154K->713K(9216K), 0.0013283 secs][Tenured: 8192K->8904K(10240K), 0.0016970 secs] 10346K->8904K(19456K), [Metaspace: 3286K->3286K(1056768K)], 0.0030726 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [Tenured: 8904K->8886K(10240K), 0.0012463 secs] 8904K->8886K(19456K), [Metaspace: 3286K->3286K(1056768K)], 0.0012659 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heapdef new generation   total 9216K, used 246K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)eden space 8192K,   3% used [0x00000000fec00000, 0x00000000fec3d890, 0x00000000ff400000)from space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)tenured generation   total 10240K, used 8886K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)the space 10240K,  86% used [0x00000000ff600000, 0x00000000ffeadba8, 0x00000000ffeadc00, 0x0000000100000000)Metaspace       used 3318K, capacity 4564K, committed 4864K, reserved 1056768Kclass space    used 364K, capacity 388K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat cn.itcast.jvm.t2.Demo2_1.main(Demo2_1.java:22)

可以看到当新生代和老年代内存都不够时,则会报错OOM

代码示例6(线程报错OOM会不会影响主线程)

    private static final int _512KB = 512 * 1024;private static final int _1MB = 1024 * 1024;private static final int _6MB = 6 * 1024 * 1024;private static final int _7MB = 7 * 1024 * 1024;private static final int _8MB = 8 * 1024 * 1024;// -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:-ScavengeBeforeFullGCpublic static void main(String[] args) throws InterruptedException {new Thread(() -> {ArrayList<byte[]> list = new ArrayList<>();list.add(new byte[_8MB]);list.add(new byte[_8MB]);}).start();System.out.println("sleep....");Thread.sleep(3000L);System.out.println("主线程结束!");}

输出

sleep....
[GC (Allocation Failure) [DefNew: 4723K->916K(9216K), 0.0023784 secs][Tenured: 8192K->9106K(10240K), 0.0029042 secs] 12915K->9106K(19456K), [Metaspace: 4202K->4202K(1056768K)], 0.0053451 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [Tenured: 9106K->9051K(10240K), 0.0031245 secs] 9106K->9051K(19456K), [Metaspace: 4202K->4202K(1056768K)], 0.0031630 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap spaceat cn.itcast.jvm.t2.Demo2_1.lambda$main$0(Demo2_1.java:27)at cn.itcast.jvm.t2.Demo2_1$$Lambda$1/1747585824.run(Unknown Source)at java.lang.Thread.run(Thread.java:745)
主线程结束!
Heapdef new generation   total 9216K, used 1411K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)eden space 8192K,  17% used [0x00000000fec00000, 0x00000000fed60e88, 0x00000000ff400000)from space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)tenured generation   total 10240K, used 9051K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)the space 10240K,  88% used [0x00000000ff600000, 0x00000000ffed6da8, 0x00000000ffed6e00, 0x0000000100000000)Metaspace       used 4732K, capacity 4866K, committed 4992K, reserved 1056768Kclass space    used 525K, capacity 559K, committed 640K, reserved 1048576K

可以看到一个线程内的OOM,不会影响到java进程的结束。


相关文章:
JVM内存结构

  • JVM内存结构笔记01-运行时数据区域
  • JVM内存结构笔记02-堆
  • JVM内存结构笔记03-方法区
  • JVM内存结构笔记04-字符串常量池
  • JVM内存结构笔记05-直接内存
  • JVM内存结构笔记06-HotSpot虚拟机对象探秘
  • JVM中常量池和运行时常量池、字符串常量池三者之间的关系

JVM垃圾回收

  • JVM垃圾回收笔记01-垃圾回收算法
  • JVM垃圾回收笔记02-垃圾回收器

JVM类加载与字节码

  • JVM类文件结构详解
  • JVM类加载过程详解

相关文章:

JVM垃圾回收笔记01-垃圾回收算法

文章目录 前言1. 如何判断对象可以回收1.1 引用计数法1.2 可达性分析算法查看根对象哪些对象可以作为 GC Root ?对象可以被回收&#xff0c;就代表一定会被回收吗&#xff1f; 1.3 引用类型1.强引用&#xff08;StrongReference&#xff09;2.软引用&#xff08;SoftReference…...

3.26学习总结

今天主要学习了内部类&#xff0c;但总感觉有点混乱&#xff0c;和之前的抽象啊&#xff0c;接口&#xff0c;多态等概念联系在一起感觉更混乱了&#xff0c;所以打算先把最近学的理清一遍&#xff0c;敲一遍代码再往后学...

京东--数据开发实习生--保险业务部门--一面凉经

Base&#xff1a; 本人投递的是后台开发岗位&#xff0c;调剂到数据开发岗位&#xff0c;京东的数据开发也做后台开发方面的工作&#xff0c;还包括算法、策略、数据挖掘和数据平台搭建之类的职责。面试内容基本只会问简历上的&#xff0c;在此基础上再去考察岗位职责相关的内…...

【Hugging Face 开源库】Diffusers 库 —— 扩散模型

Diffusers 的三个主要组件1. DiffusionPipeline&#xff1a;端到端推理工具__call__ 函数callback_on_step_end 管道回调函数 2. 预训练模型架构和模块UNetVAE&#xff08;Variational AutoEncoder&#xff09;图像尺寸与 UNet 和 VAE 的关系EMA&#xff08;Exponential Moving…...

TypeScript(TS) 的使用初识

我将详细讲解 TypeScript&#xff08;TS&#xff09; 的使用。TypeScript 是由微软开发的一种开源编程语言&#xff0c;它是 JavaScript 的超集&#xff0c;通过引入静态类型和面向对象编程特性&#xff0c;增强了 JavaScript 的开发体验和代码质量。TypeScript 最终会被编译成…...

QTcpSocket多线程连接慢问题

20250325记录 环境&#xff1a;Qt5.14.2 64位 msvc编译 在多线程环境下&#xff0c;使用QTcpSocket实现客户端&#xff0c;发现在少部分电脑上&#xff0c;连接时间过长&#xff0c;定时器检查套接字状态时&#xff0c;发现连接处于QAbstractSocket::ConnectingState状态。 …...

Vue的实例

Every Vue application starts with a single Vue component instance as the application root. Any other Vue component created in the same application needs to be nested inside this root component. 每个 Vue 应用都以一个 Vue 组件实例作为应用的根开始。在同一个应…...

[AI绘图] ComfyUI 中自定义节点插件安装方法

ComfyUI 是一个强大的 AI 图像生成工具,支持自定义节点插件扩展其功能。本文介绍 ComfyUI 中安装自定义节点插件的三种方法,包括 Git Clone 方式、插件管理器安装方式,以及手动解压 ZIP 文件的方法,并分析它们的优缺点。 1. Git Clone 方法 使用 git clone 是最稳定且推荐…...

数据库第二周作业

数据库约束、常见语句等 数据库约束 主键约束 #创建表&#xff0c;把id设为主键 mysql> create table test02(-> id int primary key, #----主键约束-> name varchar(50)-> ); Query OK, 0 rows affected (0.02 sec) ​ #插入数据测试 mysql> insert into te…...

Appium Inspector使用教程

1.下载最新版本 https://github.com/appium/appium-inspector/releases 2.本地启动一个Appium服务 若Android SDK已安装Appium服务&#xff0c;则在任意terminal使用appium启动服务即可 3.Appium Inspector客户端配置连接到Appium服务 Configuring and Starting a Session…...

【QT继承QLabel实现绘制矩形、椭圆、直线、多边形功能,并且支持修改大小,移动位置,复制,粘贴,删除功能】

文章目录 介绍绘制一个矩形&#xff08;椭圆&#xff09;roi绘制一个多边形roi对矩形roi的缩放&#xff1a;对多边形rio的缩放&#xff08;移动点的位置&#xff09; 介绍 绘制矩形&#xff0c;椭圆&#xff0c;直线实际用的都是是同一个思路&#xff1a;鼠标第一次点击就确定…...

Elasticsearch未授权访问漏洞

1、编辑elasticsearch.yml配置文件&#xff0c;添加认证相关配置 vim elasticsearch.ymlxpack.security.enabled: true xpack.license.self_generated.type: basic xpack.security.transport.ssl.enabled: true2、重启ElasticSearch # 重启方式可能略微不同 systemctl restar…...

怎么处理 Vue 项目中的错误的?

一、错误类型 任何一个框架,对于错误的处理都是一种必备的能力 在Vue 中,则是定义了一套对应的错误处理规则给到使用者,且在源代码级别,对部分必要的过程做了一定的错误处理。 主要的错误来源包括: 后端接口错误代码中本身逻辑错误二、如何处理 后端接口错误 通过axi…...

Elasticsearch原生linux部署集群 和docker部署集群

Easticsearch 是一个分布式的搜索和分析引擎&#xff0c;广泛应用于日志分析、全文检索、实时数据分析等场景。为了满足高可用性和高性能的需求&#xff0c;Elasticsearch 通常以集群的方式部署。部署 Elasticsearch 集群时&#xff0c;可以选择两种主要方式&#xff1a;原生 L…...

缓存设计模式

缓存设计模式&#xff08;Cache Design Pattern&#xff09;是一种用于存储和管理频繁访问数据的技术&#xff0c;旨在提高系统性能、降低数据库或后端服务的负载&#xff0c;并减少数据访问延迟。以下是几种常见的缓存设计模式&#xff0c;并用 Python Redis 进行示例代码实现…...

详解TCP的四次握手和三次挥手,以及里面每个阶段的状态

TCP 三次握手&#xff08;连接建立&#xff09; TCP 连接建立通过三次握手完成&#xff0c;确保双方同步初始序列号并确认可达性。 阶段说明 第一次握手 客户端 → 服务器&#xff1a;发送 SYN&#xff08;同步请求&#xff09;&#xff0c;携带初始序列号 seq x。客户端状态…...

Linux文件目录管理指令详解(上篇)

Linux文件目录管理指令详解&#xff08;上篇&#xff09; 在Linux操作系统中&#xff0c;文件目录管理是基础且重要的技能。通过一系列指令&#xff0c;用户可以高效地浏览、创建、修改和删除文件及目录。本文将详细介绍Linux中常用的文件目录管理类指令&#xff0c;包括pwd、…...

BCC-应用程序组件分析

libbpf-tools/gethostlatency 追踪glibc中的getaddrinfo、gethostbyname、gethostbyname2函数用时 # /usr/share/bcc/libbpf-tools/gethostlatency TIME PID COMM LATms HOST 14:58:32 8418 curl 313.635 www.taobao.com以# cur…...

无参数读文件和RCE

什么是无参数&#xff1f; 无参数&#xff08;No-Argument&#xff09;的概念&#xff0c;顾名思义&#xff0c;就是在PHP中调用函数时&#xff0c;不传递任何参数。我们需要利用仅靠函数本身的返回值或嵌套无参数函数的方式&#xff0c;达到读取文件或远程命令执行&#xff0…...

SpringMVC_day02

一、SSM 整合 核心步骤 依赖管理 包含 SpringMVC、Spring JDBC、MyBatis、Druid 数据源、Jackson 等依赖。注意点&#xff1a;确保版本兼容性&#xff08;如 Spring 5.x 与 MyBatis 3.5.x&#xff09;。 配置类 SpringConfig&#xff1a;扫描 Service 层、启用事务管理、导入…...

在Linux、Windows系统上安装开源InfluxDB——InfluxDB OSS v2并设置开机自启的保姆级图文教程

一、进入InfluxDB下载官网 InfluxData 文档https://docs.influxdata.com/Install InfluxDB OSS v2 | InfluxDB OSS v2 Documentation...

LinkedIn数据抓取零风险指南:亮数据住宅代理实现企业级合规采集

亮数据住宅代理实现企业级合规采集 一、前言二、尝试使用三、使用体验高效稳定易用性&#xff1a;合规与安全&#xff1a;技术支持&#xff1a; 四、适用场景五、推荐程度六、试用地址 一、前言 最近一位猎头小伙伴找到我&#xff0c;说目前很多公司的出海业务都在招人&#x…...

ROS2的发展历史、核心架构和应用场景

以下是对**ROS2&#xff08;Robot Operating System 2&#xff09;**的发展历史、核心架构和应用场景的详细解析&#xff0c;覆盖其技术演变、关键特性和生态系统&#xff1a; 一、ROS2的诞生背景&#xff1a;从ROS1到ROS2 1. ROS1的历史与局限 ROS1的起源&#xff1a; 2007年…...

PHP eval 长度限制绕过与 Webshell 获取

在 PHP 代码中&#xff0c;如果 eval($param); 存在且长度受限&#xff0c;并且过滤了 eval 和 assert&#xff0c;仍然可以通过多种方法绕过限制&#xff0c;获取 Webshell。 源码 <?php $param $_REQUEST[param]; if(strlen($param)<17 && stripos($param,…...

自然语言处理(14:处理时序数据的层的实现)

系列文章目录 第一章 1:同义词词典和基于计数方法语料库预处理 第一章 2:基于计数方法的分布式表示和假设&#xff0c;共现矩阵&#xff0c;向量相似度 第一章 3:基于计数方法的改进以及总结 第二章 1:word2vec 第二章 2:word2vec和CBOW模型的初步实现 第二章 3:CBOW模型…...

Pytest的Fixture使用

概述 Pytest中的Fixture可以使得测试代码高度复用,同时对资源进行安全的管理,以及在复杂的测试场景用进行灵活的组合。 概念 Fixture:可重用的函数,用@pytest.fixture来进行装饰,用于为测试提供数据、环境或者服务作用域:控制Fixture的生命周期,默认是function,可设置…...

【蓝桥杯】每日练习 Day13

前言 今天做了不少题&#xff0c;但是感觉都太水了&#xff0c;深思熟虑之下主播决定拿出两道相对不那么水的题来说一下&#xff08;其实还是很水&#xff09;。 两道问题&#xff0c;一道是日期问题&#xff08;模拟&#xff09;&#xff0c;一道是区间合并问题。 日期差值 …...

Vue3 中使用 vuedraggable 实现拖拽排序功能,分组拖拽

Vue3 中使用 vuedraggable 实现拖拽排序功能&#xff0c;分组拖拽 安装draggable npm install vuedraggablenext --save基础用法示例 <template><div class"app-container"><draggable v-model"list" item-key"id":group"…...

husky的简介以及如果想要放飞自我的解决方案

husky 是一个 Git Hooks 管理工具&#xff0c;它的主要作用是 在 Git 提交&#xff08;commit&#xff09;、推送&#xff08;push&#xff09;等操作时执行自定义脚本&#xff0c;比如代码检查&#xff08;Lint&#xff09;、单元测试&#xff08;Test&#xff09;、格式化代码…...

Maven工具学习使用(四)——仓库

仓库分类 对于Mavne来说,仓库只分为两类:本地仓库和远程仓库。当Maven根据坐标查询寻找构件的时候,它首先会查看本地仓库,如果本地仓库存在此构件,则直接使用;如果本地仓库不存在此构件,或者需要查看是否有更新的构件版本,Maven就会去远程仓库查找,发现需要的构件之后…...

【BFS】《单源、多源 BFS:图搜索算法的双生力量》

文章目录 前言单源BFS例题一、迷宫中离入口最近的出口二、 最小基因变化三、单词接龙四、为高尔夫比赛砍树 多源BFS例题一、 01 矩阵二、飞地的数量三、地图中的最高点四、地图分析 结语 前言 什么是单源、多源BFS算法问题呢&#xff1f; BFS&#xff08;Breadth - First Sear…...

批量取消 PDF 文档中的所有超链接

在 PDF 文档中我们可以插入各种各样的文本也可以给文本设置字体&#xff0c;颜色等多种样式&#xff0c;同时还可以给文字或者图片添加上超链接&#xff0c;当我们点击超链接之后&#xff0c;就会跳转到对应的网页。有时候这会对我们的阅读或者使用形成一定的干扰&#xff0c;今…...

13.2 kubelet containerRuntime接口定义和初始化

本节重点总结 &#xff1a; containerRuntime 需要实现3类接口 管理容器的接口管理镜像的接口Streaming API 用于客户端与容器进行交互 type KubeGenericRuntime interface {kubecontainer.Runtimekubecontainer.StreamingRuntimekubecontainer.CommandRunner }containerRun…...

使用 gone.WrapFunctionProvider 快速接入第三方服务

项目地址&#xff1a;https://github.com/gone-io/gone 本文中源代码&#xff1a; esexamples/es 文章目录 1. gone.WrapFunctionProvider 简介2. 配置注入实现3. 实战示例&#xff1a;Elasticsearch 集成4. 使用方式5. 最佳实践6. 总结 在如何给Gone框架编写Goner组件&#xf…...

git 标签学习笔记

目录 轻量级标签 带注释的标签&#xff08;推荐&#xff09; 给指定 commit 打标签 推送单个标签&#xff0c;需要单独推送&#xff0c;代码推送不会推送标签 推送所有标签 删除标签 轻量级标签 git tag v1.0.0 只是简单地给当前 commit 打上 v1.0.0 标签。 带注释的标…...

【论文阅读】基于思维链提示的大语言模型软件漏洞发现与修复方法研究

这篇文章来自于 Chain-of-Thought Prompting of Large Language Models for Discovering and Fixing Software Vulnerabilities 摘要 软件安全漏洞在现代系统中呈现泛在化趋势&#xff0c;其引发的社会影响日益显著。尽管已有多种防御技术被提出&#xff0c;基于深度学习&…...

企业在人工智能创新与安全之间走钢丝

2025 年全球 AI/ML 工具使用量将激增&#xff0c;企业将 AI 融入运营之中&#xff0c;员工也将 AI 嵌入日常工作流程中。报告显示&#xff0c;企业对 AI/ML 工具的使用同比增长 3,000% 以上&#xff0c;凸显了各行各业迅速采用 AI 技术&#xff0c;以提升生产力、效率和创新水平…...

CSS动画

目录 一、核心概念与语法 1. keyframes 关键帧 2. animation 属性 二、动画调速函数&#xff08;animation-timing-function&#xff09; 1. 预设值 2. 贝塞尔曲线 3. 步进函数&#xff08;steps()&#xff09; 三、动画控制与交互 1. 暂停与恢复 2. JavaScript 控制…...

计算机视觉(CV)技术的优势和挑战

计算机视觉&#xff08;CV&#xff09;技术是人工智能领域中的一个重要分支&#xff0c;它主要通过让机器学会“看”和“理解”图像或视频来模拟人类视觉系统。以下是计算机视觉技术的一些优势和挑战&#xff1a; 优势&#xff1a; 自动化&#xff1a;计算机视觉技术可以实现…...

动态IP与静态IP该如何选?

一、当IP地址成为"网络身份" 2023年亚马逊封号潮中&#xff0c;某杭州卖家因登录IP频繁切换&#xff08;早8点在纽约&#xff0c;午间瞬移到东京&#xff09;&#xff0c;触发平台风控导致账号冻结。这类"时空错乱症"揭示了跨境电商的生存法则&#xff1a…...

Vue.js 完全指南:从入门到精通

1. Vue.js 简介 1.1 什么是 Vue.js? Vue.js(通常简称为 Vue)是一个用于构建用户界面的渐进式 JavaScript 框架。所谓"渐进式",意味着 Vue 的设计是由浅入深的,你可以根据自己的需求选择使用它的一部分或全部功能。 Vue 最初由尤雨溪(Evan You)在 2014 年创…...

《TypeScript 7天速成系列》第3天:TypeScript高级类型通关秘籍:泛型+联合+交叉类型实战

TypeScript 的类型系统是其最强大的特性之一&#xff0c;但也是许多开发者感到困惑的地方。今天我们就来破解 TypeScript 中最难的类型系统&#xff0c;掌握泛型、联合类型和交叉类型的使用技巧。 一、泛型函数与泛型接口 泛型是 TypeScript 中创建可重用组件的重要工具&…...

Python----数据分析(足球运动员数据分析)

一、数据展示 1.1、数据 1.2、列名 字段名备注Name姓名Nationality国籍National_Position国家队位置National_Kit国家队号码Club所在俱乐部Club_Position所在俱乐部位置Club_Kit俱乐部号码Club_Joining加入俱乐部时间Contract_Expiry合同到期时间Rating评分Height身高Weight体…...

音视频 三 看书的笔记 MediaPlayer的C/S架构

MediaPlayer在运行时分为Client和Server两部分 Client层‌&#xff1a;位于Java层&#xff0c;用户通过调用Java层的API&#xff08;如setDataSource&#xff09;来操作MediaPlayer。 Server层‌&#xff1a;位于C层&#xff0c;负责实际的媒体处理工作。Server层通过Binder机…...

Elasticsearch:使用 AI SDK 和 Elastic 构建 AI 代理

作者&#xff1a;来自 Elastic Carly Richmond 你是否经常听到 AI 代理&#xff08;AI agents&#xff09;这个词&#xff0c;但不太确定它们是什么&#xff0c;或者如何在 TypeScript&#xff08;或 JavaScript&#xff09;中构建一个&#xff1f;跟我一起深入了解 AI 代理的概…...

echarts添加坐标轴点击事件

echarts添加坐标轴点击事件 chart.on(click, (params) > {if(params.componentType yAxis && this.type ! 1){console.log(params);// 检查是否点击了系列数据console.log(你点击了 ${params.name} 的数据点&#xff0c;值为 ${params.value}); this.$bus.$emi…...

如何在linux中部署dns服务 主备dns (详细全过程)

环境centos 7.9 主DNS&#xff1a;192.168.60.131 备DNS&#xff1a;192.168.60.134 我以 chenxingyu0.com 指向 192.168.60.200为例 首先是主dns #!/bin/bash# 检查是否为 root 用户 if [ "$(id -u)" ! "0" ]; thenecho "请使用…...

GitLab 中文版17.10正式发布,27项重点功能解读【二】

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料&#xff1a; 极狐GitLab 官网极狐…...

matplotlib——南丁格尔玫瑰

南丁格尔玫瑰图&#xff08;Nightingale Rose Chart&#xff09;&#xff0c;是一种特殊形式的柱状图&#xff0c;它以南丁格尔&#xff08;Florence Nightingale&#xff09;命名&#xff0c;她在1858年首次使用这种图表来展示战争期间士兵死亡原因的数据。 它将数据绘制在极坐…...

WPF 与 C# 融合开发:从基础到高级应用(一)

WPF 与 C# 融合开发&#xff1a;从基础到高级应用 一、C# 语言基础回顾 1.1 C# 语言概述 C# 是微软开发的一种现代、面向对象的编程语言&#xff0c;它融合了 C、C 和 Java 等语言的优点&#xff0c;具有简洁、安全、高效等特点。C# 广泛应用于 Windows 平台的应用开发&…...