国外专门做童装的网站,凡科网下载,个人怎么注册域名,新闻门户网站免费建设目录 垃圾收集器与内存分配策略
引用
对象的访问方式有哪些?#xff08;句柄和直接指针#xff09;
Java的引用有哪些类型?
如何判断对象是否是垃圾?
请列举一些可作为GC Roots的对象?
对象头了解吗? mark word#xff08;hashcode、分代、锁标志位#xff09;、…目录 垃圾收集器与内存分配策略
引用
对象的访问方式有哪些?句柄和直接指针
Java的引用有哪些类型?
如何判断对象是否是垃圾?
请列举一些可作为GC Roots的对象?
对象头了解吗? mark wordhashcode、分代、锁标志位、指向类信息的指针和数组长度(数组才有)
Object a new object()的大小
对象引用占多少大小类型指针
执行main方法的过程
创建对象的过程
对象的生命周期
什么是指针碰撞什么是空闲列表用于给对象分配内存
JVM 里 new 对象时堆会发生抢占吗JVM 是怎么设计来保证线程安全的
垃圾收集器 主要作用于方法区常量和堆实例对象
Full GC 的触发条件?
聊聊Java中的GC分别作用在什么范围
有哪些GC算法? 可达性分析标记清除算法内存碎片-复制算法(内存利用低)-标记整理算法-分代回收
说一下新生代的区域划分
你知道哪些垃圾收集器?
Serial收集器 复制 新生代 单线程
Serial Old 收集器 单线程 标记整理
ParNew收集器 复制 新生代 多线程
Parallel Scavenge 收集器 新生代、多线程 、复制、 关注垃圾回收吞吐量
Parallel Old 收集器 多线程 标记整理
CMS收集器 并发标记清除追求最短STW 老年代
G1收集器高吞吐低停顿的平衡 分代回收 标记整理复制
ZGC 收集器
工作中项目使用的什么垃圾回收算法
G1回收器的特色是什么
GC只会对「堆」进行GC吗 还会针对方法区
有了 CMS为什么还要引入 G1
垃圾收集器应该如何选择 垃圾收集器与内存分配策略
引用
对象的访问方式有哪些?句柄和直接指针
Java程序会通过栈上的引用操作堆上的具体对象对象的访问方式由虚拟机决定主流访问方式主要有句柄和直接指针。
句柄 : 堆会划分出一块内存作为句柄池引用中存储对象的句柄地址句柄包含对象实例数据与类型数据的地址信息。
优点是引用中存储的是稳定句柄地址在GC过程中对象被移动时只会改变句柄的实例数据指针而引用本身不需要修改。
直接指针 :引用中存储的直接就是对象的地址。对象包含到对象类型数据的指针通过这个指针可以访问对象类型数据。
使用直接指针访问方式最大的好处就是访问对象速度快它节省了一次指针定位的时间开销
虚拟机hotspot主要是使用直接指针来访问对象。 Java的引用有哪些类型?
JDK1.2后对引用进行了扩充按强度分为四种:
强引用:最常见的引用例如Object obj - new Object()就属于强引用。只要对象有强引用指向且GC Roots可达在内存回收时即使濒临内存耗尽也不会被回收。
软引用:弱于强引用描述非必需对象。在系统将发生内存溢出前会把软引用关联的对象加入回收范围以获得更多内存空间。用来缓存服务器中间计算结果及不需要实时保存的用户行为等。
弱引用:弱于软引用描述非必需对象。弱引用关联的对象只能生存到下次YGCMinor GC前当垃圾收集器开始工作时无论当前内存是否足够都会回收只被弱引用关联的对象。由于YGC具有不确定性因此弱引用何时被回收也不确定。
虚引用:最弱的引用定义完成后无法通过该引用获取对象。唯一目的就是为了能在对象被回收时收到一个系统通知。虚引用必须与引用队列联合使用垃圾回收时如果出现虚引用就会在回收对象前把这个虚引用加入引用队列。
虚引用看起来和弱引用没啥区别只是必须搭配ReferenceQueue。用虚引用的目的一般是跟踪对象被回收的活动。
如何判断对象是否是垃圾?
引用计数:在对象中添加一个引用计数器如果被引用计数器加1引用失效计数器减1如果计数器为0则被标记为垃圾。原理简单效率高但是在Java 中很少使用因为存在对象间循环引用的问题导致计数器无法清零。
可达性分析: 主流语言的内存管理都使用可达性分析判断对象是否存活。基本思路是通过称为 GC Roots 的根对象作为起始节点从这些节点开始根据引用关系向下搜索搜索过程走过的路径称为引用链如果某个对象到 GC Roots 没有任何引用链相连则会被标记为垃圾。
可作为 GC Roots的对象包括虚拟机栈和本地方法栈中引用的对象、类静态属性引用的对象、常量引用的对象。 引用的对象
请列举一些可作为GC Roots的对象?
所谓的 GC Roots就是一组必须活跃的引用不是对象它们是程序运行时的起点是一切引用链的源头。
在 Java 中GC Roots 包括以下几种
在虚拟机栈中引用的对象例如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。在方法区中类静态属性引用的对象例如Java类的引用类型静态变量。在方法区中常量引用的对象例如字符串常量池(String Table)里的引用。在本地方法栈中Native方法引用的对象。Java虚拟机内部的引用如基本数据类型对应的Class对象一些常驻的异常对象(比如NullPointExcepiton、OutOfMemoryError)等还有系统类加载器。所有被同步锁(synchronized关键字)持有的对象。反映Java虚拟机内部情况的JM XBean、JVM TI中注册的回调、本地代码缓存等。
对象头了解吗? mark wordhashcode、分代、锁标志位、指向类信息的指针和数组长度(数组才有)
在 HotSpot 中对象在堆内存中的存储布局可以划分为三个部分对象头Object Header、实例数据Instance Data和对齐填充Padding。
对象的实例数据就是存储了对象的具体信息属性和类型。对齐填充字节:因为JVM要求对象占的内存大小是8bit 的倍数因此后面有几个字节用于把对象的大小补齐至 8bit的倍数。空间换时间
CPU 进行内存访问时一次寻址的指针大小是 8 字节正好是 L1 缓存行的大小。如果不进行内存对齐则可能出现跨缓存行访问导致额外的缓存行加载降低了 CPU 的访问效率。
对象头由以下三部分组成:mark word、指向类信息的指针和数组长度(数组才有)。
mark word 包含:包含了对象自身的运行时数据如哈希码HashCode、垃圾回收分代年龄、锁状态标志、线程持有的锁、偏向线程 ID 等信息。在 64 位操作系统下占 8 个字节32 位操作系统下占 4 个字节。类型指针Class Pointer指向对象所属类的元数据的指针JVM 通过这个指针来确定对象的类。在开启了压缩指针的情况下这个指针可以被压缩。在开启指针压缩的情况下占 4 个字节否则占 8 个字节。在 JDK 8 中压缩指针默认是开启的以减少 64 位应用中对象引用的内存占用。
内存对齐的主要作用是:
平台原因:不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据否则抛出硬件异常。性能原因:经过内存对齐后CPU的内存访问速度大大提升。 Object a new object()的大小
一般来说对象的大小是由对象头、实例数据和对齐填充三个部分组成的。
对象头的大小在 32 位 JVM 上是 8 字节在 64 位 JVM 上是 16 字节如果开启了压缩指针就是 12 字节。实例数据的大小取决于对象的属性和它们的类型。对于new Object()来说Object 类本身没有实例字段因此这部分可能非常小或者为零。对齐填充的大小取决于对象头和实例数据的大小以确保对象的总大小是 8 字节的倍数。 一般来说目前的操作系统都是 64 位的并且 JDK 8 中的压缩指针是默认开启的因此在 64 位 JVM 上new Object()的大小是 16 字节12 字节的对象头 84 4 字节的对齐填充。
对象引用占多少大小类型指针
在 64 位 JVM 上未开启压缩指针时对象引用占用 8 字节开启压缩指针时对象引用可被压缩到 4 字节。
而 HotSpot JVM 默认开启了压缩指针因此在 64 位 JVM 上对象引用占用 4 字节。
执行main方法的过程
编译.java后得到.class 后执行这个class文件系统会启动一个JVM进程进行类的加载根据全限定名找到二进制字节文件转化成方法区的运行时数据结构。JVM 找到的主程序入口执行main方法。main方法的第一条语句为Person p new Person(大彬)就是让JVM创建一个Person 对象但是这个时候方法区中是没有Person类的信息的所以JVM马上加载 Person类把Person类的信息放到方法区中。加载完Person类后JVM 在堆中分配内存给Person 对象然后调用构造函数初始化 Person 对象这个Person 对象持有指向方法区中的Person类的类型信息的引用。执行p.getName()时JVM根据 p 的引用找到 p 所指向的对象然后根据此对象持有的引用定位到方法区中Person类的类型信息的方法表获得getName()的字节码地址。执行getName()方法。
创建对象的过程 在Java中创建对象的过程包括以下几个步骤
类加载检查虚拟机遇到一条 new 指令时首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有那必须先执行相应的类加载过程。分配内存在类加载检查通过后接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。初始化零值内存分配完成后虚拟机需要将分配到的内存空间都初始化为零值成员变量数值类型是 0布尔类型是 false对象类型是 null 不包括对象头这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用程序能访问到这些字段的数据类型所对应的零值。
进行必要设置比如对象头初始化零值完成之后虚拟机要对对象进行必要的设置例如这个对象是哪个类的实例、对象的哈希码、对象的 GC 分代年龄等信息。这些信息存放在对象头中。另外根据虚拟机当前运行状态的不同如是否启用偏向锁等对象头会有不同的设置方式。执行 init 方法在上面工作都完成之后从虚拟机的视角来看一个新的对象已经产生了但从 Java 程序的视角来看对象创建才刚开始——构造函数即class文件中的方法还没有执行所有的字段都还为零对象需要的其他资源和状态信息还没有按照预定的意图构造好。所以一般来说执行 new 指令之后会接着执行方法把对象按照程序员的意愿进行初始化这样一个真正可用的对象才算完全被构造出来。将成员变量赋值为预期的值
对象的生命周期
对象的生命周期包括创建、使用和销毁三个阶段
创建对象通过关键字new在堆内存中被实例化构造函数被调用对象的内存空间被分配。使用对象被引用并执行相应的操作可以通过引用访问对象的属性和方法在程序运行过程中被不断使用。销毁当对象不再被引用时通过垃圾回收机制自动回收对象所占用的内存空间。垃圾回收器会在适当的时候检测并回收不再被引用的对象释放对象占用的内存空间完成对象的销毁过程。
什么是指针碰撞什么是空闲列表用于给对象分配内存 在堆内存分配对象时主要使用两种策略指针碰撞和空闲列表。
①、指针碰撞Bump the Pointer
假设堆内存是一个连续的空间分为两个部分一部分是已经被使用的内存另一部分是未被使用的内存。
在分配内存时Java 虚拟机维护一个指针指向下一个可用的内存地址每次分配内存时只需要将指针向后移动碰撞一段距离然后将这段内存分配给对象实例即可。
②、空闲列表Free List
JVM 维护一个列表记录堆中所有未占用的内存块每个空间块都记录了大小和地址信息。
当有新的对象请求内存时JVM 会遍历空闲列表寻找足够大的空间来存放新对象。
分配后如果选中的空闲块未被完全利用剩余的部分会作为一个新的空闲块加入到空闲列表中。
指针碰撞适用于管理简单、碎片化较少的内存区域如年轻代而空闲列表适用于内存碎片化较严重或对象大小差异较大的场景如老年代。
JVM 里 new 对象时堆会发生抢占吗JVM 是怎么设计来保证线程安全的
会假设 JVM 虚拟机上每一次 new 对象时指针就会向右移动一个对象 size 的距离一个线程正在给 A 对象分配内存指针还没有来的及修改另一个为 B 对象分配内存的线程又引用了这个指针来分配内存这就发生了抢占。
有两种可选方案来解决这个问题 采用CAS 分配重试的方式来保证更新操作的原子性每个线程在 Java 堆中预先分配一小块内存也就是本地线程分配缓冲Thread Local Allocation BufferTLAB要分配内存的线程先在本地缓冲区中分配只有本地缓冲区用完了分配新的缓存区时才需要同步锁定。
垃圾收集器 主要作用于方法区常量和堆实例对象
Full GC 的触发条件? 对于Minor GC其触发条件比较简单当Eden 空间满时就将触发一次Minor GC。
而Full GC触发条件相对复杂有以下情况会发生 full GC:
调用 System.gc()
只是建议虚拟机执行 Full GC但是虚拟机不一定真正去执行。不建议使用这种方式而是让虚拟机管理内存。
老年代空间不足
老年代空间不足的常见场景为大对象直接进入老年代、长期存活的对象进入老年代等。为了避免以上原因引起的 Full GC应当尽量不要创建过大的对象以及数组。
除此之外可以通过 -Xmn参数调大新生代的大小让对象尽量在新生代被回收掉不进入老年代。还可以通过 -XX:MaxTenuringThreshold 调大对象进入老年代的年龄让对象在新生代多存活一段时间。
空间分配担保失败
新生代的 To 区放不下从 Eden 和 From 拷贝过来对象或者新生代对象 GC 年龄到达阈值需要晋升这两种情况老年代如果放不下的话都会触发 Full GC。
JDK 1.7及以前的永久代空间不足
在JDK 1.7及以前HotSpot 虚拟机中的方法区是用永久代实现的永久代中存放的为一些Class的信息、常量、静态变量等数据。当系统中要加载的类、反射的类和调用的方法较多时永久代可能会被占满在未配置为采用CMS GC 的情况下也会执行 Full GC。如果经过 Full GC仍然回收不了那么虚拟机抛出 java.lang.OutOfMemoryError。
聊聊Java中的GC分别作用在什么范围
新生代收集(Minor GC / Young GC) 只是新生代的垃圾收集老年代收集 (Major GC / Old GC) 只是老年代的垃圾收集(目前只有CMS GC会有单独收集老年代的行为)整堆收集(Full GC)收集整个堆和方法区元空间的垃圾混合收集(Mixed GC)收集整个新生代以及部分老年代的垃圾收集 (目前只有G1会有这种行为)
有哪些GC算法? 可达性分析标记清除算法内存碎片-复制算法(内存利用低)-标记整理算法-分代回收
Java的内存回收机制基于自动内存管理开发人员无需手动释放内存。垃圾回收器会自动识别不再使用的对象并回收它们所占用的内存空间。
垃圾回收算法主要有
标记-清除算法标记-清除算法分为“标记”和“清除”两个阶段首先通过可达性分析标记出所有需要回收的对象然后统一回收所有被标记的对象。 标记-清除算法有两个缺陷一个是效率问题标记和清除的过程效率都不高另外一个就是清除结束后会造成大量的碎片空间。有可能会造成在申请大块内存的时候因为没有足够的连续空间导致再次 GC。
复制算法为了解决碎片空间的问题出现了“复制算法”。复制算法的原理是将内存分成两块每次申请内存时都使用其中的一块当内存不够时将这一块内存中所有存活的复制到另一块上。然后将然后再把已使用的内存整个清理掉。 复制算法解决了空间碎片的问题。但是也带来了新的问题。因为每次在申请内存时都只能使用一半的内存空间。内存利用率严重不足。
标记-整理算法复制算法在 GC 之后存活对象较少的情况下效率比较高但如果存活对象比较多时会执行较多的复制操作效率就会下降。而老年代的对象在 GC 之后的存活率就比较高所以就有人提出了“标记-整理算法”。标记-整理算法的“标记”过程与“标记-清除算法”的标记过程一致但标记之后不会直接清理。而是将所有存活对象都移动到内存的一端。移动结束后直接清理掉剩余部分。分代回收算法分代收集是将内存划分成了新生代和老年代。分配的依据是对象的生存周期或者说经历过的 GC 次数。对象创建时一般在新生代申请内存当经历一次 GC 之后如果对还存活那么对象的年龄 1。当年龄超过一定值(默认是 15可以通过参数 -XX:MaxTenuringThreshold 来设定)后如果对象还存活那么该对象会进入老年代。
说一下新生代的区域划分
新生代的垃圾收集主要采用标记-复制算法因为新生代的存活对象比较少每次复制少量的存活对象效率比较高。
基于这种算法虚拟机将内存分为一块较大的 Eden 空间和两块较小的 Survivor 空间每次分配内存只使用 Eden 和其中一块 Survivor。发生垃圾收集时将 Eden 和 Survivor 中仍然存活的对象一次性复制到另外一块 Survivor 空间上然后直接清理掉 Eden 和已用过的那块 Survivor 空间。默认 Eden 和 Survivor 的大小比例是 8∶1。 你知道哪些垃圾收集器? 垃圾回收器主要分为以下几种:Serial、ParNew、Parallel Scavenge、Serial old、 Parallel old、CMS、G1.
Serial收集器 复制 新生代 单线程
单线程收集器使用一个垃圾收集线程去进行垃圾回收在进行垃圾回收的时候必须暂停其他所有的工作线程(Stop The World)直到它收集结束。
特点:简单高效;内存消耗小;没有线程交互的开销单线程收集效率高;需暂停所有的工作线程用户体验不好。
Serial 是虚拟机在客户端模式的默认新生代收集器
Serial Old 收集器 单线程 标记整理
Serial收集器的老年代版本单线程收集器使用标记整理算法。 ParNew收集器 复制 新生代 多线程
Serial收集器的多线程版本除了使用多线程进行垃圾收集外其他行为、参数与 Serial 收集器基本一致。 Parallel Scavenge 收集器 新生代、多线程 、复制、 关注垃圾回收吞吐量
新生代收集器基于复制算法实现的收集器。特点是吞吐量优先能够并行收集的多线程收集器允许多个垃圾回收线程同时运行降低垃圾收集时间提高吞吐量。 Parallel Scavenge收集器关注点是吞吐量高效率的利用 CPU资源。
Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量分别是控制最大垃圾收集停顿时间的
-XX:MaxGCPauseMillis参数以及直接设置吞吐量大小的-XX:GCTimeRatio参数。
-XX:MaxGCPauseMillis参数的值是一个大于0的毫秒数收集器将尽量保证内存回收花费的时间不超过用户设定值。-XX:GCTimeRatio参数的值大于0小于100即垃圾收集时间占总时间的比率相当于吞吐量的倒数。
Parallel Old 收集器 多线程 标记整理
Parallel Scavenge收集器的老年代版本。多线程垃圾收集使用标记整理算法。 CMS收集器 并发标记清除追求最短STW 老年代
初始标记和重新标记会 STWJDK 1.5 时引入JDK9 被标记弃用JDK14 被移除。
Concurrent Mark Sweep并发标记清除追求获取最短停顿时间实现了让垃圾收集线程与用户线程基本上同时工作。
CMS垃圾收集器关注点更多的是用户线程的停顿时间。
CMS垃圾回收基于标记清除算法实现整个过程分为四个步骤:
初始标记:暂停所有用户线程(Stop The World)记录直接与GCRoots直接相连的对象。并发标记:从GC Roots 开始对堆中对象进行可达性分析找出存活对象耗时较长但是不需要停顿用户线程。重新标记:在并发标记期间对象的引用关系可能会变化需要重新进行标记。此阶段也会暂停所有用户线程。并发清除:清除标记对象这个阶段也是可以与用户线程同时并发的。
在整个过程中耗时最长的是并发标记和并发清除阶段这两个阶段垃圾收集线程都可以与用户线程一起工作所以从总体上来说CMS收集器的内存回收过程是与用户线程一起并发执行的。
优点:并发收集停顿时间短。
缺点:
标记清除算法导致收集结束有大量空间碎片。产生浮动垃圾在并发清理阶段用户线程还在运行会不断有新的垃圾产生这一部分垃圾出现在标记过程之后CMS 无法在当次收集中回收它们只好等到下一次垃圾回收再处理; G1收集器高吞吐低停顿的平衡 分代回收 标记整理复制
G1Garbage-First Garbage Collector在 JDK 1.7 时引入在 JDK 9 时取代 CMS 成为了默认的垃圾收集器。G1 有五个属性分代、增量、并行、标记整理、STW。
G1垃圾收集器的目标是在不同应用场景中追求高吞吐量和低停顿之间的最佳平衡。
G1将整个堆分成相同大小的分区 ( Region)有四种不同类型的分区:Eden、 Survivor、Old和Humongous。分区的大小取值范围为1M到 32M,都是2的幂次方。
分区大小可以通过 -XX:G1HeapRegionSize 参数指定。
Humongous区域用于存储大对象。G1规定只要大小超过了一个分区容量一半的对象就认为是大对象。
G1收集器对各个分区回收所获得的空间大小和回收所需时间的经验值进行排序得到一个优先级列表每次根据用户设置的最大回收停顿时间优先回收价值最大的分区。也是名字的由来Garbage-First
特点:可以由用户指定期望的垃圾收集停顿时间。
G1收集器的回收过程分为以下几个步骤:
初始标记。暂停所有其他线程记录直接与GC Roots直接相连的对象耗时较短。并发标记。从GC Roots 开始对堆中对象进行可达性分析找出要回收的对象耗时较长不过可以和用户程序并发执行。最终标记。需对其他线程做短暂的暂停用于处理并发标记阶段对象引用出现变动的区域。筛选回收。对各个分区的回收价值和成本进行排序根据用户所期望的停顿时间来制定回收计划然后把决定回收的分区的存活对象复制到空的分区中再清理掉整个旧的分区的全部空间。
G1 在垃圾回收期间仍然需要「Stop the World」。不过G1 在停顿时间上添加了预测机制用户可以 JVM 启动时指定期望停顿时间G1 会尽可能地在这个时间内完成垃圾回收。
这里的操作涉及存活对象的移动会暂停用户线程由多条收集器线程并行完成。 ZGC 收集器
ZGC 是 JDK 11 时引入的一款低延迟的垃圾收集器它的目标是在不超过 10ms 的停顿时间内为堆大小达到 16TB 的应用提供一种高吞吐量的垃圾收集器。
ZGC 的两个关键技术指针染色和读屏障不仅应用在并发转移阶段还应用在并发标记阶段将对象设置为已标记传统的垃圾回收器需要进行一次内存访问并将对象存活信息放在对象头中而在 ZGC 中只需要设置指针地址的第 42-45 位即可并且因为是寄存器访问所以速度比访问内存更快。 工作中项目使用的什么垃圾回收算法
我们生产环境中采用了设计比较优秀的 G1 垃圾收集器G1 采用的是分区式标记-整理算法将堆划分为多个区域按需回收适用于大内存和多核环境能够同时考虑吞吐量和暂停时间。
或者
我们系统采用的是 CMS 收集器CMS 采用的是标记-清除算法能够并发标记和清除垃圾减少暂停时间适用于对延迟敏感的应用。
再或者
我们系统采用的是 Parallel 收集器Parallel 采用的是年轻代使用复制算法老年代使用标记-整理算法适用于高吞吐量要求的应用。
G1回收器的特色是什么
G1回收器的特色在于它将堆内存划分成多个大小相等的独立区域并且通过维护一个优先列表来进行局部区域的垃圾收集从而减少全堆垃圾收集的频率和停顿时间。G1也特别注重停顿时间的可预测性并允许用户指定期望的停顿时间目标。
GC只会对「堆」进行GC吗 还会针对方法区
主要的垃圾收集活动确实发生在堆内存中因为这是大多数Java对象存活和死亡的地方。不过方法区也是垃圾收集的目标之一例如回收废弃常量和无用的类。
程序计数器、虚拟机栈和本地方法栈通常随线程而生随线程而灭所以它们不是垃圾收集的目标。
有了 CMS为什么还要引入 G1
CMS 适用于对延迟敏感的应用场景主要目标是减少停顿时间但容易产生内存碎片。G1 则提供了更好的停顿时间预测和内存压缩能力适用于大内存和多核处理器环境。 垃圾收集器应该如何选择
这里简单地列一下上面提到的一些收集器的适用场景
Serial 如果应用程序有一个很小的内存空间大约 100 MB亦或它在没有停顿时间要求的单线程处理器上运行。Parallel如果优先考虑应用程序的峰值性能并且没有时间要求要求或者可以接受 1 秒或更长的停顿时间。CMS/G1如果响应时间比吞吐量优先级高或者垃圾收集暂停必须保持在大约 1 秒以内。ZGC如果响应时间是高优先级的或者堆空间比较大。
自己整理借鉴很多博主感谢他们