GC算法實(shí)現(xiàn)篇之并發(fā)標(biāo)記清除
Concurrent Mark and Sweep(并發(fā)標(biāo)記-清除)
CMS的官方名稱為 “Mostly Concurrent Mark and Sweep Garbage Collector”(主要并發(fā)-標(biāo)記-清除-垃圾收集器). 其對年輕代采用并行 STW方式的 mark-copy (標(biāo)記-復(fù)制)算法, 對老年代主要使用并發(fā) mark-sweep (標(biāo)記-清除)算法。
CMS的設(shè)計(jì)目標(biāo)是避免在老年代垃圾收集時(shí)出現(xiàn)長時(shí)間的卡頓。主要通過兩種手段來達(dá)成此目標(biāo)。
- 第一, 不對老年代進(jìn)行整理, 而是使用空閑列表(free-lists)來管理內(nèi)存空間的回收。
- 第二, 在 mark-and-sweep (標(biāo)記-清除) 階段的大部分工作和應(yīng)用線程一起并發(fā)執(zhí)行。
也就是說, 在這些階段并沒有明顯的應(yīng)用線程暫停。但值得注意的是, 它仍然和應(yīng)用線程爭搶CPU時(shí)間。默認(rèn)情況下, CMS 使用的并發(fā)線程數(shù)等于CPU內(nèi)核數(shù)的 1/4
。
通過以下選項(xiàng)來指定CMS垃圾收集器:
java -XX:+UseConcMarkSweepGC com.mypackages.MyExecutableClass
如果服務(wù)器是多核CPU,并且主要調(diào)優(yōu)目標(biāo)是降低延遲, 那么使用CMS是個很明智的選擇. 減少每一次GC停頓的時(shí)間,會直接影響到終端用戶對系統(tǒng)的體驗(yàn), 用戶會認(rèn)為系統(tǒng)非常靈敏。 因?yàn)槎鄶?shù)時(shí)候都有部分CPU資源被GC消耗, 所以在CPU資源受限的情況下,CMS會比并行GC的吞吐量差一些。
和前面的GC算法一樣, 我們先來看看CMS算法在實(shí)際應(yīng)用中的GC日志, 其中包括一次 minor GC, 以及一次 major GC 停頓:
****-05-26T16:23:07.219-0200:?64.322:?[GC?(Allocation?Failure)?64.322:? ?[ParNew:?613404K->68068K(613440K),?0.1020465?secs] ?10885349K->10880154K(12514816K),?0.1021309?secs] ?[Times:?user=0.78?sys=0.01,?real=0.11?secs] ****-05-26T16:23:07.321-0200:?64.425:?[GC?(CMS?Initial?Mark)? ?[1?CMS-initial-mark:?10812086K(11901376K)]? ?10887844K(12514816K),?0.0001997?secs]? ?[Times:?user=0.00?sys=0.00,?real=0.00?secs] ****-05-26T16:23:07.321-0200:?64.425:?[CMS-concurrent-mark-start] ****-05-26T16:23:07.357-0200:?64.460:?[CMS-concurrent-mark:?0.035/0.035?secs]? ?[Times:?user=0.07?sys=0.00,?real=0.03?secs] ****-05-26T16:23:07.357-0200:?64.460:?[CMS-concurrent-preclean-start] ****-05-26T16:23:07.373-0200:?64.476:?[CMS-concurrent-preclean:?0.016/0.016?secs]? ?[Times:?user=0.02?sys=0.00,?real=0.02?secs] ****-05-26T16:23:07.373-0200:?64.476:?[CMS-concurrent-abortable-preclean-start] ****-05-26T16:23:08.446-0200:?65.550:?[CMS-concurrent-abortable-preclean:?0.167/1.074?secs]? ?[Times:?user=0.20?sys=0.00,?real=1.07?secs] ****-05-26T16:23:08.447-0200:?65.550:?[GC?(CMS?Final?Remark)? ?[YG occupancy:?387920?K?(613440?K)] ?65.550:?[Rescan?(parallel)?,?0.0085125?secs] ?65.559:?[weak refs processing,?0.0000243?secs] ?65.559:?[class?unloading,?0.0013120?secs] ?65.560:?[scrub symbol table,?0.0008345?secs] ?65.561:?[scrub?string?table,?0.0001759?secs] ?[1?CMS-remark:?10812086K(11901376K)]? ?11200006K(12514816K),?0.0110730?secs]? ?[Times:?user=0.06?sys=0.00,?real=0.01?secs] ****-05-26T16:23:08.458-0200:?65.561:?[CMS-concurrent-sweep-start] ****-05-26T16:23:08.485-0200:?65.588:?[CMS-concurrent-sweep:?0.027/0.027?secs]? ?[Times:?user=0.03?sys=0.00,?real=0.03?secs] ****-05-26T16:23:08.485-0200:?65.589:?[CMS-concurrent-reset-start] ****-05-26T16:23:08.497-0200:?65.601:?[CMS-concurrent-reset:?0.012/0.012?secs]? ?[Times:?user=0.01?sys=0.00,?real=0.01?secs]
Minor GC(小型GC)
日志中的第一次GC事件是清理年輕代的小型GC(Minor GC)。讓我們來分析 CMS 垃圾收集器的行為:
****-05-26T16:23:07.219-0200: 64.322:[GC(Allocation Failure) 64.322: [ParNew: 613404K->68068K(613440K), 0.1020465 secs] 10885349K->10880154K(12514816K), 0.1021309 secs] [Times: user=0.78 sys=0.01, real=0.11 secs]
****-05-26T16:23:07.219-0200
– GC事件開始的時(shí)間. 其中-0200
表示西二時(shí)區(qū),而中國所在的東8區(qū)為 +0800
。
64.322
– GC事件開始時(shí),相對于JVM啟動時(shí)的間隔時(shí)間,單位是秒。
GC
– 用來區(qū)分 Minor GC 還是 Full GC 的標(biāo)志。GC
表明這是一次小型GC(Minor GC)
Allocation Failure
– 觸發(fā)垃圾收集的原因。本次GC事件, 是由于年輕代中沒有適當(dāng)?shù)目臻g存放新的數(shù)據(jù)結(jié)構(gòu)引起的。
ParNew
– 垃圾收集器的名稱。這個名字表示的是在年輕代中使用的: 并行的 標(biāo)記-復(fù)制(mark-copy), 全線暫停(STW)垃圾收集器, 專門設(shè)計(jì)了用來配合老年代使用的 Concurrent Mark & Sweep 垃圾收集器。
613404K->68068K
– 在垃圾收集之前和之后的年輕代使用量。
(613440K)
– 年輕代的總大小。
0.1020465 secs
– 垃圾收集器在 w/o final cleanup 階段消耗的時(shí)間
10885349K->10880154K
– 在垃圾收集之前和之后堆內(nèi)存的使用情況。
(12514816K)
– 可用堆的總大小。
0.1021309 secs
– 垃圾收集器在標(biāo)記和復(fù)制年輕代存活對象時(shí)所消耗的時(shí)間。包括和ConcurrentMarkSweep收集器的通信開銷, 提升存活時(shí)間達(dá)標(biāo)的對象到老年代,以及垃圾收集后期的一些最終清理。
[Times: user=0.78 sys=0.01, real=0.11 secs]
– GC事件的持續(xù)時(shí)間, 通過三個部分來衡量:
- user – 在此次垃圾回收過程中, 由GC線程所消耗的總的CPU時(shí)間。
- sys – GC過程中中操作系統(tǒng)調(diào)用和系統(tǒng)等待事件所消耗的時(shí)間。
- real – 應(yīng)用程序暫停的時(shí)間。在并行GC(Parallel GC)中, 這個數(shù)字約等于: (user time + system time)/GC線程數(shù)。 這里使用的是8個線程。 請注意,總是有固定比例的處理過程是不能并行化的。
從上面的日志可以看出,在GC之前總的堆內(nèi)存使用量為 10,885,349K, 年輕代的使用量為 613,404K。這意味著老年代使用量等于 10,271,945K。GC之后,年輕代的使用量減少了 545,336K, 而總的堆內(nèi)存使用只下降了 5,195K。可以算出有 540,141K 的對象從年輕代提升到老年代。
Full GC(完全GC)
現(xiàn)在, 我們已經(jīng)熟悉了如何解讀GC日志, 接下來將介紹一種完全不同的日志格式。下面這一段很長很長的日志, 就是CMS對老年代進(jìn)行垃圾收集時(shí)輸出的各階段日志。為了簡潔,我們對這些階段逐個介紹。 首先來看CMS收集器整個GC事件的日志:
****-05-26T16:23:07.321-0200:?64.425:?[GC?(CMS?Initial?Mark)? ?[1?CMS-initial-mark:?10812086K(11901376K)]? ?10887844K(12514816K),?0.0001997?secs]? ?[Times:?user=0.00?sys=0.00,?real=0.00?secs] ****-05-26T16:23:07.321-0200:?64.425:?[CMS-concurrent-mark-start] ****-05-26T16:23:07.357-0200:?64.460:?[CMS-concurrent-mark:?0.035/0.035?secs]? ?[Times:?user=0.07?sys=0.00,?real=0.03?secs] ****-05-26T16:23:07.357-0200:?64.460:?[CMS-concurrent-preclean-start] ****-05-26T16:23:07.373-0200:?64.476:?[CMS-concurrent-preclean:?0.016/0.016?secs]? ?[Times:?user=0.02?sys=0.00,?real=0.02?secs] ****-05-26T16:23:07.373-0200:?64.476:?[CMS-concurrent-abortable-preclean-start] ****-05-26T16:23:08.446-0200:?65.550:?[CMS-concurrent-abortable-preclean:?0.167/1.074?secs]? ?[Times:?user=0.20?sys=0.00,?real=1.07?secs] ****-05-26T16:23:08.447-0200:?65.550:?[GC?(CMS?Final?Remark)? ?[YG occupancy:?387920?K?(613440?K)] ?65.550:?[Rescan?(parallel)?,?0.0085125?secs] ?65.559:?[weak refs processing,?0.0000243?secs] ?65.559:?[class?unloading,?0.0013120?secs] ?65.560:?[scrub symbol table,?0.0008345?secs] ?65.561:?[scrub?string?table,?0.0001759?secs] ?[1?CMS-remark:?10812086K(11901376K)]? ?11200006K(12514816K),?0.0110730?secs]? ?[Times:?user=0.06?sys=0.00,?real=0.01?secs] ****-05-26T16:23:08.458-0200:?65.561:?[CMS-concurrent-sweep-start] ****-05-26T16:23:08.485-0200:?65.588:?[CMS-concurrent-sweep:?0.027/0.027?secs]? ?[Times:?user=0.03?sys=0.00,?real=0.03?secs] ****-05-26T16:23:08.485-0200:?65.589:?[CMS-concurrent-reset-start] ****-05-26T16:23:08.497-0200:?65.601:?[CMS-concurrent-reset:?0.012/0.012?secs]? ?[Times:?user=0.01?sys=0.00,?real=0.01?secs]
只是要記住 —— 在實(shí)際情況下, 進(jìn)行老年代的并發(fā)回收時(shí), 可能會伴隨著多次年輕代的小型GC. 在這種情況下, 大型GC的日志中就會摻雜著多次小型GC事件, 像前面所介紹的一樣。
階段 1: Initial Mark(初始標(biāo)記). 這是第一次STW事件。 此階段的目標(biāo)是標(biāo)記老年代中所有存活的對象, 包括 GC ROOR 的直接引用, 以及由年輕代中存活對象所引用的對象。 后者也非常重要, 因?yàn)槔夏甏仟?dú)立進(jìn)行回收的。
****-05-26T16:23:07.321-0200: 64.421: [GC (CMS Initial Mark1 [1 CMS-initial-mark: 10812086K1(11901376K)1] 10887844K1(12514816K)1, 0.0001997 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]1
****-05-26T16:23:07.321-0200: 64.42
– GC事件開始的時(shí)間. 其中 -0200
是時(shí)區(qū),而中國所在的東8區(qū)為 +0800。 而 64.42 是相對于JVM啟動的時(shí)間。 下面的其他階段也是一樣,所以就不再重復(fù)介紹。
CMS Initial Mark
– 垃圾回收的階段名稱為 “Initial Mark”。 標(biāo)記所有的 GC Root。
10812086K
– 老年代的當(dāng)前使用量。
(11901376K)
– 老年代中可用內(nèi)存總量。
10887844K
– 當(dāng)前堆內(nèi)存的使用量。
(12514816K)
– 可用堆的總大小。
0.0001997 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
– 此次暫停的持續(xù)時(shí)間, 以 user, system 和 real time 3個部分進(jìn)行衡量。
階段 2: Concurrent Mark(并發(fā)標(biāo)記). 在此階段, 垃圾收集器遍歷老年代, 標(biāo)記所有的存活對象, 從前一階段 “Initial Mark” 找到的 root 根開始算起。 顧名思義, “并發(fā)標(biāo)記”階段, 就是與應(yīng)用程序同時(shí)運(yùn)行,不用暫停的階段。 請注意, 并非所有老年代中存活的對象都在此階段被標(biāo)記, 因?yàn)樵跇?biāo)記過程中對象的引用關(guān)系還在發(fā)生變化。
在上面的示意圖中, “Current object” 旁邊的一個引用被標(biāo)記線程并發(fā)刪除了。
****-05-26T16:23:07.321-0200: 64.425: [CMS-concurrent-mark-start] ****-05-26T16:23:07.357-0200: 64.460: [CMS-concurrent-mark1: 035/0.035 secs1] [Times: user=0.07 sys=0.00, real=0.03 secs]1
CMS-concurrent-mark
– 并發(fā)標(biāo)記(“Concurrent Mark”) 是CMS垃圾收集中的一個階段, 遍歷老年代并標(biāo)記所有的存活對象。
035/0.035 secs
– 此階段的持續(xù)時(shí)間, 分別是運(yùn)行時(shí)間和相應(yīng)的實(shí)際時(shí)間。
[Times: user=0.07 sys=0.00, real=0.03 secs]
– Times
這部分對并發(fā)階段來說沒多少意義, 因?yàn)槭菑牟l(fā)標(biāo)記開始時(shí)計(jì)算的,而這段時(shí)間內(nèi)不僅并發(fā)標(biāo)記在運(yùn)行,程序也在運(yùn)行
階段 3: Concurrent Preclean(并發(fā)預(yù)清理). 此階段同樣是與應(yīng)用線程并行執(zhí)行的, 不需要停止應(yīng)用線程。 因?yàn)榍耙浑A段是與程序并發(fā)進(jìn)行的,可能有一些引用已經(jīng)改變。如果在并發(fā)標(biāo)記過程中發(fā)生了引用關(guān)系變化,JVM會(通過“Card”)將發(fā)生了改變的區(qū)域標(biāo)記為“臟”區(qū)(這就是所謂的卡片標(biāo)記,Card Marking)。
在預(yù)清理階段,這些臟對象會被統(tǒng)計(jì)出來,從他們可達(dá)的對象也被標(biāo)記下來。此階段完成后, 用以標(biāo)記的 card 也就被清空了。
此外, 本階段也會執(zhí)行一些必要的細(xì)節(jié)處理, 并為 Final Remark 階段做一些準(zhǔn)備工作。
****-05-26T16:23:07.357-0200: 64.460: [CMS-concurrent-preclean-start] ****-05-26T16:23:07.373-0200: 64.476: [CMS-concurrent-preclean: 0.016/0.016 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
CMS-concurrent-preclean
– 并發(fā)預(yù)清理階段, 統(tǒng)計(jì)此前的標(biāo)記階段中發(fā)生了改變的對象。
0.016/0.016 secs
– 此階段的持續(xù)時(shí)間, 分別是運(yùn)行時(shí)間和對應(yīng)的實(shí)際時(shí)間。
[Times: user=0.02 sys=0.00, real=0.02 secs]
– Times 這部分對并發(fā)階段來說沒多少意義, 因?yàn)槭菑牟l(fā)標(biāo)記開始時(shí)計(jì)算的,而這段時(shí)間內(nèi)不僅GC的并發(fā)標(biāo)記在運(yùn)行,程序也在運(yùn)行。
階段 4: Concurrent Abortable Preclean(并發(fā)可取消的預(yù)清理). 此階段也不停止應(yīng)用線程. 本階段嘗試在 STW 的 Final Remark 之前盡可能地多做一些工作。本階段的具體時(shí)間取決于多種因素, 因?yàn)樗h(huán)做同樣的事情,直到滿足某個退出條件( 如迭代次數(shù), 有用工作量, 消耗的系統(tǒng)時(shí)間,等等)。
****-05-26T16:23:07.373-0200: 64.476: [CMS-concurrent-abortable-preclean-start] ****-05-26T16:23:08.446-0200: 65.550: [CMS-concurrent-abortable-preclean1: 0.167/1.074 secs2] [Times: user=0.20 sys=0.00, real=1.07 secs]3
CMS-concurrent-abortable-preclean
– 此階段的名稱: “Concurrent Abortable Preclean”。
0.167/1.074 secs
– 此階段的持續(xù)時(shí)間, 運(yùn)行時(shí)間和對應(yīng)的實(shí)際時(shí)間。有趣的是, 用戶時(shí)間明顯比時(shí)鐘時(shí)間要小很多。通常情況下我們看到的都是時(shí)鐘時(shí)間小于用戶時(shí)間, 這意味著因?yàn)橛幸恍┎⑿泄ぷ? 所以運(yùn)行時(shí)間才會小于使用的CPU時(shí)間。這里只進(jìn)行了少量的工作 — 0.167秒的CPU時(shí)間,GC線程經(jīng)歷了很多系統(tǒng)等待。從本質(zhì)上講,GC線程試圖在必須執(zhí)行 STW暫停之前等待盡可能長的時(shí)間。默認(rèn)條件下,此階段可以持續(xù)最多5秒鐘。
[Times: user=0.20 sys=0.00, real=1.07 secs]
– “Times” 這部分對并發(fā)階段來說沒多少意義, 因?yàn)槭菑牟l(fā)標(biāo)記開始時(shí)計(jì)算的,而這段時(shí)間內(nèi)不僅GC的并發(fā)標(biāo)記線程在運(yùn)行,程序也在運(yùn)行
此階段可能顯著影響STW停頓的持續(xù)時(shí)間, 并且有許多重要的配置選項(xiàng)和失敗模式。
階段 5: Final Remark(最終標(biāo)記). 這是此次GC事件中第二次(也是最后一次)STW階段。本階段的目標(biāo)是完成老年代中所有存活對象的標(biāo)記. 因?yàn)橹暗?preclean 階段是并發(fā)的, 有可能無法跟上應(yīng)用程序的變化速度。所以需要 STW暫停來處理復(fù)雜情況。
通常CMS會嘗試在年輕代盡可能空的情況運(yùn)行 final remark 階段, 以免接連多次發(fā)生 STW 事件。
看起來稍微比之前的階段要復(fù)雜一些:
****-05-26T16:23:08.447-0200: 65.550: [GC (CMS Final Remark) [YG occupancy: 387920 K (613440 K)] 65.550: [Rescan (parallel) , 0.0085125 secs] 65.559: [weak refs processing, 0.0000243 secs] 65.559: [class unloading, 0.0013120 secs] 65.560: [scrub string table, 0.0001759 secs] [1 CMS-remark: 10812086K(11901376K)] 11200006K(12514816K),0.0110730 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
****-05-26T16:23:08.447-0200: 65.550
– GC事件開始的時(shí)間. 包括時(shí)鐘時(shí)間,以及相對于JVM啟動的時(shí)間. 其中-0200
表示西二時(shí)區(qū),而中國所在的東8區(qū)為 +0800
。
CMS Final Remark
– 此階段的名稱為 “Final Remark”, 標(biāo)記老年代中所有存活的對象,包括在此前的并發(fā)標(biāo)記過程中創(chuàng)建/修改的引用。
YG occupancy: 387920 K (613440 K)
– 當(dāng)前年輕代的使用量和總?cè)萘俊?/p>
[Rescan (parallel) , 0.0085125 secs]
– 在程序暫停時(shí)重新進(jìn)行掃描(Rescan),以完成存活對象的標(biāo)記。此時(shí) rescan 是并行執(zhí)行的,消耗的時(shí)間為 0.0085125秒。
weak refs processing, 0.0000243 secs]65.559
– 處理弱引用的第一個子階段(sub-phases)。 顯示的是持續(xù)時(shí)間和開始時(shí)間戳。
class unloading, 0.0013120 secs]65.560
– 第二個子階段, 卸載不使用的類。 顯示的是持續(xù)時(shí)間和開始的時(shí)間戳。
scrub string table, 0.0001759 secs
– 最后一個子階段, 清理持有class級別 metadata 的符號表(symbol tables),以及內(nèi)部化字符串對應(yīng)的 string tables。當(dāng)然也顯示了暫停的時(shí)鐘時(shí)間。
10812086K(11901376K)
– 此階段完成后老年代的使用量和總?cè)萘?/p>
11200006K(12514816K)
– 此階段完成后整個堆內(nèi)存的使用量和總?cè)萘?/p>
0.0110730 secs
– 此階段的持續(xù)時(shí)間。
[Times: user=0.06 sys=0.00, real=0.01 secs]
– GC事件的持續(xù)時(shí)間, 通過不同的類別來衡量: user, system and real time。
在5個標(biāo)記階段完成之后, 老年代中所有的存活對象都被標(biāo)記了, 現(xiàn)在GC將清除所有不使用的對象來回收老年代空間:
階段 6: Concurrent Sweep(并發(fā)清除). 此階段與應(yīng)用程序并發(fā)執(zhí)行,不需要STW停頓。目的是刪除未使用的對象,并收回他們占用的空間。
****-05-26T16:23:08.458-0200: 65.561: [CMS-concurrent-sweep-start] ****-05-26T16:23:08.485-0200: 65.588: [CMS-concurrent-sweep: 0.027/0.027 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
CMS-concurrent-sweep
– 此階段的名稱, “Concurrent Sweep”, 清除未被標(biāo)記、不再使用的對象以釋放內(nèi)存空間。
0.027/0.027 secs
– 此階段的持續(xù)時(shí)間, 分別是運(yùn)行時(shí)間和實(shí)際時(shí)間
[Times: user=0.03 sys=0.00, real=0.03 secs]
– “Times”部分對并發(fā)階段來說沒有多少意義, 因?yàn)槭菑牟l(fā)標(biāo)記開始時(shí)計(jì)算的,而這段時(shí)間內(nèi)不僅是并發(fā)標(biāo)記在運(yùn)行,程序也在運(yùn)行。
階段 7: Concurrent Reset(并發(fā)重置). 此階段與應(yīng)用程序并發(fā)執(zhí)行,重置CMS算法相關(guān)的內(nèi)部數(shù)據(jù), 為下一次GC循環(huán)做準(zhǔn)備。
****-05-26T16:23:08.485-0200: 65.589: [CMS-concurrent-reset-start] ****-05-26T16:23:08.497-0200: 65.601: [CMS-concurrent-reset: 0.012/0.012 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
CMS-concurrent-reset
– 此階段的名稱, “Concurrent Reset”, 重置CMS算法的內(nèi)部數(shù)據(jù)結(jié)構(gòu), 為下一次GC循環(huán)做準(zhǔn)備。
0.012/0.012 secs
– 此階段的持續(xù)時(shí)間, 分別是運(yùn)行時(shí)間和對應(yīng)的實(shí)際時(shí)間
[Times: user=0.01 sys=0.00, real=0.01 secs]
– “Times”部分對并發(fā)階段來說沒多少意義, 因?yàn)槭菑牟l(fā)標(biāo)記開始時(shí)計(jì)算的,而這段時(shí)間內(nèi)不僅GC線程在運(yùn)行,程序也在運(yùn)行。
總之, CMS垃圾收集器在減少停頓時(shí)間上做了很多給力的工作, 大量的并發(fā)線程執(zhí)行的工作并不需要暫停應(yīng)用線程。 當(dāng)然, CMS也有一些缺點(diǎn),其中最大的問題就是老年代內(nèi)存碎片問題, 在某些情況下GC會造成不可預(yù)測的暫停時(shí)間, 特別是堆內(nèi)存較大的情況下。
原文鏈接:https://plumbr.io/handbook/garbage-collection-algorithms-implementations#concurrent-mark-and-sweep
以上就是GC算法實(shí)現(xiàn)篇之并發(fā)標(biāo)記清除的詳細(xì)內(nèi)容,更多關(guān)于GC算法并發(fā)標(biāo)記清除的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
論java如何通過反射獲得方法真實(shí)參數(shù)名及擴(kuò)展研究
這篇文章主要為大家介紹了java如何通過反射獲得方法的真實(shí)參數(shù)名以及擴(kuò)展研究,有需要的朋友可以借鑒參考下,希望能夠有所幫助祝大家多多進(jìn)步早日升職加薪2022-01-01詳解Eclipse提交項(xiàng)目到GitHub以及解決代碼沖突
這篇文章主要介紹了詳解Eclipse提交項(xiàng)目到GitHub以及解決代碼沖突,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-03-03SpringBoot + Spring Cloud Consul 服務(wù)注冊和發(fā)現(xiàn)詳細(xì)解析
這篇文章主要介紹了SpringBoot + Spring Cloud Consul 服務(wù)注冊和發(fā)現(xiàn),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07Java中Object.equals和String.equals的區(qū)別詳解
這篇文章主要給大家介紹了Java中Object.equals和String.equals的區(qū)別,文中通過一個小示例讓大家輕松的明白這兩者的區(qū)別,對大家具有一定的參考價(jià)值,需要的朋友們下面來一起看看吧。2017-04-04Java實(shí)現(xiàn)單鏈表SingleLinkedList增刪改查及反轉(zhuǎn) 逆序等
單鏈表是鏈表的其中一種基本結(jié)構(gòu)。一個最簡單的結(jié)點(diǎn)結(jié)構(gòu)如圖所示,它是構(gòu)成單鏈表的基本結(jié)點(diǎn)結(jié)構(gòu)。在結(jié)點(diǎn)中數(shù)據(jù)域用來存儲數(shù)據(jù)元素,指針域用于指向下一個具有相同結(jié)構(gòu)的結(jié)點(diǎn)。 因?yàn)橹挥幸粋€指針結(jié)點(diǎn),稱為單鏈表2021-10-10Spring中的DefaultResourceLoader使用方法解讀
這篇文章主要介紹了Spring中的DefaultResourceLoader使用方法解讀,DefaultResourceLoader是spring提供的一個默認(rèn)的資源加載器,DefaultResourceLoader實(shí)現(xiàn)了ResourceLoader接口,提供了基本的資源加載能力,需要的朋友可以參考下2024-02-02java swing實(shí)現(xiàn)QQ賬號密碼輸入框
這篇文章主要為大家詳細(xì)介紹了Java swing實(shí)現(xiàn)QQ賬號密碼輸入框,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-06-06java使用MulticastSocket實(shí)現(xiàn)組播
這篇文章主要為大家詳細(xì)介紹了java使用MulticastSocket實(shí)現(xiàn)組播,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01