G1详解

article/2025/9/1 10:10:06

一 G1收集器

  • g1收集器是一个面向服务端的垃圾收集器适用于多核处理器、大内存容量的服务端系统。
  • 它满足短时间gc停顿的同时达到一个较高的吞吐量。
  • JDK7以上版本适用

先介绍两个概念:吞吐量和响应能力,响应能力和吞吐量是评价一个系统的两个重要指标

吞吐量

  • 吞吐量关注的是,在一个指定的时间内,最大化一个应用的工作量。
  • 如下方式来衡量一个系统吞吐量的好坏:
  • 在一定时间内同一个事务(或者任务、请求)完成的次数(tps)
  • 数据库一定时间以完成多少次查询
  • 对于关注吞吐量的系统,一定次数卡顿(即stw)是可以接受的,因为这个系统关注长时间的大量任务的执行能力,单次快速的响应并不值得考虑

响应能力

响应能力指一个程序或者系统对请求是否能够及时响应,比如:

  • 一个桌面UI能多快地响应一个事件
  • 一个网站能够多快返回一个页面请求
  • 数据库能够多快返回查询的数据

对于这类对响应能力敏感的场景,长时间的停顿是无法接受的,关注每一次反应的能力。

1.1 G1收集器的设计目标

与应用线程同时工作,几乎不需要 stop the work(与CMS类似);

  • G1的设计规划是要替换掉CMS,G1在某些方面弥补了CMS的不足,比如CMS使用的是mark-sweep算法,自然会产生内存碎片(CMS只能在Full GC时,用 stop the world整理内存碎片),然而G1基于copying算法,高效的整理剩余内存,而不需要管理内存碎片

  • GC停顿更加可控,甚至可以设置一个时间阈值,比如说可以回收某些部分的老年代,而不像CMS老年代必须全部回收(其它收集器时间可能难以把握)

1.2 G1重要概念

1.2.1 分区( Region)

  • G1采取了不同的策略来解决并行、串行和CMS收集器的碎片、暂停时间不可控等问题—G1将整个堆分成相同大小的分区或称为区域( Region)

    • G1的堆结构如下:
      • 1582976958190
        1582976958190
    • 每个分区都可能是年轻代也可能是老年代,但是在同一时刻只能属于某个代。年轻代,survivor区,老年代这些概念还存在,成为逻辑上的概念,这样方便复用之前分代框架的逻辑。分区在物理上不需要连续,则带来了额外的好处,可以在 老年代中只对某些区域(Region)进行回收。对于 新生代依然是在新生代满了的时候,对整个新生代进行回收一一整个新生代中的对象,要么被回收、要么晋升,至于新生代也采取分区机制的原因,则是因为这样跟老年代的策略统一,方便调整代的大小【其实新生代并不适合这样的垃圾收集,因为是对新生代的所有区域进行回收!不用像老年代那样选择回收效益高的Region】
    • 在G1中,还有一种特殊的区域,叫 Humongous区域。如果一个对象占用的空间达到或是超过了分区容量50%以上,G1收集器就认为这是一个巨型对象。这些巨型对象,默认直接会被分配在老年代,但是如果它是一个短期存在的巨型对象就会对垃圾收集器造成负面影响。为了解决这个问题,G1划分了一个 Humongous区,它用来专门存放巨型对象。如果一个H区装不下一个巨型对象,那么G1会寻找连续的H分区来存储。为了能找到连续的H区,有时候不得不启动Full GC

1.2.2 收集集合(CSet)

收集集合(Collection Set):一组可被回收的分区的集合,在CSet中存活的数据会在GC过程中被移动到另一个可用分区,CSet中的分区可以来自eden空间、 survivor空间、或者老年代,并且可以同时包含这几代分区的内容。

1.2.3 已记忆集合(RSet)

已记忆集合(Remember Set):RSet使区域Region的并行和独立成为可能,RSet记录了其他Region中的对象引用本 Region中对象的关系,属于 points-into结构(谁引用了我的对象)。RSet的价值在于使得垃圾收集器不需要扫描整个堆找到谁引用了当前分区中的对象,只需要扫描RSet即可。

point-into Rset解析

G1 GC是在 points-out的 card table之上再加了一层结构来构成 points-into Rset:每个Region会使用point-into Rset记录下到底哪些别的Region有指向自己的指针,而这些指针分别在哪些card的范围内【也就是说 card table 记录自己指向了谁,Point-into Ret 记录了谁指向了自己】。这个RSet其实是一个 hash table,key是别的Region的起始地址,value是一个集合,里面的元素是 card table的 Index.举例来说,如果region A的Rset里有一项的key是 region B,value里有 index为1234的card,它的意思就是 region B的一个card里有引用指向region A。所以对 region A来说该RSet记录的是 points-into的关系,而 card table仍然记录了 points-out的关系。

car table 解析

card table是什么又是怎么来的呢?如果一个对象引用的对象很多,赋值器需要对每个引用做处理,赋值器开销会很大,为了解决赋值器开销这个问题,在G1中又引入了另外一个概念,卡表( Card Table)。一个 Card Table将一个分区在逻辑上划分为固定大小的连续区域每个区域称之为卡。卡通常较小,介于128到512字节之间。 Card Table通常为字节数组,由Card的索引(即数组下标)来标识每个分区的空间地址

下图表示了RSet、Card和Region的关系):

1583938173624
1583938173624

上图中有三个Region,每个Region被分成了多个Card,在不同Region中的Card会相互引用,Region1中的Card中的对象引用了Region2中的Card中的对象,蓝色实线表示的就是points-out的关系,而在Region2的RSet中,记录了Region1的Card,即红色虚线表示的关系,这就是points- into。

而维系RSet中的引用关系靠post-write barrier和Concurrent refinement threads来维护,操作伪代码如下:

void oop_field_store(oop* field, oop new_value) {pre_write_barrier(field);             // pre-write barrier: for maintaining SATB invariant*field = new_value;                   // the actual storepost_write_barrier(field, new_value); // post-write barrier: for tracking cross-region reference
}

RSet究竟是怎么辅助GC的呢?在做YGC的时候,只需要选定young generation region的RSet作为根集,这些RSet记录了old->young的跨代引用,避免了扫描整个old generation。 而mixed gc的时候,old generation中记录了old->old的RSet,young->old的引用由扫描全部young generation region得到,这样也不用扫描全部old generation region。所以RSet的引入大大减少了GC的工作量。

1.2.4 SATB

Snapshot-at-the-beginning(SATB)SATB是G1 GC在全局并发标记阶段使用的增量式的标记算法。并发标记是并发多线程的,但并发线程在同一时刻只扫描一个分区

1.3 GC模式

G1提供了两种GC模式, Young GC和 Mixed Gc两种都是完全 Stop The World的

1.3.1 Young GC

Young GC:将选择所有年轻代里的 Region进行GC(所以CSet里存的就是所有年轻代里面的Region)。将存活的对象复制到幸survivor区是多线程执行的并且需要stw,如果满足晋升到老年代阈值,则某些对象将被提升到老年代,并通过动态计算出下次GC时堆中年轻代的 Region个数,即年轻代内存大小来控制 Young GC的时间开销。

Young GC的触发时机

Young GC在Eden充满时触发,在回收之后所有之前属于Eden的区块全部变成空白即不属于任何一个分区(Eden、 Survivor、Old)

1.3.2 Mixed GC

Mixed GC:将选择所有年轻代里的 Region,外加根据 global concurrent marking统计得出收集收益高的若干老年代 Region进行GC(所以CSet里存的是所有年轻代里的 Region加上在全局并发标记阶段标记出来的收益高的 Region)。在用户指定的开销目标范围内尽可能选择收益高的老年代 Region,以此来控制Mixed GC的时间开销。

  • 需要注意的是:Mixed GC不是 Full GC,它只能回收部分老年代的 Region而不是全部的Region,如果 Mixed GC实在无法跟上程序分配内存的速度,导致老年代填满无法继续进行 Mixed GC,就会退化然后使用 serial old GC( Full GC)来收集整个 GC heap。所以本质上,G1是不提供 Full GC的。所以总的来说,G1的运行过程是这样的:会在 Young GC和Mixed GC之间不断地切换运行,同时定期地做全局并发标记,在实在赶不上对象创建速度的情况下使用Full Gc( Serial GC)。
  • 它的GC步骤分为两步全局并发标记(global concurrent marking)和拷贝存活对象(evacuation)

Mixed gc 中的Global concurrent marking

Mixed gc 中的 global concurrent marking的执行过程类似于CMS,但是不同的是,在G1 GC中,它主要是为 Mixed GC提供标记服务的【老年代之所以知道为什么哪里的回收效益高就是根据此服务得出的结果确定的】,并不是一次GC过程的一个必须环节。global concurrent marking的执行过程分为四个步骤

  • 初始标记( initial mark,STW):它标记了从GC Root开始直接可达的对象。第一阶段 initial mark是共用了 Young GC的暂停,这是因为他们可以复用 root scan操作,所以可以说 global concurrent marking是伴随 Young GC而发生的。
  • 并发标记( Concurrent Marking):这个阶段从GC Root开始对heap中的对象进行标记,标记线程与应用程序线程并发执行,并且收集各个Region的存活对象信息。
  • 重新标记( Remark,STW) :标记那些在并发标记阶段发生变化的对象,将被回收。
  • 清理( Cleanup ):清除空 Region(没有存活对象的),加入到 free list。第四阶段 Cleanup只是回收了没有存活对象的 Region,所以它并不需要STW

停顿预测模型

  • G1收集器突出表现出来的一点是通过一个停顿预测模型根据用户配置的停顿时间来选择CSet的大小,从而达到用户期待的应用程序暂停时间
  • 通过-XX:MaxgcpauseMillis 参数来设置。这一点有点类似于 Parallel Scavenge收集器。关于停顿时间的设置并不是越短越好,设置的时间越短意味着每次收集的CSet越小,导致垃圾逐步积累变多,最终不得不退化成 Serial GC;停顿时间设置的过长那么会导致每次都会产生长时间的停顿影响了程序对外的响应时间

Mixed GC的触发时机

触发时机由一些参数控制,另外这些参数也控制着哪些老年代 Region会被选入CSet(收集集合)

  • 参数:G1heapwastepercent: Global concurrent marking结束之后,我们可以知道 old gen regions中有多少空间要被回收在每次YGC之后和再次发生Mixed GC之前【YGC是在Eden空间满了就进行GC】会检査垃圾占比是否达到此参数,只有达到了,下次才会发生 Mixed GC
  • 参数:G1MixedGCliveThresholdPercent: old generation region中的存活对象的占比,只有占比低于此参数,该old generation region才会被选入CSet
  • 参数:G1MixedGCCountTarget:一次global concurrent marking之后,最多执行 MixedGC的次数
  • 参数:G1OldCSetRegionThresholdPercent:一次Mxed GC中能被选入Cset的最多old generation region数量
  • 1583073184480
    1583073184480

1.3.3 G1的收集概览

G1算法将堆划分为若干个区域( Region),它仍然属于分代收集器。不过,这些区域的一部分包含新生代和老年代,新生代的垃圾收集依然采用暂停所有应用线程的方式,将存活对象拷贝到老年代或者Survivor空间。老年代也分成很多区域,G1收集器通过将对象从一个区域复制到另外一个区域,完成了清理工作。这就意味着,在正常的处理过程中,G1完成了堆的压缩(至少是部分堆的压缩),这样也就不会有CMS内存碎片问题的存在

YGC收集时如何找到根对象呢?老年代所有的对象都是根对象吗?那如果直接对整个老年代进行扫描的话会耗费大量的时间。于是G1引入了我们上面提到的RSet的概念,作用是跟踪指向某个heap区内的对象引用。但是在新生代之间记录引用吗?这是不必要的,原因在于每次GC时所有新生代都会被扫描,所以只需要记录老年代到新生代之间的引用即可。YGC收集过程如下:,不懂的原因,对Rset 和point out point into 相关还是不理解

1583157404318
1583157404318

三色标记算法

这是Mixed gc 中的Global concurrent marking中的内容,提到并发标记,我们不得不了解并发标记的三色标记算法,mark的过程就是遍历heap标记 live object的过程。它是描述追踪式回收器的一种有效的方法,利用它可以推演回收器的正确性。我们将对象分成三种类型:

  • 黑色:根对象或者该对象与它的子对象都被扫描过(即对象被标记了,且它的所有feld也被标记完了)
  • 灰色:对象本身被扫描,但还没扫描完该对象中的子对象(即它的 field还没有被标记或标记完)
  • 白色:未被扫描对象,如果扫描完成所有对象之后最终为白色的为不可达对象,即垃圾对象(即对象没有被标记到)

标记过程演示

  • 1583131636331
    1583131636331
  • 1583131729874
    1583131729874
  • 1583131855524
    1583131855524

可能出现的问题

  • 1583132245202
    1583132245202
  • 这时候应用程序执行了以下操作A.c=C和B.c=null这样,对象的状态图变成如下情形

  • 1583132314470
    1583132314470
  • 这时候垃圾收集器再去标记扫描的时候就会变成下图这样,这样就会造成漏标:

  • 1583132429116
    1583132429116

解决方法-SATB

SATB snapshot at the beginning

  • 在开始的时候生成一个快照图,标记存活的对象
  • 在并发标记的时候所有被改变的对象入队(在write barrier里把所有旧的引用所指向的对象都变成非白的,如上图中,将B所指向的引用C变成非白的)
  • 可能存在浮动垃圾,将在下次垃圾收集时被收集

SATB详解

  • SATB详解SATB是维持并发GC的一种手段。G1并发的基础就是SATB。SATB可以理解成在GC开始之前对堆内存里的对象做一次快照,此时活的对象就认为是活的,从而形成个对象图。在GC收集的时候,新生代的对象也认为是活的对象,除此之外其他不可达的对象都认为是垃圾对象

  • 如何找到在GC过程中分配的对象呢? 这是收集过程中可能遇到的第二个问题,每个Region记录着两个top-at-mark- start(TAMS)指针,分别为 prevtamst和 next AMS。在TAMS以上的对象就是新分配的,因而被视为隐式 marked。通过这种方式我们就找到了在GC过程中新分配的对象,并把这些对象认为是活的对象

  • 解决了对象在GC过程中分配的问题,那么在GC过程中引用发生变化的问题怎么解决呢?Write Barrier就是对引用字段进行赋值做额外处理。通过 Write Barrier就可以了解到哪些引用对象发生了什么样的变化。

  • SATB仅仅对于在 marking开始阶段进行snapshot"(marked all reachable at markstart)),但是 concurrent的时候并发修改可能造成对象漏标记

    • 对 black新引用了一个 white对象,然后又从gray对象中删除了对该 white对象的引用这样会造成了该whie对象漏标记
    • 对 black新引用了一个 white对象,然后从gray对象删了一个引用该 white对象的 white对象,这样也会造成了该 white对象漏标记
    • 对 black新引用了一个刚new出来的 white对象,没有其他gray对象引用该 White对象这样也会造成了该 white对象漏标记
    • 对于三色算法在 concurrent的时候可能产生的漏标记问题,SATB在 marking阶段中对于从gray对象移除的目标引用对象标记为gray,对于 black引用的新产生的对象标记为 black; 由于是在开始的时候进行snapshot,因而可能存在 Floating Garbage
  • 漏标与误标

    • 误标没什么关系,顶多造成浮动垃圾,在下次gc还是可以回收的,但是漏标的后果是致命的把本应该存活的对象给回收了,从而影响的程序的正确性
    • 漏标的情况只会发生在 白色对象中,且满足以下任意一个条件
      • 并发标记时,应用线程给一个黑色对象的引用类型字段赋值了该白色对象
      • 并发标记时,应用线程删除所有灰色对象到该白色对象的引用(但是此时或许有黑色对象引用该白色对象)
      • 如何解决以上问题?
        • 对于第一种情况,利用 post-write barrier,记录所有新增的引用关系,然后根据这些引用关系为根重新扫描一遍,Write Barrier就是对引用字段进行赋值做额外处理。通过 Write Barrier就可以了解到哪些引用对象发生了什么样的变化
        • 对于第二种情况,利用pre-write barrier,将所有即将被删除的引用关系的旧引用记录下来,最后以这些旧引用为根重新扫描遍,这样就能扫描标记到那个“白色对象”

1.4 G1最佳实践

1.4.1 不断调优暂停时间指标

通过 XX. MaxGcPauseMillis=x 可以设置启动应用程序暂停的时间,G1在运行的时候会根据这个参数选择Cset来满足响应时间的设置。一般情况下这个值设置到100ms或者200ms都是可以的(不同情况下会不一样),但如果设置成50ms就不太合理。暂停时间设置的太短,就会导致出现G1跟不上垃圾产生的速度。最终退化成 Full gc。所以对这个参数的调优是一个持续的过程,逐步调整到最佳状态。

1.4.2 不要设置新生代和老年代的大小

G1收集器在运行的时候会调整新生代和老年代的大小。通过改变代的大小来调整对象晋升的速度以及晋升年龄,从而达到我们为收集器设置的暂停时间目标。设置了新生代大小相当于放弃了G1为我们做的自动调优。我们需要做的只是设置整个堆内存的大小,剩下的交给G1自己去分配各个代的大小即可

1.4.3 关注 Evacuation Failure

Evacuation Failure【Evacuation是拷贝存活对象的意思,是GC步骤之一,GC步骤分为两步全局并发标记(global concurrent marking)和拷贝存活对象(evacuation)】 类似于CMS里面的晋升失败堆空间的垃圾太多导致无法完成 Region之间的拷贝,于是不得不退化成 Full GC来做一次全局范围内的垃圾收集

对比其它收集器

  • G1 VS CMS对比使用mark- sweep的CMS,G1使用的copying算法不会造成内存碎片
  • 对比 Parallel Scavenge(基于 copying)Parallel Old收集器(基于mark- compact-sweep), Parallel会对整个区域做整理导致gc停顿会比较长,而G1只是特定地整理压缩某些Region
    • G1并非一个实时的收集器,与 parallel Scavenge一样,对gc停顿时间的设置并不绝对生效,只是G1有较高的几率保证不超过设定的gc停顿时间。与之前的gc收集器对比,G1会根据用户设定的gc停顿时间智能评估哪几个 region需要被回收可以满足用户的设定

1.5 实验

实验代码

实验参数

实验结果

2020-03-03T11:06:20.055+0800: [GC pause (G1 Humongous Allocation) (young) (initial-mark), 0.0024128 secs][Parallel Time: 1.3 ms, GC Workers: 8]# GC Workers: 8 ,当前的GC线程数为8[GC Worker Start (ms): Min: 178.9, Avg: 178.9, Max: 178.9, Diff: 0.1]# GC线程开始[Ext Root Scanning (ms): Min: 0.3, Avg: 0.4, Max: 0.5, Diff: 0.3, Sum: 3.2]# 根节点扫描[Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]# 更新RSet[Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0][Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Object Copy (ms): Min: 0.6, Avg: 0.7, Max: 0.8, Diff: 0.2, Sum: 5.8][Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]# 处理引用队列[Termination Attempts: Min: 1, Avg: 6.0, Max: 9, Diff: 8, Sum: 48]# 此以上的步骤都是和之前说到的YGC的处理流程是一一对应的[GC Worker Other (ms): Min: 0.0, Avg: 0.1, Max: 0.3, Diff: 0.3, Sum: 0.9][GC Worker Total (ms): Min: 1.2, Avg: 1.2, Max: 1.3, Diff: 0.1, Sum: 10.0][GC Worker End (ms): Min: 180.2, Avg: 180.2, Max: 180.2, Diff: 0.0][Code Root Fixup: 0.0 ms][Code Root Purge: 0.0 ms][Clear CT: 0.1 ms]# 即clear CartTable [Other: 1.0 ms][Choose CSet: 0.0 ms]# 选择要回收的Region[Ref Proc: 0.1 ms][Ref Enq: 0.0 ms]# 引用的处理[Redirty Cards: 0.1 ms][Humongous Register: 0.0 ms][Humongous Reclaim: 0.0 ms][Free CSet: 0.0 ms][Eden: 2048.0K(6144.0K)->0.0B(2048.0K) Survivors: 0.0B->1024.0K Heap: 3990.1K(10.0M)->2847.6K(10.0M)]# 表示执行完YGC之后,整个eden空间的结果,可以看到执行完之后eden空间变成了0;Survivor空间变成1m,这是从新生代来的[Times: user=0.00 sys=0.00, real=0.00 secs] 
2020-03-03T11:06:20.058+0800: [GC concurrent-root-region-scan-start]
2020-03-03T11:06:20.059+0800: [GC pause (G1 Humongous Allocation) (young)2020-03-03T11:06:20.059+0800: [GC concurrent-root-region-scan-end, 0.0011467 secs]
2020-03-03T11:06:20.059+0800: [GC concurrent-mark-start]
, 0.0019177 secs]
# 以上是并发的一些处理[Root Region Scan Waiting: 0.5 ms][Parallel Time: 0.9 ms, GC Workers: 8][GC Worker Start (ms): Min: 182.5, Avg: 182.6, Max: 182.8, Diff: 0.3][Ext Root Scanning (ms): Min: 0.0, Avg: 0.2, Max: 0.3, Diff: 0.3, Sum: 1.2][Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0][Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Object Copy (ms): Min: 0.4, Avg: 0.4, Max: 0.5, Diff: 0.1, Sum: 3.5][Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.1, Sum: 0.4][Termination Attempts: Min: 1, Avg: 4.8, Max: 8, Diff: 7, Sum: 38][GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1][GC Worker Total (ms): Min: 0.4, Avg: 0.7, Max: 0.9, Diff: 0.4, Sum: 5.2][GC Worker End (ms): Min: 183.3, Avg: 183.3, Max: 183.4, Diff: 0.1][Code Root Fixup: 0.0 ms][Code Root Purge: 0.0 ms][Clear CT: 0.1 ms][Other: 0.4 ms][Choose CSet: 0.0 ms][Ref Proc: 0.2 ms][Ref Enq: 0.0 ms][Redirty Cards: 0.1 ms][Humongous Register: 0.0 ms][Humongous Reclaim: 0.0 ms][Free CSet: 0.0 ms][Eden: 1024.0K(2048.0K)->0.0B(1024.0K) Survivors: 1024.0K->1024.0K Heap: 3912.6K(10.0M)->3895.4K(10.0M)]# 第二次YGC的结果[Times: user=0.00 sys=0.00, real=0.00 secs] 
2020-03-03T11:06:20.061+0800: [GC concurrent-mark-end, 0.0015466 secs]
2020-03-03T11:06:20.061+0800: [Full GC (Allocation Failure)  3895K->3731K(10M), 0.0042062 secs][Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 1024.0K->0.0B Heap: 3895.4K(10.0M)->3731.8K(10.0M)], [Metaspace: 3113K->3113K(1056768K)][Times: user=0.03 sys=0.00, real=0.00 secs] 
2020-03-03T11:06:20.066+0800: [GC remark, 0.0000313 secs][Times: user=0.00 sys=0.00, real=0.00 secs] 
2020-03-03T11:06:20.066+0800: [GC concurrent-mark-abort]
完成了
Heapgarbage-first heap   total 10240K, used 4755K [0x00000000ff600000, 0x00000000ff700050, 0x0000000100000000)region size 1024K, 1 young (1024K), 0 survivors (0K)# 堆的空间分配情况Metaspace       used 3238K, capacity 4496K, committed 4864K, reserved 1056768Kclass space    used 352K, capacity 388K, committed 512K, reserved 1048576K

Process finished with exit code 0

1.6 参考文章

  • https://www.php.cn/java-article-357970.html

本文使用 mdnice 排版


http://chatgpt.dhexx.cn/article/aFoWcMEL.shtml

相关文章

G1垃圾回收器详解

文章目录 前言一、思考问题二、官方文档三、基本介绍四、G1的内存模型五、G1的标记过程六、G1的垃圾回收1、G1过程梳理2、Young GC3、Mixed GC4、Full GC 七、参数介绍八、分析各阶段触发时机根据GC日志分析Young GC的触发时机根据GC日志分析并发标记的触发时机根据GC日志分析M…

G1 GC

G1GC基本概念 G1 GC可以看做是CMS GC的重大升级改造G1 GC的全称是Garbage-First,意为垃圾优先,哪一块的垃圾最多就优先清理他。G1 GC最主要的设计目标是:将STW停顿的时间和分布,变成可预期且可配置的。(默认200ms&…

G1垃圾回收器-----基本知识及原理解析

G1介绍(Garbage first) G1主要面向的是服务端的垃圾回收器。在G1之前,JVM的主要垃圾回收器采用的是物理分代的思想,将内存区域严格的划分成年轻代(young GC)和老年代(major GC)&…

可控硅BT136典型应用电路

1.双向可控硅SCR可根据负载功率大小选择97A6(约1A)、TLC336A(约3A)、BT136-500D(约6A)中的一个,选择原则是触发电流要小于25mA。 2.C4取值在0.1 ~ 0.47uF之间&#xff0c…

全能电子地图实时路况_全能SUV与城市SUV的区别在哪?日产奇骏对比本田皓影

在如今的市场中,越来越多的车企将重点放在SUV车型中,尤其今年大量家用SUV涌入市场,特别是紧凑级SUV将这片市场瓜分的“支离破碎”。即使车型越来越多,市场被瓜分的越来越小,但是在很多消费者心中,几个主流品…

音质卓越颜值在线,五款高人气头戴式HIFI音质蓝牙耳机排名

随着我们生活水平的逐渐提高,我们对科技产品的要求也就越来越高,就拿耳机来说,对于很多上班族或是年轻人耳机绝对是出门或是旅行的必备品之一,没有音乐的路途那绝对是缺少了灵魂,那自然耳机的地位也就越发的重要。无线,音效,小巧,这几个因素也就成为了我们选择耳机的最…

服务器购买及宝塔部署环境说明(阿里云为例)

服务器相关知识 为什么程序员都需要自己的服务器? 1、你作为一个程序员,必须要发部自己的网站和项目! 2、联系Linux的操作。 3、自己的远程仓库、远程数据库、远程tomcat…都可以搭建在服务器上 4、联系,Linux进行任意的环境…

Linux学习一概述和环境搭建(入门概述,环境搭建,走近Linux系统)

此文档学习来自b站遇见狂神说,自己做的学习笔记整合。 狂神说Linux 继续Java全栈开发的Linux,而不是运维级别! 我们为什么要在这个时间学习Linux?Java全栈开发的我们要掌握哪些知识?需要准备什么工作? J…

CK6855M1蓝牙离线语音识别灯控模组使用说明书

CK6855M1蓝牙离线语音识别灯控模组使用说明书 一、功能说明 CK6855M1模块是一款专为灯具照明产品设计的离线蓝牙语音识别模组。模组支持红外遥控接收,支持RGBWY灯控制,支持无极调光,支持AD按键调灯以及音频上下曲的控制,支持5.1…

为什么P2P模式下载的人越多速度越快,为什么P2P伤害机械硬盘

台风来临前的夜晚,有点激动不想睡觉,看了几个电影,日本恐怖片,台风雨夜,非常不错,P2P很流畅,观察IP地址大量也是附近的,江浙沪,难道也都在迎台风看电影? 大家…

伊人在线高清视频 index.php,《天元》“畅音阁”首发飞行技能视频

直面一款网游给人感觉的优异度最大成分中的重中之重就是第一感——“视听”印象。一直以来,游戏音乐以及画面成分的优异华丽与否直接关系到了玩家对于此款游戏的好感度,无可厚非。而《天元》特别针对于此,倾情打造出一篇关于对于其所有“视听…

迅雷跃居全球BT市场第一

据torrentfreak报道,根据早些时候海盗湾公布的一些数据表明,全球BT软件的使用者,大约有三分之一来自中国,而来自美国的仅有8%,迅雷成为全球BitTorrent市场份额最大的客户端。 迅雷本身并不是一个专门使用P2P的BT客户端…

11个资源强大的网站!知乎超20万人强烈推荐,再也不怕资源难找

在我们日常工作学习中难免就需要在网站搜索资料,这时候一定需要一个能够帮你搜索一切你想要的资源,从而为你剩下一大半时间,那么今天为大家整理了11个超级好用的黑科技资源搜索网站,帮你解决因为上网找不到合适的资源而发愁&#…

Linux6.8搭建sftp服务

最近因公司工作需要用到sftp服务器,参考网上各种方法,但被网上各种方法尤其是权限设置问题搞得晕头转向,现在将自己搭建过程总结了一下,提供给大家希望有所帮助。 sftp是Secure FileTransfer Protocol的缩写,安全文件传…

107-161JS

107-161 target事件委托正则表达式捕获exec特性 this指向改变this指向ES6定义变量ES6箭头函数ES6解构赋值ES6展开运算符ES6模块化初识面向对象创建对象构造函数注意问题面向对象的原型ES6-Class面向对象继承ES6继承Aajaxajax同步异步请求方式1ajax的封装回调地狱promise基础语法…

fastjson与net.sf.json区别

在现在的开发当中&#xff0c; 绝大多数引用阿里巴巴的fastjson。 当然net.sf.json同样可以使用。 一、引入com.alibaba.fastjson包 <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency><groupId>com.alibaba</groupId>…

SftpGo:一款高性能的sftp server服务

SftpGo是一款高性能、功能齐全、易用可配置的一款sftp server 服务&#xff0c;基于go开发。目前在linux、macos下均可以稳定运行(windows个人未测试)。数据可以持久化到主流的数据库&#xff0c;诸如Mysql、PostgreSQL、Sqlilte. sftpgo主要组成 服务端主程序: sftpgosever…

SF简易IDC系统V1.0免授权

介绍&#xff1a; 提供官方EP接口&#xff08;可免费无限开通主机和CDN&#xff09; 2.站长可自己上架主机定义金额&#xff0c;更有自定义模式&#xff08;客户自己选择配置&#xff0c;根据配置进行付钱&#xff09; 3.独立支付接口 —————— 源码无后门&#xff0c;免授…

CentOS7.6-搭建SFTP服务

1.需求 搭建多账号SFTP&#xff0c;不同的用户只能够查看自己所属的目录禁止SFTP账号通过SSH连接SFTP 用户demo1、demo2&#xff0c;所属目录&#xff1a;/data/sftp/demo1、/data/sftp/demo2 2.操作步骤 创建目录 mkdir /data/sftp/{demo1,demo2} -p创建用户组sftp groupad…

Fastdfs安装(超级无敌详细版)

注&#xff1a;如果还没开始安装&#xff0c;请根据本教程完整走完&#xff0c;每个教程都有差异&#xff0c;拼拼凑凑更容易出问题。本教程各个包的版本都是亲自试过没问题的&#xff0c;熟练的半小时内即可安装好。 由于在学习阶段&#xff0c;看了许多别人的教程和视频&…