JVM虚拟机

1.运行时数据区

1.1程序计数器

用来存储指向下一条指令的地址。由执行引擎读取下一条指令。
  1. 线程私有的。

  2. 它是一块很小的内存空间。

  3. 程序计数器会存储当前线程正在执行的Java方法的JVM指令地址,或者,如果在执行native方法,则是未指定值(undefined).X

  4. 为什么使用PC寄存器记录当前线程的执行地址?

    因为CPU需要不停的切换各个线程,切换回来以后,就得知道从哪开始继续执行。
    

1.2虚拟机栈

1.Java虚拟机栈是什么?
  它是线程私有的,其内部保存一个个的栈帧(一个栈帧对应着一个方法)
2.生命周期与线程保持一致。
3.作用:
  主管Java程序的运行,它保存方法的局部变量,部分结果,并参与方法的调用和返回
4.对java栈的操作只有两个(入栈,出栈),所以访问速度仅次于程序计数器,对于栈
  来说不存在垃圾回收问题。
5.设置栈内存大小
  使用-Xss设置线程的最大栈空间,栈的大小直接决定了函数调用的最大可达深度。
6.栈帧被弹出有两种方式:
  1. 正常的函数返回,使用return指令
  2. 抛出异常。
7.栈帧的内部结构包括局部变量表,操作数栈,动太链接,方法返回地址,
一些附加信息。

1.2.1局部变量表

1.局部变量表:定义为一个数字数组,主要用于存储方法参数和定义在方法
体内的局部变量,包括各种基本类型和对象引用,以及returnAddress类型。

2.Slot(变量槽):作为局部变量表中最基本的存储单元,32位以内的类型只占用一个
slot,64位类型(long 和double)占用两个slot。

3.如果当前帧是由实例方法或构造方法创建的,那么该对象引用this将会存放在
index为0的slot处,其余的参数按照参数表顺序继续排序。

4.帧中的局部变量表中的槽位是可以重用的,如果一个局部变量表过了其作用
域,那么在其作用域之后申明的新的局部变量就很有可能会复用过期的局部变量的槽位,
从而达到节省资源的目的。

5.局部变量表中的变量也是重要的垃圾回收根节点,只要被局部变量表中直接或间接
引用的对象都不会被回收。

1.2.2操作数栈

1.是用数组来实现的(局部变量表也是数组结构)
2.每一个独立的栈帧中除了包含局部变量表之外,还包含一个后进先出的操作数栈。
3.在方法执行过程中,根据字节码指令,往栈中写入数据或提取数据,叫入栈和出栈。
4.主要用于保存计算过程的中间结果,作为计算过程变量临时的存储空间。
5.它是jvm执行引擎的一个工作区。
6.它并非采用访问索引的方式进行数据访问的,而是通过入栈和出栈。
7. stack=1, locals=1, args_size=1(其中stack表示最大操作数栈深度)
public int fun1() {
    int a = 3;
    int b = 4;
    int c = a + b;
    return c;
}
public void fun2() {
    int r = fun1();
}
0 iconst_3 常量3压入操作数栈
1 istore_1  弹出操作数栈栈顶元素,保存到局部变量表第1个位置
2 iconst_4 常量4压入操作数栈
3 istore_2 弹出操作数栈栈顶元素,保存到局部变量表第2个位置
4 iload_1 将第1个变量压入操作数栈
5 iload_2 将第2个变量压入操作数栈
6 iadd 操作数栈中的前两个int相加,并将结果压入操作数栈顶
7 istore_3 弹出操作数栈栈顶元素,保存到局部变量表第3个位置
8 iload_3 加载局部变量表的第3个变量到操作数栈顶
9 ireturn 返回

1.2.2动态链接

每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用。
动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用

1.2.3方法的调用

1.静态连接:目标方法在编译期可知
2.动态连接:.被调用的方法在编译期无法被确定下来。
3.invokevirtual:调用所有虚方法(final修饰的除外)
4.invokestatic, invokespecial, invokevirtual, invokeinterface
5.动态调用指令,invokeddynamic(动态解析出需要调用的方法,然后执行)
6.为了提高性能,JVM采用在类的方法区建立一个虚方法表(virtual method table)
(非虚方法不会出现在表中)来实现,使用索引表来代替查找。

1.2.4方法返回地址

1.存放调用该方法的pc寄存器的值
2.方法正常退出时,调用者的PC计数器的值作为返回地址,即调用该方法的指令的下一条
 指令的地址。

在这里插入图片描述

1.3 本地方法

一个Native Method就是一个java调用非java代码的接口。本地接口的作用是融合不同的
编程语言为java所用,它的初忠是融合C/C++程序。

1.4 本地方法栈

Java虚拟机栈是管理java方法的调用,本地方法栈是管理本地方法方法的调用。
本地方法栈也是线程私有的

1.5 堆

概述:一个JVM实例只存在一个堆内存,堆也是java内存管理的核心区域。JAVA堆区在JVM启动的时候即被创建,其空间大小也就确定了。是JVM管理的最大一块内存空间。堆内存大写是可以调节的。堆可以是处于物理上不连续的内存空间,但在逻辑上它应该被视为连续。所有的线程共享Java堆, 在这里还可以划分线程私有缓冲区(Thread Local Allocation Buffer, TLAB)

1.- Xms10m(初始堆内存大小10m) -Xmx(最大堆内存10m)

1.5.1内存细分

Java7及之前堆内存逻辑上分为三部分:新生区+养老区+永久区
Java8及之前堆内存逻辑上分为三部分:新生区+养老区+元空间
通过将-Xms 和-Xmx两个参数配置相同的值,其目的是为了能够在Java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提高性能。
默认情况下,初始内存大小,物理电脑内存大小/64
最大内存大小,物理电脑内存大小/4

  1. jps (查看进程号)
  2. jstat -gc 进程号 (查看内存情况)

在这里插入图片描述

  1. -XX:+PrintGCDetails
    在这里插入图片描述
  2. Exception in thread “main” java.lang.OutOfMemoryError: Java heap space

1.5.2年轻代与老年代

  1. 年轻代可以划分为Eden空间,Survivor0空间和Survivor1空间(有时也叫from区和to区)
  2. 默认-XX:NewRatio=2, 表示新生代占1,老年代占2,新生代占整个堆的1/3
  3. 在HotSpot中,Eden空间和另外两个Survivor空间缺省所占的比例是8:1:1,可以通过选项-XX:SurvivorRatio调整这个比例空间。比如: -XX:SurvivorRatio=8
  4. 自适应策略:-XX:+UseAdaptivesSizePolicy(暂时用不到)
  5. -Xmn 设置新生代最大内存大小,一般不设置
  6. 啥时候能去养老区呢,可以设置次数 -XX:MaxTenuringThreshold=N 默认值为15
  7. 针对幸存者s0,s1的总结:复制之后有交换,谁空谁是to
  8. 关于垃圾回收:频繁在新生区收集, 很少在养老区收集,几乎不在永久区/元空间收集。

1.5.3 Minor GC、Major GC 、Full GC

  1. Minor GC/Young GC,只是新生代的垃圾收集。
  2. 老年代收集(Major GC / Old GC):只是老年代的垃圾收集。
  3. Full GC 整堆收集,收集整个java堆和方法区的垃圾收集。
  4. 很多时候,Major GC会和Full GC 混淆使用,需要具体分辨是老年代回收还是整堆回收。
    在这里插入图片描述

1.5.4 为什么有(Thread Local Allocation Buffer)?

1. 堆区是线程共享区域,任何线程都可以访问到堆区中的共享数据
2. 由于对象实例的创建在JVM中非常频繁,因此在并发环境下从堆区中划分内存空间是线程不安全的
3. 为了避免多个线程操作同一个地址,需要使用加锁等机制,进而影响分配速度。

默认情况下,TLAB空间的内存非常小,仅占整个Eden空间的1%。

1.5.5 堆空间中常用的参数

  1. -XX:+PrintFlagsInitial 查看所有参数的默认值
  2. -XX:+PrintFlagsFinal 查看所有参数的最终值(可能会存在修改, 不再是初始值)
  3. -Xms 初始堆空间内存(默认物理内存1/64)
  4. -Xmx 最大堆空间内存(默认物理内存1/64)
  5. -Xmn 设置新生代的大小(初始值及最大值)
  6. -XX:NewRatio 配置新生代与老年代在堆空间的占比
  7. -XX:SurvivorRatio 设置新生代中Eden和s0/s1空间的比例
  8. -XX:MaxTenuringThreshold 设置新生代垃圾的最大年龄
  9. -XX:+PrintGCDetails 输出详细的GC处理日志
  10. -XX:+PrintGC -verbose:gc
  11. -XX:HandlePromotionFailure 是否设置空间分配担保 (JDK6后不再支持)
    空间担保策略:只要老年代连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC, 否则将进行Full GC。

1.5.6 逃逸分析

当一个对象在方法中被定义后,对象只在方法内部使用,则认为没有发生逃逸。
当一个对象在方法中被定义后,它被外部方法所引用,则认为发生逃逸。例如作为调用参数传递到其他地方中。
在这里插入图片描述
哪些情况逃逸了:成员变量赋值,方法返回值,实例引用传递。
代码优化:

  1. 栈上分配:将堆分配转化成栈分配。如果一个对象在子程序中被分配,要使指向该对象的指针永远不会逃逸,对象可能是栈分配的候选,而不是堆分配。
  2. 同步省略:如果一个对象被发现只能从一个线程被访问到,那么对于这个对象的操作可以不考虑同步。
  3. 分离对象或标题替换:有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分或全部可以不存在内存,而是存储在CPU寄存器上。参数:-XX:+EliminateAllocations 开启标量替换(默认打开),允许将对象打散分配在栈上。
    在这里插入图片描述

1.6 方法区

  1. 方法区与java堆一样,是各个线程共享的内存区域。
  2. 方法区在jvm启动时候被创建,并且它的实际的物理内存空间中java堆区一样都可以是不连续的。
  3. 方法区的大小,跟堆空间一样,可以选择固定大小或者可扩展。
  4. 方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误:java.lang.OutOfMemoryError:PermGen space 或者java.lang.OutOfMemoryError:Metaspace
  5. 关闭JVM就会释放这个区域的内存。
  6. 元空间与永久代最大的区别在于:元空间不在虚拟机设置的内存中,而是使用本地内存。
  7. 元空间设置大小 MetaspaceSize, window 下,是21M, -XX:MetaspaceSize的值 为-1, 即没有限制。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

1.6.1 方法区的内部结构

方法区的存储内容如下:它用于存储已被虚拟机加载的类型信息,常量,静态变量,即时编译器编译后的代码缓存等。
在这里插入图片描述
全局常量:static final
被声明为final的类变量的处理方法则不同,每个全局变量在编译的时候就被分配了

public static int cout = 1;
public static final int = 2;

运行时常量池
一个有效的字节码文件中除了包含类的版本信息,字段,方法以及接口等描述信息外,还包含一项信息那就是常量池表(constant pool table),包括各种字面量和对类型,域,方法的符号引用。
小结:常量池,可以看做是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等类型。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
永久代为什么要被元空间代替

  1. 为永久代设置空间大小是很难确定的
  2. 对永久代调优是很困难的
    方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型。
    在这里插入图片描述
    创建对象的6个步骤
    在这里插入图片描述
    1. 加载类元信息
    2. 为对象分配内存空间
    3. 处理并发问题(CAS, TLAB)
    4. 对象属性默认初始化
    5. 设置对象头信息
    6. 对象属性显示初始化,代码块初始化,构造器初始化

内存布局(包括对象头,实例数据, 对齐填充(仅仅占位符的作用))
对象头包括(运行时元数据,类型指针,如果是数组还包括数组的长度)
运行时元数据包括(hash值,GC分代年龄,锁状态标志,线程持有锁,线程偏向ID,偏向时间戳)
实例数据是类真正的有效信息,包括各个字段
规则,相同宽度的字段总是分配在一起,父类中定义的变量会出现在子类之前,如果CompactFields=true,将子类的窄变量信息可能插入到父类空隙。

对象访问的两种方式(句柄访问,直接指针(hotspot采用))

直接内存分配(通过ByteBuffer):

int buffer = 1024 * 1024 * 1024;
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(buffer);

直接内存在java堆外,默认大小与-Xmx设置的大小相同,也可通过MaxDirectMemorySize设置大小
缺点: 1. 分配回收成本较高 2 不受jvm内存回收管理
直接内存不足也会报Exception in thread “main” java.lang.OutOfMemoryError: Direct buffer memory

1.6 执行引擎

执行引擎的任务是将字节码解释/编译为对应平台上的本地机器指令、

1.6.1 解释器和JIT(即时编译器)

  1. 解释器:当java虚拟机启动时会根据预定义的规范对字节码采用逐行解释的方式执行,将每条字节码中的内容翻译为对应平台的本地机器指令执行
  2. 编译器:虚拟机将源代码直接编译成和本地机器平台相关的机器语言,即时编译器的目的是,将整个函数体编译成为机器码,每次函数执行时,只执编译后的机器码即可。
  3. 解释器的优点,省去了编译的时间,可以立即执行

1.7 String

  1. final 修饰
  2. 不可变性
  3. 通过字面量定义的字符串,是存放在堆中的字符串常量池中的
  4. String 的String pool是一个固定大小的HashTable, 默认值长度是(jdk6为1009, jdk7为60013)
  5. 可通过-XX:StringTableSize设置
  6. jk6到jdk7, 为什么要把字符串常量池从永久代调整到堆中(1.永久代空间比较小,永久代垃圾回收的频率低)
  7. 字符串常量池不允许出现相同的字符串
  8. 常量之间拼接结果还是在常量池中,是编译器优化的结果
  9. 拼接之间有变量,则在堆中new String(),存放结果
  10. final 修饰的String,可以看成常量,不是变量,因此字符串拼接不再是使用StringBuilder
  11. StringBuilder 优化方案,建议构造方法传入参数,避免扩容。
  12. PrintStringTableStatistics 打印字符串常量池统计数据
  13. 字符串常量池存在垃圾回收
  14. G1算法的垃圾回收的String去重操作

2.垃圾回收

什么是垃圾:运行程序中没有任何指针指向的对象, 这个对象就是需要被回收的垃圾。

2.1 垃圾标记阶段(对象存活判断)

两种方式(引用计数算法,标记清除算法)

2.1.1 引用计数算法(javaGC没有用)

优点:实现简单,垃圾对象便于辨识,判定效率高,回收没有延迟性。
缺点:无法处理循环引用的情况

2.1.2 可达性分析

GC ROOTS包括以下几类元素

  1. 虚拟机栈引用的对象
  2. 本地方法栈引用的对象
  3. 方法区类的静态属性引用的对象
  4. 方法区中常量引用的对象(字符串常量池里的引用)
  5. 所有被同步锁(synchronized)持有的对象
  6. 分代收集和局部回收(Partial GC):除了这些固定的GC ROOTS集合以外,根据用户所选用的垃圾收集器及当前回收的内存区域不同,可以有其他的对象临时性的加入,构成了完整的GC ROOT集合。
  7. 分析工作必须要在一个能保障一致性的快照中进行,所以必须要STW(stop the world),枚举根节点时必须要停顿。

2.1.3 finalization机制

一个对象的三种状态(可触及,可复活,不可触及)
判定一个对象是否可回收,需要经历两次标记过程:
8. 如果对象obj到GC ROOTS没有引用链,进行第一次标记
9. 进行筛选
10. 如果该对象没有重写finalize()方法,或者finalize()方法已经被虚拟机调用过了,该对象被判定为不可触及; 如果对象重写了 finalize()方法,那么它会被插入到F-Queue队列中,由一个虚拟机自动创建的,低优先极的Finallizer线程触发其finalize()执行。稍后GC会对F-Queue进行第二次标记。如果该对象在finalize()方法中与引用链上的任何对象建立了联系,那么在第二次标记时,该对象会被移出即将回收的集合。之后,对象会再次出现没有引用出现的情况。在这个情况下,finalize()方法不会被再次调用。

2.1.4 标记清除算法

执行过程((停止整个过程)swt)
标记:标记可达对象,记录在对象的header中
清除:遍历,如果没有在header中标记为可达对象,则将其回收。
缺点:(1.效率不算高(遍历的问题),2需要STW, 3.容易产生内存碎片,需要维护一个空闲列表)
这里的清除是指,把需要清除的对象地址保存的空闲列表里。

2.1.5 复制算法

优点:没有标记和清除过程,实现简单,运行高效;复制过去以后保证空间的连续性,不会出现碎片问题。
缺点:需要两倍的内存空间;GC需要维护region之间的引用关系,内存占用和时间开销都不小。
特别地:如果系统中的垃圾对象很多,复制算法需要复制的对象数量要非常低才行。(正好适用于朝生夕死的新生代)

2.1.6 标记压缩算法

优点:给新对象分配内存时,JVM只需要持有一个内存的起始地址即可,消除了复制算法中内存减半的高额代价。
缺点:效率上低复制算法,移动对象的同时,需要调整引用的地址。移动过程中需要(STW)暂停用户应用程序。

2.1.7 增量收集算法

垃圾收集线程只收集一小片区域的内存空间,接着切换到应用程序线程。依次反复,直到垃圾收集完成。
缺点:造成系统吞吐量的下降

2.1.8 分区算法

分区算法将整个堆空间划分成连续的不同小区间。每一个小区间都独立使用,独立回收。这种算法的好处是一次可以控制回收多少个小区间。

2.1.9 System.gc()的理解

System.gc(); //提醒jvm的垃圾回收器执行gc,但是不确定是否马上执行
Runtime.getRuntime().gc(); //与System.gc()的作用一样,就是System.gc()的底层
System.runFinalization(); //强制调用使用引用的对象的finalize()方法

2.1.10 内存溢出

没有空闲内存,垃圾收集器也无法提供更多的内存

  1. Java虚拟机的堆内存设置不够
  2. 代码中创建了大量大对象,并且长时间不能被垃圾收集器收集

2.1.11 内存泄漏的分析

严格来说,只有对象不会再被程序用到了,但是GC又不能回收他们的情况,才叫内存泄漏。
举例:
1. 单例模式:单例的生命周期和应用程序是一样长的,所以单例程序中,如果持有对外部程序引用的话,那么这个外部对象是不能被回收的,则会导致内存泄漏的产生。
2. 一些提供close资源未关闭导致内存泄漏。如数据库连接(dataSource.getConnection()),网络连接(socket),IO连接,必须手动close ,否则不能被回收。

2.1.12 Stop the world

  1. STW和采用哪款GC无关,所有GC都有这个事件。
  2. STW是JVM在后台自动发起,自动完成的
  3. 停顿产生时整个应用程序线程都会被暂停,没有任何响应。
  4. 开发中不要用system.gc(),导致 Stop the world

2.1.12 垃圾回收的并行与并发

并发:在操作系统上,是批几个应用程序同时处于在启动运行与运行完毕之间,并且几个应用程序在同一个处理器上运行。并发不是真正意义上的“同时进行”
并行:当系统有一个以上CPU,当一个CPU执行一个进程,另一个CPU执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,我们称之为并行。(并行适合科学计算,后台处理等弱交互场景)。
并行(Parallel)收集:多条垃圾收集线程并行工作,用户线程仍处于等待状态。ParNew, Parallel Scavenge, Parellel Old
串行(Serial):单线程执行
并发:用户线程与垃圾回收线程同时执行(但不一定是并行的,可能会交替执行),垃圾回收线程在执行时不会停顿用户线程。

2.1.12 安全点与安全区域

程序执行时并非在所有的地方都可以停顿下来开始GC,只有在特定的位置在能停顿下来开始GC, 这些位置被称为安全点。

  1. 如何在GC发生时,检查所有线程都跑到最近的安全点停顿下来呢?
  2. 抢占式中断(目前没有虚拟机采用)
  3. 主动式中断
    安全区域是批指在一段代码片断中, 对象的引用不会发生变化,在这个区域中的任何位置开始GC都是安全的。(为了解决程序处于block或sleep状态, 线程无法响应JVM的中断请求, 走到安全点去中断挂起)

2.1.12

Thread thread = new Thread();
thread.setDaemon(true);//设置为守护线程,当程序中没有非守护线程,守护线程也就执行结束了
thread.start();

2.1.12 评估GC的性能指标

  1. 吞吐量:运行用户代码的时间占总运行时间的比例(总运行时间:程序的运行时间+内存回收的时间)
  2. 暂定时间:执行垃圾收集时,程序的工作线程被暂停的时间(对于一个交互式的应用程序,要求低暂停时间(低延迟))
  3. 内存占用:Java堆区所占用的内存大小
    现在的标准:在最大㖔吐量优先的情况下,降低停顿时间

2.1.12 垃圾收集器有哪些

  1. 串行收集器:Serial, Serial Old
  2. 并行收集器:ParNew Parallel Scavenge, Parallel Old
  3. 并发收集器:CMS, G1

2.1.12 垃圾收集器与垃圾分代之间的关系

  1. 新生代收集器:Serial, ParNew, Parallel Scavenge
  2. 老年代收集器:Serial Old, Parallel Old, CMS
  3. 整堆收集器: G1
    在这里插入图片描述
    查看命令行相关参数(包含垃圾收集器)-XX:+PrintCommandLineFlags

2.1.12 serial回收器:串行回收

  1. serial收集器采用复制算法,串行回收和"stop-the-world"机制的方式执行内存回收
  2. serial old 收集器也采用了串行回收和"stop-the-world"机制,使用标记-压缩算法
  3. 优势:简单高效
  4. -XX:+UseSerialGC 参数可以指定年轻代和老年代都使用串行收集器,等价于新生代使用Serial 老年代使用Serial Old GC

总结:现在已经不用串行的。而且限定单核CPU才可以用,现在都不是单核的。对于较强的应用而言,这种垃圾收集器是不能接受的。一般在Java web应用程序中是不会采用串行垃圾收集器的。

2.1.12 ParNew回收器:并行回收

ParNew收集器除了采用并行回收的方式执行内存回收外,与Serial 垃圾收集器几乎没有任何区别。
ParNew/Serial Old
对于新生代,回收次数频繁,使用并行方式高效
对于老年代,回收次数少,使用串行方式节省资源(CPU并行需要切换线程,串行可以省去切换线程的资源)
除Serial外,目前只有ParNew GC能与CMS收集器配合使用。

-XX:+UseParNewGC 表示新生代使用ParNew
-XX:ParallelGCThreads限制线程数量,默认开启和CPU数量相同的线程数。

2.1.12 Parallel Scavenge回收器:吞吐量优先

和ParNew收集器不同,Parallel Scavenge收集器的目标是达到一个可控制的吞吐量,它也被称为㖔吐量优先的收集器
自适应调用策略也是Parallel Scavenge 与ParNew一个重要的区别
高㖔吐量可以高效的利用CPU时间,尽快的完成程序的运算任务,主要适合在后台运行而不需要太多交互的任务。因为,常见在服务器中环境使用,例如,那些执行批处理,订单处理,工资支付,科学计算的应用程序。
手动指定新生代:-XX:UseParallelGC
手动指定老年代: -XX:UseParallelOldGC
设置年经代并行收集器的线程数,最好与CPU数量相同,以避免过多的线程数量影响垃圾收集性能。-XX:ParalleGCThreads
默认情况下,当CPU数量小于8,ParalleGCThreads的值等于CPU的数量
当CPU的数量大于8个,ParalleGCThreads的值= 3+(5*CPU_Count/8)
设置垃圾收集的最大停顿时间:-XX:MaxGCPauseMillis
垃圾收集时间 占总时间 的比例: -XX:GCTimeRatio 默认值为99

-XX:+UseAdaptiveSizePolicy 设置Parallel Scavenge收集器具有自适应调节策略。

2.1.12 CMS回收器:低延迟

它第一次实现了让垃圾收集线程用用户线程同时工作。
重视服务响应速度,希望系统停顿时间短,如互联网站或B/S系统的服务端

CMS(老年代)只能与ParNew (新生代)或 Serial(新生代)收集器配合使用。它采用标记-清除算法,会产生内存碎片

优点

  • 低延迟
  • 并发收集

缺点:

  • 会产生内存碎片
  • 对CPU资源非常敏感
  • CMS收集器无法处理浮动垃圾
    JDK14,移除了CMS垃圾回收器

在这里插入图片描述

2.1.12 G1回收器:区域化分代式

-XX:+UseG1GC来启用
优点:
1. 并行与并发
2. 分代收集

 G1依然属于分代垃圾回收器,将堆空间分为若干个区域,这些区域包含逻辑上的年经代和
 老年代,它同时兼顾年经和老年代
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值