JVM初探:內存分配、GC原理與垃圾收集器

JVM內存的分配與回收大致可分為如下4個步驟: 何時分配 -> 怎樣分配 -> 何時回收 -> 怎樣回收.
除了在概念上可簡單認為new時分配外, 我們著重介紹后面的3個步驟:


I. 怎樣分配- JVM內存分配策略

對象內存主要分配在新生代Eden區, 如果啟用了本地線程分配緩沖, 則優先在TLAB上分配, 少數情況能會直接分配在老年代, 或被拆分成標量類型在棧上分配(JIT優化). 分配的規則并不是百分百固定, 細節主要取決于垃圾收集器組合, 以及VM內存相關的參數.


對象分配

  • 優先在Eden區分配
    在JVM內存模型一文中, 我們大致了解了VM年輕代堆內存可以劃分為一塊Eden區和兩塊Survivor區. 在大多數情況下, 對象在新生代Eden區中分配, 當Eden區沒有足夠空間分配時, VM發起一次Minor GC, 將Eden區和其中一塊Survivor區內尚存活的對象放入另一塊Survivor區域, 如果在Minor GC期間發現新生代存活對象無法放入空閑的Survivor區, 則會通過空間分配擔保機制使對象提前進入老年代(空間分配擔保見下).
  • 大對象直接進入老年代
    Serial和ParNew兩款收集器提供了-XX:PretenureSizeThreshold的參數, 令大于該值的大對象直接在老年代分配, 這樣做的目的是避免在Eden區和Survivor區之間產生大量的內存復制(大對象一般指?需要大量連續內存的Java對象, 如很長的字符串和數組), 因此大對象容易導致還有不少空閑內存就提前觸發GC以獲取足夠的連續空間.

對象晉升

  • 年齡閾值
    VM為每個對象定義了一個對象年齡(Age)計數器, 對象在Eden出生如果經第一次Minor GC后仍然存活, 且能被Survivor容納的話, 將被移動到Survivor空間中, 并將年齡設為1. 以后對象在Survivor區中每熬過一次Minor GC年齡就+1. 當增加到一定程度(-XX:MaxTenuringThreshold, 默認15), 將會晉升到老年代.
  • 提前晉升: 動態年齡判定
    然而VM并不總是要求對象的年齡必須達到MaxTenuringThreshold才能晉升老年代:?如果在Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半, 年齡大于或等于該年齡的對象就可以直接進入老年代, 而無須等到晉升年齡.

II. 何時回收-對象生死判定

(哪些內存需要回收/何時回收)

在堆里面存放著Java世界中幾乎所有的對象實例, 垃圾收集器在對堆進行回收前, 第一件事就是判斷哪些對象已死(可回收).


可達性分析算法

在主流商用語言(如Java、C#)的主流實現中, 都是通過可達性分析算法來判定對象是否存活的: 通過一系列的稱為?GC Roots?的對象作為起點, 然后向下搜索; 搜索所走過的路徑稱為引用鏈/Reference Chain, 當一個對象到?GC Roots?沒有任何引用鏈相連時, 即該對象不可達, 也就說明此對象是不可用的, 如下圖:?Object5、6、7?雖然互有關聯, 但它們到GC Roots是不可達的, 因此也會被判定為可回收的對象:

  • 在Java, 可作為GC Roots的對象包括:
    1. 方法區: 類靜態屬性引用的對象;
    2. 方法區: 常量引用的對象;
    3. 虛擬機棧(本地變量表)中引用的對象.
    4. 本地方法棧JNI(Native方法)中引用的對象。

注: 即使在可達性分析算法中不可達的對象, VM也并不是馬上對其回收, 因為要真正宣告一個對象死亡, 至少要經歷兩次標記過程: 第一次是在可達性分析后發現沒有與GC Roots相連接的引用鏈, 第二次是GC對在F-Queue執行隊列中的對象進行的小規模標記(對象需要覆蓋finalize()方法且沒被調用過).


III. GC原理- 垃圾收集算法

分代收集算法 VS 分區收集算法

  • 分代收集
    當前主流VM垃圾收集都采用”分代收集”(Generational Collection)算法, 這種算法會根據對象存活周期的不同將內存劃分為幾塊, 如JVM中的?新生代老年代永久代. 這樣就可以根據各年代特點分別采用最適當的GC算法:

    ?

    • 在新生代: 每次垃圾收集都能發現大批對象已死, 只有少量存活. 因此選用復制算法,?只需要付出少量存活對象的復制成本就可以完成收集.
    • 在老年代: 因為對象存活率高、沒有額外空間對它進行分配擔保, 就必須采用“標記—清理”“標記—整理”算法來進行回收,?不必進行內存復制, 且直接騰出空閑內存.
  • 分區收集
    上面介紹的分代收集算法是將對象的生命周期按長短劃分為兩個部分, 而分區算法則將整個堆空間劃分為連續的不同小區間, 每個小區間獨立使用, 獨立回收. 這樣做的好處是可以控制一次回收多少個小區間.
    在相同條件下, 堆空間越大, 一次GC耗時就越長, 從而產生的停頓也越長. 為了更好地控制GC產生的停頓時間, 將一塊大的內存區域分割為多個小塊, 根據目標停頓時間, 每次合理地回收若干個小區間(而不是整個堆), 從而減少一次GC所產生的停頓.

分代收集

新生代-復制算法

該算法的核心是將可用內存按容量劃分為大小相等的兩塊, 每次只用其中一塊, 當這一塊的內存用完, 就將還存活的對象復制到另外一塊上面, 然后把已使用過的內存空間一次清理掉.

(圖片來源:?jvm垃圾收集算法)

這使得每次只對其中一塊內存進行回收, 分配也就不用考慮內存碎片等復雜情況, 實現簡單且運行高效.

現代商用VM的新生代均采用復制算法, 但由于新生代中的98%的對象都是生存周期極短的, 因此并不需完全按照1∶1的比例劃分新生代空間, 而是將新生代劃分為一塊較大的Eden區和兩塊較小的Survivor區(HotSpot默認Eden和Survivor的大小比例為8∶1), 每次只用Eden和其中一塊Survivor. 當發生MinorGC時, 將Eden和Survivor中還存活著的對象一次性地拷貝到另外一塊Survivor上, 最后清理掉Eden和剛才用過的Survivor的空間. 當Survivor空間不夠用(不足以保存尚存活的對象)時, 需要依賴老年代進行空間分配擔保機制, 這部分內存直接進入老年代.


老年代-標記清除算法

該算法分為“標記”和“清除”兩個階段:?首先標記出所有需要回收的對象(可達性分析), 在標記完成后統一清理掉所有被標記的對象.

該算法會有以下兩個問題:
1. 效率問題: 標記和清除過程的效率都不高;
2. 空間問題: 標記清除后會產生大量不連續的內存碎片, 空間碎片太多可能會導致在運行過程中需要分配較大對象時無法找到足夠的連續內存而不得不提前觸發另一次垃圾收集.


老年代-標記整理算法

標記清除算法會產生內存碎片問題, 而復制算法需要有額外的內存擔保空間, 于是針對老年代的特點, 又有了標記整理算法. 標記整理算法的標記過程與標記清除算法相同, 但后續步驟不再對可回收對象直接清理, 而是讓所有存活的對象都向一端移動,然后清理掉端邊界以外的內存.


永久代-方法區回收

  • 在方法區進行垃圾回收一般”性價比”較低, 因為在方法區主要回收兩部分內容:?廢棄常量無用的類. 回收廢棄常量與回收其他年代中的對象類似, 但要判斷一個類是否無用則條件相當苛刻:
    1. 該類所有的實例都已經被回收, Java堆中不存在該類的任何實例;
    2. 該類對應的Class對象沒有在任何地方被引用(也就是在任何地方都無法通過反射訪問該類的方法);
    3. 加載該類的ClassLoader已經被回收.

但即使滿足以上條件也未必一定會回收, Hotspot VM還提供了-Xnoclassgc參數控制(關閉CLASS的垃圾回收功能). 因此在大量使用動態代理、CGLib等字節碼框架的應用中一定要關閉該選項, 開啟VM的類卸載功能, 以保證方法區不會溢出.


補充: 空間分配擔保

在執行Minor GC前, VM會首先檢查老年代是否有足夠的空間存放新生代尚存活對象, 由于新生代使用復制收集算法, 為了提升內存利用率, 只使用了其中一個Survivor作為輪換備份, 因此當出現大量對象在Minor GC后仍然存活的情況時, 就需要老年代進行分配擔保, 讓Survivor無法容納的對象直接進入老年代, 但前提是老年代需要有足夠的空間容納這些存活對象. 但存活對象的大小在實際完成GC前是無法明確知道的, 因此Minor GC前, VM會先首先檢查老年代連續空間是否大于新生代對象總大小或歷次晉升的平均大小, 如果條件成立, 則進行Minor GC, 否則進行Full GC(讓老年代騰出更多空間).
然而取歷次晉升的對象的平均大小也是有一定風險的, 如果某次Minor GC存活后的對象突增,遠遠高于平均值的話,依然可能導致擔保失敗(Handle Promotion Failure, 老年代也無法存放這些對象了), 此時就只好在失敗后重新發起一次Full GC(讓老年代騰出更多空間).


IX. GC實現- 垃圾收集器

GC實現目標: 準確、高效、低停頓、空閑內存規整.


新生代

1. Serial收集器

Serial收集器是Hotspot運行在Client模式下的默認新生代收集器, 它的特點是?只用一個CPU/一條收集線程去完成GC工作, 且在進行垃圾收集時必須暫停其他所有的工作線程(“Stop The World” -后面簡稱STW).

雖然是單線程收集, 但它卻簡單而高效, 在VM管理內存不大的情況下(收集幾十M~一兩百M的新生代), 停頓時間完全可以控制在幾十毫秒~一百多毫秒內.


2. ParNew收集器

ParNew收集器其實是前面Serial的多線程版本, 除使用多條線程進行GC外, 包括Serial可用的所有控制參數、收集算法、STW、對象分配規則、回收策略等都與Serial完全一樣(也是VM啟用CMS收集器-XX: +UseConcMarkSweepGC的默認新生代收集器).

由于存在線程切換的開銷, ParNew在單CPU的環境中比不上Serial, 且在通過超線程技術實現的兩個CPU的環境中也不能100%保證能超越Serial. 但隨著可用的CPU數量的增加, 收集效率肯定也會大大增加(ParNew收集線程數與CPU的數量相同, 因此在CPU數量過大的環境中, 可用-XX:ParallelGCThreads參數控制GC線程數).


3. Parallel Scavenge收集器

與ParNew類似, Parallel Scavenge也是使用復制算法, 也是并行多線程收集器. 但與其他收集器關注盡可能縮短垃圾收集時間不同, Parallel Scavenge更關注系統吞吐量:

系統吞吐量=運行用戶代碼時間(運行用戶代碼時間+垃圾收集時間)

停頓時間越短就越適用于用戶交互的程序-良好的響應速度能提升用戶的體驗;而高吞吐量則適用于后臺運算而不需要太多交互的任務-可以最高效率地利用CPU時間,盡快地完成程序的運算任務. Parallel Scavenge提供了如下參數設置系統吞吐量:

Parallel Scavenge參數描述
MaxGCPauseMillis(毫秒數) 收集器將盡力保證內存回收花費的時間不超過設定值, 但如果太小將會導致GC的頻率增加.
GCTimeRatio(整數:0 < GCTimeRatio < 100) 是垃圾收集時間占總時間的比率
-XX:+UseAdaptiveSizePolicy啟用GC自適應的調節策略: 不再需要手工指定-Xmn-XX:SurvivorRatio-XX:PretenureSizeThreshold等細節參數, VM會根據當前系統的運行情況收集性能監控信息, 動態調整這些參數以提供最合適的停頓時間或最大的吞吐量

老年代

Serial Old收集器

Serial Old是Serial收集器的老年代版本, 同樣是單線程收集器,使用“標記-整理”算法:

  • Serial Old應用場景如下:
    • JDK 1.5之前與Parallel Scavenge收集器搭配使用;
    • 作為CMS收集器的后備預案, 在并發收集發生Concurrent Mode Failure時啟用(見下:CMS收集器).

Parallel Old收集器

Parallel Old是Parallel Scavenge收老年代版本, 使用多線程和“標記-整理”算法, 吞吐量優先, 主要與Parallel Scavenge配合在?注重吞吐量?及?CPU資源敏感?系統內使用:


CMS收集器

CMS(Concurrent Mark Sweep)收集器是一款具有劃時代意義的收集器, 一款真正意義上的并發收集器, 雖然現在已經有了理論意義上表現更好的G1收集器, 但現在主流互聯網企業線上選用的仍是CMS(如Taobao、微店).
CMS是一種以獲取最短回收停頓時間為目標的收集器(CMS又稱多并發低暫停的收集器), 基于”標記-清除”算法實現, 整個GC過程分為以下4個步驟:
1.?初始標記(CMS initial mark)
2. 并發標記(CMS concurrent mark: GC Roots Tracing過程)
3.?重新標記(CMS remark)
4. 并發清除(CMS concurrent sweep: 已死象將會就地釋放, 注意:?此處沒有壓縮)
其中兩個加粗的步驟(初始標記重新標記)仍需STW. 但初始標記僅只標記一下GC Roots能直接關聯到的對象, 速度很快; 而重新標記則是為了修正并發標記期間因用戶程序繼續運行而導致標記產生變動的那一部分對象的標記記錄, 雖然一般比初始標記階段稍長, 但要遠小于并發標記時間.

(由于整個GC過程耗時最長的并發標記和并發清除階段的GC線程可與用戶線程一起工作, 所以總體上CMS的GC過程是與用戶線程一起并發地執行的.

由于CMS收集器將整個GC過程進行了更細粒度的劃分, 因此可以實現并發收集、低停頓的優勢, 但它也并非十分完美, 其存在缺點及解決策略如下:

  1. CMS默認啟動的回收線程數=(CPU數目+3)4

    當CPU數>4時, GC線程最多占用不超過25%的CPU資源, 但是當CPU數<=4時, GC線程可能就會過多的占用用戶CPU資源, 從而導致應用程序變慢, 總吞吐量降低.

  2. 無法處理浮動垃圾, 可能出現Promotion FailureConcurrent Mode Failure而導致另一次Full GC的產生: 浮動垃圾是指在CMS并發清理階段用戶線程運行而產生的新垃圾. 由于在GC階段用戶線程還需運行, 因此還需要預留足夠的內存空間給用戶線程使用, 導致CMS不能像其他收集器那樣等到老年代幾乎填滿了再進行收集. 因此CMS提供了-XX:CMSInitiatingOccupancyFraction參數來設置GC的觸發百分比(以及-XX:+UseCMSInitiatingOccupancyOnly來啟用該觸發百分比), 當老年代的使用空間超過該比例后CMS就會被觸發(JDK 1.6之后默認92%). 但當CMS運行期間預留的內存無法滿足程序需要, 就會出現上述Promotion Failure等失敗, 這時VM將啟動后備預案: 臨時啟用Serial Old收集器來重新執行Full GC(CMS通常配合大內存使用, 一旦大內存轉入串行的Serial GC, 那停頓的時間就是大家都不愿看到的了).
  3. 最后, 由于CMS采用”標記-清除”算法實現, 可能會產生大量內存碎片. 內存碎片過多可能會導致無法分配大對象而提前觸發Full GC. 因此CMS提供了-XX:+UseCMSCompactAtFullCollection開關參數, 用于在Full GC后再執行一個碎片整理過程. 但內存整理是無法并發的, 內存碎片問題雖然沒有了, 但停頓時間也因此變長了, 因此CMS還提供了另外一個參數-XX:CMSFullGCsBeforeCompaction用于設置在執行N次不進行內存整理的Full GC后, 跟著來一次帶整理的(默認為0: 每次進入Full GC時都進行碎片整理).

分區收集- G1收集器

G1(Garbage-First)是一款面向服務端應用的收集器, 主要目標用于配備多顆CPU的服務器治理大內存.
- G1 is planned as the long term replacement for the Concurrent Mark-Sweep Collector (CMS).
-?-XX:+UseG1GC?啟用G1收集器.

與其他基于分代的收集器不同, G1將整個Java堆劃分為多個大小相等的獨立區域(Region), 雖然還保留有新生代和老年代的概念, 但新生代和老年代不再是物理隔離的了, 它們都是一部分Region(不需要連續)的集合.

每塊區域既有可能屬于O區、也有可能是Y區, 因此不需要一次就對整個老年代/新生代回收. 而是當線程并發尋找可回收的對象時, 有些區塊包含可回收的對象要比其他區塊多很多. 雖然在清理這些區塊時G1仍然需要暫停應用線程, 但可以用相對較少的時間優先回收垃圾較多的Region(這也是G1命名的來源). 這種方式保證了G1可以在有限的時間內獲取盡可能高的收集效率.


新生代收集

G1的新生代收集跟ParNew類似: 存活的對象被轉移到一個/多個Survivor Regions. 如果存活時間達到閥值, 這部分對象就會被提升到老年代.

  • G1的新生代收集特點如下:
    • 一整塊堆內存被分為多個Regions.
    • 存活對象被拷貝到新的Survivor區或老年代.
    • 年輕代內存由一組不連續的heap區組成, 這種方法使得可以動態調整各代區域尺寸.
    • Young GCs會有STW事件, 進行時所有應用程序線程都會被暫停.
    • 多線程并發GC.

老年代收集

G1老年代GC會執行以下階段:

注: 一下有些階段也是年輕代垃圾收集的一部分.

indexPhaseDescription
(1)初始標記 (Initial Mark: Stop the World Event)在G1中, 該操作附著一次年輕代GC, 以標記Survivor中有可能引用到老年代對象的Regions.
(2)掃描根區域 (Root Region Scanning: 與應用程序并發執行)掃描Survivor中能夠引用到老年代的references. 但必須在Minor GC觸發前執行完.
(3)并發標記 (Concurrent Marking : 與應用程序并發執行)在整個堆中查找存活對象, 但該階段可能會被Minor GC中斷.
(4)重新標記 (Remark : Stop the World Event)完成堆內存中存活對象的標記. 使用snapshot-at-the-beginning(SATB, 起始快照)算法, 比CMS所用算法要快得多(空Region直接被移除并回收, 并計算所有區域的活躍度).
(5)清理 (Cleanup : Stop the World Event and Concurrent)見下 5-1、2、3
?5-1 (Stop the world)在含有存活對象和完全空閑的區域上進行統計
?5-2 (Stop the world)擦除Remembered Sets.
?5-3 (Concurrent)重置空regions并將他們返還給空閑列表(free list)
(*)Copying/Cleanup (Stop the World Event)選擇”活躍度”最低的區域(這些區域可以最快的完成回收). 拷貝/轉移存活的對象到新的尚未使用的regions. 該階段會被記錄在gc-log內(只發生年輕代[GC pause (young)], 與老年代一起執行則被記錄為[GC Pause (mixed)].

詳細步驟可參考?Oracle官方文檔-The G1 Garbage Collector Step by Step.

  • G1老年代GC特點如下:
    • 并發標記階段(index 3)
      1. 在與應用程序并發執行的過程中會計算活躍度信息.
      2. 這些活躍度信息標識出那些regions最適合在STW期間回收(which regions will be best to reclaim during an evacuation pause).
      3. 不像CMS有清理階段.
    • 再次標記階段(index 4)
      1. 使用Snapshot-at-the-Beginning(SATB)算法比CMS快得多.
      2. 空region直接被回收.
    • 拷貝/清理階段(Copying/Cleanup Phase)
      • 年輕代與老年代同時回收.
      • 老年代內存回收會基于他的活躍度信息.

補充: 關于Remembered Set

G1收集器中, Region之間的對象引用以及其他收集器中的新生代和老年代之間的對象引用都是使用Remembered Set來避免掃描全堆. G1中每個Region都有一個與之對應的Remembered Set, VM發現程序對Reference類型數據進行寫操作時, 會產生一個Write Barrier暫時中斷寫操作, 檢查Reference引用的對象是否處于不同的Region中(在分代例子中就是檢查是否老年代中的對象引用了新生代的對象), 如果是, 便通過CardTable把相關引用信息記錄到被引用對象所屬的Region的Remembered Set中. 當內存回收時, 在GC根節點的枚舉范圍加入Remembered Set即可保證不對全局堆掃描也不會有遺漏.


V. JVM小工具

在${JAVA_HOME}/bin/目錄下Sun/Oracle給我們提供了一些處理應用程序性能問題、定位故障的工具, 包含

bin描述功能
jps打印Hotspot VM進程VMID、JVM參數、main()函數參數、主類名/Jar路徑
jstat查看Hotspot VM?運行時信息類加載、內存、GC[可分代查看]、JIT編譯
jinfo查看和修改虛擬機各項配置-flag name=value
jmapheapdump: 生成VM堆轉儲快照、查詢finalize執行隊列、Java堆和永久代詳細信息jmap -dump:live,format=b,file=heap.bin [VMID]
jstack查看VM當前時刻的線程快照: 當前VM內每一條線程正在執行的方法堆棧集合Thread.getAllStackTraces()提供了類似的功能
javap查看經javac之后產生的JVM字節碼代碼自動解析.class文件, 避免了去理解class文件格式以及手動解析class文件內容
jcmd一個多功能工具, 可以用來導出堆, 查看Java進程、導出線程信息、 執行GC、查看性能相關數據等幾乎集合了jps、jstat、jinfo、jmap、jstack所有功能
jconsole基于JMX的可視化監視、管理工具可以查看內存、線程、類、CPU信息, 以及對JMX MBean進行管理
jvisualvmJDK中最強大運行監視和故障處理工具可以監控內存泄露、跟蹤垃圾回收、執行時內存分析、CPU分析、線程分析…

VI. VM常用參數整理

參數描述
-Xms最小堆大小
-Xmx最大堆大小
-Xmn新生代大小
-XX:PermSize永久代大小
-XX:MaxPermSize永久代最大大小
-XX:+PrintGC輸出GC日志
-verbose:gc-
-XX:+PrintGCDetails輸出GC的詳細日志
-XX:+PrintGCTimeStamps輸出GC時間戳(以基準時間的形式)
-XX:+PrintHeapAtGC在進行GC的前后打印出堆的信息
-Xloggc:/path/gc.log日志文件的輸出路徑
-XX:+PrintGCApplicationStoppedTime打印由GC產生的停頓時間

在此處無法列舉所有的參數以及他們的應用場景, 詳細移步Oracle官方文檔-Java HotSpot VM?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/387040.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/387040.shtml
英文地址,請注明出處:http://en.pswp.cn/news/387040.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

02 CSS

使用 table 進行布局存在缺陷&#xff0c;而一般的布局都會采用 DIVCSS 來進行布局。 Div 它是一個html 標簽&#xff0c;一個塊級元素(單獨顯示一行)。它單獨使用沒有任何意義&#xff0c;必須結合 CSS 來使用。它主要用于頁面的布局。 Span 它是一個 html 標簽&#xff0c;…

為什么要在密碼里加點“鹽”

鹽&#xff08;Salt&#xff09; 在密碼學中&#xff0c;是指通過在密碼任意固定位置插入特定的字符串&#xff0c;讓散列后的結果和使用原始密碼的散列結果不相符&#xff0c;這種過程稱之為“加鹽”。 以上這句話是維基百科上對于 Salt 的定義&#xff0c;但是僅憑這句話還是…

一致性哈希算法 應用

互聯網創業中大部分人都是草根創業&#xff0c;這個時候沒有強勁的服務器&#xff0c;也沒有錢去買很昂貴的海量數據庫。在這樣嚴峻的條件下&#xff0c;一批又一批的創業者從創業中獲得成 功&#xff0c;這個和當前的開源技術、海量數據架構有著必不可分的關系。比如我們使用m…

(9)How to take a picture of a black hole

https://www.ted.com/talks/katie_bouman_what_does_a_black_hole_look_like/transcript 00:13In the movie "Interstellar[??nt?r?stel?(r)]星際的," we get an up-close look at a supermassive black hole. Set against a backdrop of bright gas, the black…

單個節點的緩存容量達到上限 Hash算法一致性

場景 單個節點的緩存容量達到上限&#xff0c;無法繼續單點增加內存&#xff0c;如何解決&#xff1f; 單個節點支撐的QPS達到上限&#xff0c;如何解決&#xff1f; 初步方案 增加N個緩存節點&#xff0c;為了保證緩存數據的均勻&#xff0c;一般情況會采用對key值hash&…

java學習筆記11 (構造方法 this深探)

在開發中&#xff0c;經常需要在創建對象的同事明確對象對的屬性值&#xff0c;比如一個person對象創建的時候就應該有name和age 等屬性&#xff0c;那么如何做到在創建對象的同時給對象的屬性值初始化值呢&#xff1f; 這里介紹構造方法 1 構造方法沒有返回值類型&#xff0c;…

密碼中不能包含全角字符的正則表達式

String regex "^((?![^\\x00-\\xff]).)*$"; String str "aA"; System.out.println(str.matches(regex));

編程算法 - 將排序數組按絕對值大小排序 代碼(java)

一個含有多個元素的數組&#xff0c;有多種排序方式。它可以升序排列&#xff0c;可以降序排列&#xff0c;也可以像我們以前章節說過的&#xff0c;以波浪形方式排序&#xff0c;現在我們要看到的一種是絕對值排序。對于數組A,絕對值排序滿足以下條件&#xff1a;|A[i]| < …

QT Linux打包發布

Linux&#xff1a; 1、用Release編譯&#xff1b; 2、把可執行文件(如paike)放入新建目錄中; 3、當前目錄下編寫腳本copyDependency.sh&#xff0c;把動態鏈接庫導入當前目錄&#xff1b; #!/bin/shexe"paike" #發布的程序名稱destination"/home/paike"…

CRM公海自動回收規則

企微云CRM操作指南 – 道一云|企微https://wbg.do1.com.cn/xueyuan/2568.html 銷售云 - 美洽 - 連接客戶&#xff0c;親密無間https://meiqia.com/sales-cloud 轉載于:https://www.cnblogs.com/rgqancy/p/10695466.html

三分鐘看懂一致性哈希算法

一致性哈希算法&#xff0c;作為分布式計算的數據分配參考&#xff0c;比傳統的取模&#xff0c;劃段都好很多。 在電信計費中&#xff0c;可以作為多臺消息接口機和在線計費主機的分配算法&#xff0c;根據session_id來分配&#xff0c;這樣當計費主機動態伸縮的時候&#xf…

數據結構09圖

第七章 圖 Graph 7.1 圖的定義和術語 頂點 Vertex V 是頂點的有窮非空集合&#xff0c;頂點數 |V| n VR 兩個頂點之間關系的集合&#xff0c;邊數 |VR| e 有向圖 Digraph <v, w> Arc v Tail / Inital node w Head / Terminal node 無向圖 Undigraph <v, w> 必…

JVM調優-GC參數

一、Throughput收集器(吞吐量) -XX:UseParallelGC -XX:UseParallelOldGC *參數調整&#xff1a;通過調整堆大小&#xff0c;減少GC停頓時間&#xff0c;增大吞吐量 增強堆大小可以減少Full GC頻率&#xff0c;但卻會增加停頓時間 1.手動調整 -Xmn -Xms -XX:NewRatioN 手動指…

aspnetcore源碼學習(一)

---恢復內容開始--- 筆者從事netcore相關項目開發已經大半年了&#xff0c;從netcore 1.0到現在3.0大概經過了3年左右的時間&#xff0c;記得netcore剛出來的時候國內相關的學習資料缺乏&#xff0c;限制于外語不大熟練的限制國外的相關書籍看起來相當吃力&#xff0c;于是在當…

評估一個垃圾收集(GC)

在實踐中我們發現對于大多數的應用領域&#xff0c;評估一個垃圾收集(GC)算法如何根據如下兩個標準&#xff1a; 吞吐量越高算法越好暫停時間越短算法越好 首先讓我們來明確垃圾收集(GC)中的兩個術語:吞吐量(throughput)和暫停時間(pause times)。 JVM在專門的線程(GC threads…

python數據分析常用包之Scipy

Scipy轉載于:https://www.cnblogs.com/jacky912/p/10697853.html

docker容器狀態跟蹤及疑惑

一、 1 def status_test():2 container client.containers.create("comp")3 print ("create: ", container.status)4 container.start()5 print ("start: ", container.status)6 container.pause()7 print ("paus…

CAP和BASE理論

幾個名詞解釋&#xff1a; 網絡分區&#xff1a;俗稱“腦裂”。當網絡發生異常情況&#xff0c;導致分布式系統中部分節點之間的網絡延時不斷變大&#xff0c;最終導致組成分布式系統的所有節點中&#xff0c;只有部分節點之間能夠進行正常通信&#xff0c;而另一些節點則不能…

Mysql案例5:取得平均薪資最高的部門的部門名稱

一、要求&#xff1a;查詢平均薪水最高部門的部門編號 二、背景&#xff1a;當前數據庫有employee表和department表&#xff0c;數據分別如下&#xff1a; employee表&#xff1a; department表&#xff1a; 三、難點&#xff1a; 1、需要考慮最高平均薪資可能在多個部門同時出…

Spring 處理過程分析

一、處理過程分析 1、首先&#xff0c;Tomcat每次啟動時都會加載并解析/WEB-INF/web.xml文件&#xff0c;所以可以先從web.xml找突破口&#xff0c;主要代碼如下&#xff1a;<servlet ><servlet-name >spring-mvc</servlet-name><!-- servlet類 --><…