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 版本之前,堆内存被通常分为下面三部分:
- 新生代内存(Young Generation)
- 老生代(Old Generation)
- 永久代(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...");}
}
操作以下步骤
- 运行程序,停留在mark 1
- jps查看进程pid
- 使用以下命令执行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二进制文件) - 程序继续向下执行到mark 2,使用上面命令执行mark 2时的堆对象快照
- 打开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不是只使用以上一种算法,根据情况使用。具体使用的是分代垃圾回收。
- 新的对象首先分配在伊甸园区域
- 当新生代空间不足时,触发 minor gc(新生代垃圾回收,通过可达性分析算法标记),采用复制算法,将伊甸园存活的对象使用 copy 复制到 to 中,存活的对象年龄加1,并且交换 from和to区域的位置。
- 当第二次新生代空间不足时,触发 minor gc,将伊甸园和 from 存活的对象使用 copy 复制到 to 中,存活的对象年龄加 1并且交换 from to
- …
- minor gc 会引发 stop the world,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行
- 当对象寿命超过阈值时,会晋升至老年代,阈值的最大寿命是15(因为是保持在对象头中,所以为4bit)
- 当老年代空间不足,会先尝试触发 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 ?对象可以被回收,就代表一定会被回收吗? 1.3 引用类型1.强引用(StrongReference)2.软引用(SoftReference…...
3.26学习总结
今天主要学习了内部类,但总感觉有点混乱,和之前的抽象啊,接口,多态等概念联系在一起感觉更混乱了,所以打算先把最近学的理清一遍,敲一遍代码再往后学...
京东--数据开发实习生--保险业务部门--一面凉经
Base: 本人投递的是后台开发岗位,调剂到数据开发岗位,京东的数据开发也做后台开发方面的工作,还包括算法、策略、数据挖掘和数据平台搭建之类的职责。面试内容基本只会问简历上的,在此基础上再去考察岗位职责相关的内…...
【Hugging Face 开源库】Diffusers 库 —— 扩散模型
Diffusers 的三个主要组件1. DiffusionPipeline:端到端推理工具__call__ 函数callback_on_step_end 管道回调函数 2. 预训练模型架构和模块UNetVAE(Variational AutoEncoder)图像尺寸与 UNet 和 VAE 的关系EMA(Exponential Moving…...
TypeScript(TS) 的使用初识
我将详细讲解 TypeScript(TS) 的使用。TypeScript 是由微软开发的一种开源编程语言,它是 JavaScript 的超集,通过引入静态类型和面向对象编程特性,增强了 JavaScript 的开发体验和代码质量。TypeScript 最终会被编译成…...
QTcpSocket多线程连接慢问题
20250325记录 环境:Qt5.14.2 64位 msvc编译 在多线程环境下,使用QTcpSocket实现客户端,发现在少部分电脑上,连接时间过长,定时器检查套接字状态时,发现连接处于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 是最稳定且推荐…...
数据库第二周作业
数据库约束、常见语句等 数据库约束 主键约束 #创建表,把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服务,则在任意terminal使用appium启动服务即可 3.Appium Inspector客户端配置连接到Appium服务 Configuring and Starting a Session…...
【QT继承QLabel实现绘制矩形、椭圆、直线、多边形功能,并且支持修改大小,移动位置,复制,粘贴,删除功能】
文章目录 介绍绘制一个矩形(椭圆)roi绘制一个多边形roi对矩形roi的缩放:对多边形rio的缩放(移动点的位置) 介绍 绘制矩形,椭圆,直线实际用的都是是同一个思路:鼠标第一次点击就确定…...
Elasticsearch未授权访问漏洞
1、编辑elasticsearch.yml配置文件,添加认证相关配置 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 是一个分布式的搜索和分析引擎,广泛应用于日志分析、全文检索、实时数据分析等场景。为了满足高可用性和高性能的需求,Elasticsearch 通常以集群的方式部署。部署 Elasticsearch 集群时,可以选择两种主要方式:原生 L…...
缓存设计模式
缓存设计模式(Cache Design Pattern)是一种用于存储和管理频繁访问数据的技术,旨在提高系统性能、降低数据库或后端服务的负载,并减少数据访问延迟。以下是几种常见的缓存设计模式,并用 Python Redis 进行示例代码实现…...
详解TCP的四次握手和三次挥手,以及里面每个阶段的状态
TCP 三次握手(连接建立) TCP 连接建立通过三次握手完成,确保双方同步初始序列号并确认可达性。 阶段说明 第一次握手 客户端 → 服务器:发送 SYN(同步请求),携带初始序列号 seq x。客户端状态…...
Linux文件目录管理指令详解(上篇)
Linux文件目录管理指令详解(上篇) 在Linux操作系统中,文件目录管理是基础且重要的技能。通过一系列指令,用户可以高效地浏览、创建、修改和删除文件及目录。本文将详细介绍Linux中常用的文件目录管理类指令,包括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
什么是无参数? 无参数(No-Argument)的概念,顾名思义,就是在PHP中调用函数时,不传递任何参数。我们需要利用仅靠函数本身的返回值或嵌套无参数函数的方式,达到读取文件或远程命令执行࿰…...
SpringMVC_day02
一、SSM 整合 核心步骤 依赖管理 包含 SpringMVC、Spring JDBC、MyBatis、Druid 数据源、Jackson 等依赖。注意点:确保版本兼容性(如 Spring 5.x 与 MyBatis 3.5.x)。 配置类 SpringConfig:扫描 Service 层、启用事务管理、导入…...
在Linux、Windows系统上安装开源InfluxDB——InfluxDB OSS v2并设置开机自启的保姆级图文教程
一、进入InfluxDB下载官网 InfluxData 文档https://docs.influxdata.com/Install InfluxDB OSS v2 | InfluxDB OSS v2 Documentation...
LinkedIn数据抓取零风险指南:亮数据住宅代理实现企业级合规采集
亮数据住宅代理实现企业级合规采集 一、前言二、尝试使用三、使用体验高效稳定易用性:合规与安全:技术支持: 四、适用场景五、推荐程度六、试用地址 一、前言 最近一位猎头小伙伴找到我,说目前很多公司的出海业务都在招人&#x…...
ROS2的发展历史、核心架构和应用场景
以下是对**ROS2(Robot Operating System 2)**的发展历史、核心架构和应用场景的详细解析,覆盖其技术演变、关键特性和生态系统: 一、ROS2的诞生背景:从ROS1到ROS2 1. ROS1的历史与局限 ROS1的起源: 2007年…...
PHP eval 长度限制绕过与 Webshell 获取
在 PHP 代码中,如果 eval($param); 存在且长度受限,并且过滤了 eval 和 assert,仍然可以通过多种方法绕过限制,获取 Webshell。 源码 <?php $param $_REQUEST[param]; if(strlen($param)<17 && stripos($param,…...
自然语言处理(14:处理时序数据的层的实现)
系列文章目录 第一章 1:同义词词典和基于计数方法语料库预处理 第一章 2:基于计数方法的分布式表示和假设,共现矩阵,向量相似度 第一章 3:基于计数方法的改进以及总结 第二章 1:word2vec 第二章 2:word2vec和CBOW模型的初步实现 第二章 3:CBOW模型…...
Pytest的Fixture使用
概述 Pytest中的Fixture可以使得测试代码高度复用,同时对资源进行安全的管理,以及在复杂的测试场景用进行灵活的组合。 概念 Fixture:可重用的函数,用@pytest.fixture来进行装饰,用于为测试提供数据、环境或者服务作用域:控制Fixture的生命周期,默认是function,可设置…...
【蓝桥杯】每日练习 Day13
前言 今天做了不少题,但是感觉都太水了,深思熟虑之下主播决定拿出两道相对不那么水的题来说一下(其实还是很水)。 两道问题,一道是日期问题(模拟),一道是区间合并问题。 日期差值 …...
Vue3 中使用 vuedraggable 实现拖拽排序功能,分组拖拽
Vue3 中使用 vuedraggable 实现拖拽排序功能,分组拖拽 安装draggable npm install vuedraggablenext --save基础用法示例 <template><div class"app-container"><draggable v-model"list" item-key"id":group"…...
husky的简介以及如果想要放飞自我的解决方案
husky 是一个 Git Hooks 管理工具,它的主要作用是 在 Git 提交(commit)、推送(push)等操作时执行自定义脚本,比如代码检查(Lint)、单元测试(Test)、格式化代码…...
Maven工具学习使用(四)——仓库
仓库分类 对于Mavne来说,仓库只分为两类:本地仓库和远程仓库。当Maven根据坐标查询寻找构件的时候,它首先会查看本地仓库,如果本地仓库存在此构件,则直接使用;如果本地仓库不存在此构件,或者需要查看是否有更新的构件版本,Maven就会去远程仓库查找,发现需要的构件之后…...
【BFS】《单源、多源 BFS:图搜索算法的双生力量》
文章目录 前言单源BFS例题一、迷宫中离入口最近的出口二、 最小基因变化三、单词接龙四、为高尔夫比赛砍树 多源BFS例题一、 01 矩阵二、飞地的数量三、地图中的最高点四、地图分析 结语 前言 什么是单源、多源BFS算法问题呢? BFS(Breadth - First Sear…...
批量取消 PDF 文档中的所有超链接
在 PDF 文档中我们可以插入各种各样的文本也可以给文本设置字体,颜色等多种样式,同时还可以给文字或者图片添加上超链接,当我们点击超链接之后,就会跳转到对应的网页。有时候这会对我们的阅读或者使用形成一定的干扰,今…...
13.2 kubelet containerRuntime接口定义和初始化
本节重点总结 : containerRuntime 需要实现3类接口 管理容器的接口管理镜像的接口Streaming API 用于客户端与容器进行交互 type KubeGenericRuntime interface {kubecontainer.Runtimekubecontainer.StreamingRuntimekubecontainer.CommandRunner }containerRun…...
使用 gone.WrapFunctionProvider 快速接入第三方服务
项目地址:https://github.com/gone-io/gone 本文中源代码: esexamples/es 文章目录 1. gone.WrapFunctionProvider 简介2. 配置注入实现3. 实战示例:Elasticsearch 集成4. 使用方式5. 最佳实践6. 总结 在如何给Gone框架编写Goner组件…...
git 标签学习笔记
目录 轻量级标签 带注释的标签(推荐) 给指定 commit 打标签 推送单个标签,需要单独推送,代码推送不会推送标签 推送所有标签 删除标签 轻量级标签 git tag v1.0.0 只是简单地给当前 commit 打上 v1.0.0 标签。 带注释的标…...
【论文阅读】基于思维链提示的大语言模型软件漏洞发现与修复方法研究
这篇文章来自于 Chain-of-Thought Prompting of Large Language Models for Discovering and Fixing Software Vulnerabilities 摘要 软件安全漏洞在现代系统中呈现泛在化趋势,其引发的社会影响日益显著。尽管已有多种防御技术被提出,基于深度学习&…...
企业在人工智能创新与安全之间走钢丝
2025 年全球 AI/ML 工具使用量将激增,企业将 AI 融入运营之中,员工也将 AI 嵌入日常工作流程中。报告显示,企业对 AI/ML 工具的使用同比增长 3,000% 以上,凸显了各行各业迅速采用 AI 技术,以提升生产力、效率和创新水平…...
CSS动画
目录 一、核心概念与语法 1. keyframes 关键帧 2. animation 属性 二、动画调速函数(animation-timing-function) 1. 预设值 2. 贝塞尔曲线 3. 步进函数(steps()) 三、动画控制与交互 1. 暂停与恢复 2. JavaScript 控制…...
计算机视觉(CV)技术的优势和挑战
计算机视觉(CV)技术是人工智能领域中的一个重要分支,它主要通过让机器学会“看”和“理解”图像或视频来模拟人类视觉系统。以下是计算机视觉技术的一些优势和挑战: 优势: 自动化:计算机视觉技术可以实现…...
动态IP与静态IP该如何选?
一、当IP地址成为"网络身份" 2023年亚马逊封号潮中,某杭州卖家因登录IP频繁切换(早8点在纽约,午间瞬移到东京),触发平台风控导致账号冻结。这类"时空错乱症"揭示了跨境电商的生存法则:…...
Vue.js 完全指南:从入门到精通
1. Vue.js 简介 1.1 什么是 Vue.js? Vue.js(通常简称为 Vue)是一个用于构建用户界面的渐进式 JavaScript 框架。所谓"渐进式",意味着 Vue 的设计是由浅入深的,你可以根据自己的需求选择使用它的一部分或全部功能。 Vue 最初由尤雨溪(Evan You)在 2014 年创…...
《TypeScript 7天速成系列》第3天:TypeScript高级类型通关秘籍:泛型+联合+交叉类型实战
TypeScript 的类型系统是其最强大的特性之一,但也是许多开发者感到困惑的地方。今天我们就来破解 TypeScript 中最难的类型系统,掌握泛型、联合类型和交叉类型的使用技巧。 一、泛型函数与泛型接口 泛型是 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层:位于Java层,用户通过调用Java层的API(如setDataSource)来操作MediaPlayer。 Server层:位于C层,负责实际的媒体处理工作。Server层通过Binder机…...
Elasticsearch:使用 AI SDK 和 Elastic 构建 AI 代理
作者:来自 Elastic Carly Richmond 你是否经常听到 AI 代理(AI agents)这个词,但不太确定它们是什么,或者如何在 TypeScript(或 JavaScript)中构建一个?跟我一起深入了解 AI 代理的概…...
echarts添加坐标轴点击事件
echarts添加坐标轴点击事件 chart.on(click, (params) > {if(params.componentType yAxis && this.type ! 1){console.log(params);// 检查是否点击了系列数据console.log(你点击了 ${params.name} 的数据点,值为 ${params.value}); this.$bus.$emi…...
如何在linux中部署dns服务 主备dns (详细全过程)
环境centos 7.9 主DNS:192.168.60.131 备DNS: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 平台,很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版,专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料: 极狐GitLab 官网极狐…...
matplotlib——南丁格尔玫瑰
南丁格尔玫瑰图(Nightingale Rose Chart),是一种特殊形式的柱状图,它以南丁格尔(Florence Nightingale)命名,她在1858年首次使用这种图表来展示战争期间士兵死亡原因的数据。 它将数据绘制在极坐…...
WPF 与 C# 融合开发:从基础到高级应用(一)
WPF 与 C# 融合开发:从基础到高级应用 一、C# 语言基础回顾 1.1 C# 语言概述 C# 是微软开发的一种现代、面向对象的编程语言,它融合了 C、C 和 Java 等语言的优点,具有简洁、安全、高效等特点。C# 广泛应用于 Windows 平台的应用开发&…...