自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

中廠Java后端15連問!

開發(fā) 后端
G1(Garbage-First)收集器是 Java 虛擬機(jī)中的一種垃圾收集器。它于 JDK 7 中首次引入,并在后續(xù)版本中不斷改進(jìn)優(yōu)化。

前言

大家好,我是田螺。最近一位星球粉絲去面試一個(gè)中廠,Java后端。他說,好幾道題答不上來,于是我?guī)兔φ砹艘徊ù鸢?/p>

  1. G1收集器
  2. JVM內(nèi)存劃分
  3. 對象進(jìn)入老年代標(biāo)志
  4. 你在項(xiàng)目中用到的是哪種收集器,怎么調(diào)優(yōu)的
  5. new對象的內(nèi)存分布
  6. 局部變量的內(nèi)存分布
  7. Synchronized和Lock的區(qū)別
  8. Synchronized原理
  9. 可重入是如何知道當(dāng)前鎖的擁有著的
  10. Spring用到的設(shè)計(jì)模式
  11. 說說SPI
  12. 排行榜怎么設(shè)計(jì)
  13. SpringBoot 中注解實(shí)現(xiàn)緩存用過沒?實(shí)現(xiàn)原理是什么。
  14. 深分頁優(yōu)化
  15. Redis分布式鎖如何進(jìn)一步提升性能

1. 說說G1收集器

G1(Garbage-First)收集器是 Java 虛擬機(jī)中的一種垃圾收集器。它于 JDK 7 中首次引入,并在后續(xù)版本中不斷改進(jìn)優(yōu)化。

  • 可以使用 -XX:+UseG1GC 開啟它。這個(gè)選項(xiàng)告訴 JVM 在運(yùn)行時(shí)使用 G1 垃圾收集器來管理堆內(nèi)存。
  • G1(Garbage-First)收集器的核心原則之一就是“首先收集盡可能多的垃圾”,即優(yōu)先回收那些包含最多垃圾的區(qū)域。這個(gè)原則是與CMS收集器有所不同的,CMS主要關(guān)注于盡可能減少應(yīng)用程序的停頓時(shí)間。
  • G1 收集器會將 Java 堆劃分為多個(gè)大小相等的區(qū)域(Region),然后根據(jù)當(dāng)前堆內(nèi)存的使用情況,選擇性地進(jìn)行垃圾回收。
  • 當(dāng) G1 收集器決定進(jìn)行Full GC時(shí),它會執(zhí)行一次 Full GC(Full GC),這時(shí)整個(gè)應(yīng)用程序?qū)和#⊿TW)。但在Mixed GC中,G1 收集器會在某些情況下選擇部分年輕代分區(qū)和部分老年代分區(qū)進(jìn)行回收,這樣一來,即使不是整個(gè)堆內(nèi)存的垃圾回收,仍然可能會導(dǎo)致一些暫停,但相對于 Full GC,這種暫停時(shí)間會更短。
  • 適用于大內(nèi)存、對停頓時(shí)間敏感、需要高吞吐量的應(yīng)用場景。

2. JVM內(nèi)存劃分

Java 虛擬機(jī)(JVM)的內(nèi)存分為多個(gè)區(qū)域,每個(gè)區(qū)域都有不同的作用和管理方式。JVM 內(nèi)存劃分的主要區(qū)域:

圖片圖片

  • 堆(Heap):堆是 JVM 中最大的一塊內(nèi)存區(qū)域,用于存儲對象實(shí)例和數(shù)組。堆內(nèi)存由所有線程共享,是垃圾收集器主要管理的區(qū)域。
  • 程序計(jì)數(shù)器(Program Counter):程序計(jì)數(shù)器是一塊較小的內(nèi)存空間,它是線程私有的,用于存儲當(dāng)前線程正在執(zhí)行的字節(jié)碼指令的地址。在多線程環(huán)境下,每個(gè)線程都有一個(gè)獨(dú)立的程序計(jì)數(shù)器,以保證線程切換后能夠恢復(fù)到正確的執(zhí)行位置。
  • 虛擬機(jī)棧(Java Virtual Machine Stacks):虛擬機(jī)棧也是線程私有的,用于存儲方法執(zhí)行過程中的局部變量、方法參數(shù)、中間結(jié)果等數(shù)據(jù)。每個(gè)方法在執(zhí)行時(shí)都會創(chuàng)建一個(gè)棧幀,棧幀包含了方法的局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法返回地址等信息。
  • 本地方法棧(Native Method Stacks):本地方法棧與虛擬機(jī)棧類似,用于執(zhí)行本地方法(Native Method)時(shí)的數(shù)據(jù)存儲。與虛擬機(jī)棧一樣,本地方法棧也是線程私有的。
  • 方法區(qū)(Method Area):方法區(qū)用于存儲類信息、常量、靜態(tài)變量和即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。在jdk7及以前,習(xí)慣上把方法區(qū),稱為永久代。jdk8開始,使用元空間取代了永久代。本質(zhì)上,方法區(qū)和永久代并不等價(jià)。通過-XX:Permsize來設(shè)置永久代初始分配空間??梢允褂脜?shù) -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize指定默認(rèn)值依賴于平臺。
  • 運(yùn)行時(shí)常量池(Runtime Constant Pool):運(yùn)行時(shí)常量池是方法區(qū)的一部分,用于存儲編譯時(shí)生成的字面量常量和符號引用。與類文件中的常量池(Constant Pool)相對應(yīng),運(yùn)行時(shí)常量池具有動(dòng)態(tài)性,可以在運(yùn)行時(shí)動(dòng)態(tài)地添加、修改常量。
  • 直接內(nèi)存(Direct Memory):直接內(nèi)存不是 JVM 內(nèi)存的一部分,但是在一些情況下會被 JVM 使用。直接內(nèi)存是通過使用 java.nio 包中的類來申請和釋放的,它允許 JVM 在堆外分配內(nèi)存,可以提高 I/O 操作的效率。

3. 對象進(jìn)入老年代標(biāo)志

圖片圖片

  • 對象年齡達(dá)到閾值:對象在 Java 堆中的存活時(shí)間通常會通過對象年齡(Age)來衡量。在某些垃圾收集器中,當(dāng)對象經(jīng)過多次垃圾收集后仍然存活,并且達(dá)到了一定的年齡閾值,就會被晉升到老年代。年齡閾值可以通過 JVM 參數(shù)進(jìn)行調(diào)整。
  • 大對象:大對象通常指的是占用大量內(nèi)存空間的對象,例如數(shù)組或者很大的字符串。在一些垃圾收集器中,為了避免在新生代中頻繁復(fù)制大對象,會直接將大對象分配在老年代中。
  • 長期存活的對象:一些長期存活的對象,例如長期存在的線程、靜態(tài)變量等,有可能直接被分配到老年代中。
  • 晉升失?。寒?dāng)新生代中的對象經(jīng)過多次垃圾回收仍然存活,并且新生代內(nèi)存不足以容納這些存活對象時(shí),會發(fā)生一次晉升失敗,這時(shí)一部分存活對象可能會被直接晉升到老年代。

通常情況下,老年代用于存放長期存活的對象,以減少垃圾收集的頻率和提高垃圾回收的效率。

4. 你在項(xiàng)目中用到的是哪種收集器,怎么調(diào)優(yōu)的

我列舉了常見的垃圾收集器以及調(diào)優(yōu)策略:

串行收集器(Serial Garbage Collector):

  • 適用于單核 CPU 或小型應(yīng)用場景。
  • 調(diào)優(yōu)參數(shù):-XX:+UseSerialGC

并行收集器(Parallel Garbage Collector):

  • 適用于多核 CPU,通過并行收集來提高垃圾回收效率。
  • 調(diào)優(yōu)參數(shù):-XX:+UseParallelGC

并發(fā)標(biāo)記-清除收集器(Concurrent Mark-Sweep Garbage Collector,CMS GC):

  • 適用于對系統(tǒng)停頓時(shí)間敏感的應(yīng)用場景。
  • 調(diào)優(yōu)參數(shù):-XX:+UseConcMarkSweepGC

G1 收集器(Garbage-First Garbage Collector):

  • 適用于大內(nèi)存、對停頓時(shí)間敏感、需要高吞吐量的應(yīng)用場景。
  • 調(diào)優(yōu)參數(shù):-XX:+UseG1GC

ZGC 和 Shenandoah(JDK 11+):

  • 適用于超大內(nèi)存、對停頓時(shí)間極為敏感的應(yīng)用場景。
  • 調(diào)優(yōu)參數(shù):-XX:+UseZGC(ZGC)、-XX:+UseShenandoahGC(Shenandoah)

當(dāng)然,調(diào)優(yōu)的具體策略會根據(jù)應(yīng)用的特點(diǎn)和需求來定,可以通過以下一些常見的調(diào)優(yōu)手段來改進(jìn)垃圾收集器的性能:

  • 調(diào)整堆大小(-Xms、-Xmx)以及新生代與老年代的比例(-XX:NewRatio、-XX:SurvivorRatio)。
  • 設(shè)置垃圾收集器的參數(shù),如并行收集器的線程數(shù)(-XX:ParallelGCThreads)、并發(fā)標(biāo)記-清除收集器的初始標(biāo)記階段并發(fā)線程數(shù)(-XX:ConcGCThreads)等。
  • 設(shè)置垃圾收集器的觸發(fā)條件,如新生代垃圾收集的觸發(fā)條件(-XX:MaxNewSize、-XX:NewThreshold)等。
  • 監(jiān)控和分析 GC 日志,根據(jù)應(yīng)用的實(shí)際情況進(jìn)行優(yōu)化。選擇合適的 GC 日志分析工具,如GCViewer、GCEasy等,進(jìn)行進(jìn)一步的性能分析和優(yōu)化。

5. new對象的內(nèi)存分布

在 Java 中,當(dāng)使用 new 關(guān)鍵字創(chuàng)建一個(gè)對象時(shí),對象在內(nèi)存中的基本結(jié)構(gòu)通常由對象頭、實(shí)例數(shù)據(jù)和填充組成。

  • 對象頭(Object Header):對象頭存儲了對象的元數(shù)據(jù)信息,如哈希碼、對象鎖狀態(tài)、GC 相關(guān)信息等。對象頭的結(jié)構(gòu)和大小在不同的 JVM 實(shí)現(xiàn)中可能會有所不同,但通常包括一些固定的字段。
  • 實(shí)例數(shù)據(jù)(Instance Data):實(shí)例數(shù)據(jù)包含了對象的實(shí)際數(shù)據(jù),即對象中的成員變量的值。這些數(shù)據(jù)根據(jù)對象的類定義來確定,包括各種類型的字段和對象引用等。
  • 填充(Padding):填充是為了滿足內(nèi)存對齊的需要而添加的額外字節(jié)。內(nèi)存對齊可以提高內(nèi)存訪問的效率,一些 JVM 可能會在對象的實(shí)例數(shù)據(jù)后添加填充字節(jié),使得對象的起始地址能夠?qū)R到某個(gè)特定的邊界。

6. 局部變量的內(nèi)存分布

棧幀結(jié)構(gòu):

  • 棧幀由三部分組成:局部變量表(Local Variable Table)、操作數(shù)棧(Operand Stack)和幀數(shù)據(jù)(Frame Data)。
  • 局部變量表用于存儲方法參數(shù)和方法內(nèi)部定義的局部變量。
  • 操作數(shù)棧用于存儲方法執(zhí)行過程中的操作數(shù)。
  • 幀數(shù)據(jù)包含了方法的異常處理信息、方法返回地址等。

局部變量表:

  • 局部變量表是一個(gè)數(shù)組,用于存儲方法參數(shù)和方法內(nèi)部定義的局部變量。
  • 局部變量表中的每個(gè)元素都可以存儲一個(gè)數(shù)據(jù)值,數(shù)據(jù)類型可以是基本數(shù)據(jù)類型或者引用類型。
  • 局部變量表中的變量只在方法執(zhí)行期間有效,當(dāng)方法執(zhí)行結(jié)束后,局部變量表所占用的內(nèi)存空間會被釋放。

局部變量的存儲位置:

  • 對于基本數(shù)據(jù)類型的局部變量,它們的值直接存儲在局部變量表中。
  • 對于引用類型的局部變量,局部變量表中存儲的是對象的引用,而對象的實(shí)際數(shù)據(jù)存儲在堆內(nèi)存中。

局部變量的生命周期:

  • 局部變量的生命周期與方法的執(zhí)行周期相同,當(dāng)方法執(zhí)行結(jié)束后,局部變量表所占用的內(nèi)存空間會被釋放。
  • 局部變量的生命周期也可能會被延長,比如局部變量被捕獲到一個(gè)匿名內(nèi)部類中,那么該局部變量的生命周期會與匿名內(nèi)部類的生命周期保持一致。

7.Synchronized和ReenTrantLock的區(qū)別

  • Synchronized是依賴于JVM實(shí)現(xiàn)的,而ReenTrantLock是API實(shí)現(xiàn)的。
  • 在Synchronized優(yōu)化以前,synchronized的性能是比ReenTrantLock差很多的,但是自從Synchronized引入了偏向鎖,輕量級鎖(自旋鎖)后,兩者性能就差不多了。
  • Synchronized的使用比較方便簡潔,它由編譯器去保證鎖的加鎖和釋放。而ReenTrantLock需要手工聲明來加鎖和釋放鎖,最好在finally中聲明釋放鎖。
  • ReentrantLock可以指定是公平鎖還是?公平鎖。?synchronized只能是?公平鎖。
  • ReentrantLock可響應(yīng)中斷、可輪回,而Synchronized是不可以響應(yīng)中斷的

8.Synchronized原理

synchronized是Java中的關(guān)鍵字,是一種同步鎖。synchronized關(guān)鍵字可以作用于方法或者代碼塊。

一般面試時(shí)??梢赃@么回答:

  • 反編譯后,monitorenter、monitorexit、ACC_SYNCHRONIZED
  • monitor監(jiān)視器
  • Java Monitor 的工作機(jī)理
  • 對象與monitor關(guān)聯(lián)

8.1 monitorenter、monitorexit、ACC_SYNCHRONIZED

  • 如果synchronized作用于代碼塊,反編譯可以看到兩個(gè)指令:monitorenter、monitorexit,JVM使用monitorenter和monitorexit兩個(gè)指令實(shí)現(xiàn)同步;
  • 如果作用synchronized作用于方法,反編譯可以看到ACCSYNCHRONIZED標(biāo)記,JVM通過在方法訪問標(biāo)識符(flags)中加入ACCSYNCHRONIZED來實(shí)現(xiàn)同步功能。
  • 同步代碼塊是通過monitorenter和monitorexit來實(shí)現(xiàn),當(dāng)線程執(zhí)行到monitorenter的時(shí)候要先獲得monitor鎖,才能執(zhí)行后面的方法。當(dāng)線程執(zhí)行到monitorexit的時(shí)候則要釋放鎖。
  • 同步方法是通過中設(shè)置ACCSYNCHRONIZED標(biāo)志來實(shí)現(xiàn),當(dāng)線程執(zhí)行有ACCSYNCHRONIZED標(biāo)志的方法,需要獲得monitor鎖。每個(gè)對象都與一個(gè)monitor相關(guān)聯(lián),線程可以占有或者釋放monitor。

8.2 monitor監(jiān)視器

monitor是什么呢?操作系統(tǒng)的管程(monitors)是概念原理,ObjectMonitor是它的原理實(shí)現(xiàn)。

在Java虛擬機(jī)(HotSpot)中,Monitor(管程)是由ObjectMonitor實(shí)現(xiàn)的,其主要數(shù)據(jù)結(jié)構(gòu)如下:

ObjectMonitor() {
    _header       = NULL;
    _count        = 0; // 記錄個(gè)數(shù)
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;
    _WaitSet      = NULL;  // 處于wait狀態(tài)的線程,會被加入到_WaitSet
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ;  // 處于等待鎖block狀態(tài)的線程,會被加入到該列表
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
  }

ObjectMonitor中幾個(gè)關(guān)鍵字段的含義如圖所示:

圖片圖片

8.3 Java Monitor 的工作機(jī)理

圖片圖片

  • 想要獲取monitor的線程,首先會進(jìn)入_EntryList隊(duì)列。
  • 當(dāng)某個(gè)線程獲取到對象的monitor后,進(jìn)入Owner區(qū)域,設(shè)置為當(dāng)前線程,同時(shí)計(jì)數(shù)器count加1。
  • 如果線程調(diào)用了wait()方法,則會進(jìn)入WaitSet隊(duì)列。它會釋放monitor鎖,即將owner賦值為null,count自減1,進(jìn)入WaitSet隊(duì)列阻塞等待。
  • 如果其他線程調(diào)用 notify() / notifyAll() ,會喚醒WaitSet中的某個(gè)線程,該線程再次嘗試獲取monitor鎖,成功即進(jìn)入Owner區(qū)域。
  • 同步方法執(zhí)行完畢了,線程退出臨界區(qū),會將monitor的owner設(shè)為null,并釋放監(jiān)視鎖。

8.4 對象與monitor關(guān)聯(lián)

圖片圖片

  • 在HotSpot虛擬機(jī)中,對象在內(nèi)存中存儲的布局可以分為3塊區(qū)域:對象頭(Header),實(shí)例數(shù)據(jù)(Instance Data)和對象填充(Padding)。
  • 對象頭主要包括兩部分?jǐn)?shù)據(jù):Mark Word(標(biāo)記字段)、Class Pointer(類型指針)。
  • Mark Word 是用于存儲對象自身的運(yùn)行時(shí)數(shù)據(jù),如哈希碼(HashCode)、GC分代年齡、鎖狀態(tài)標(biāo)志、線程持有的鎖、偏向線程 ID、偏向時(shí)間戳等。
  • 重量級鎖,指向互斥量的指針。其實(shí)synchronized是重量級鎖,也就是說Synchronized的對象鎖,Mark Word鎖標(biāo)識位為10,其中指針指向的是Monitor對象的起始地址。

9.可重入是如何知道當(dāng)前鎖的擁有著的

比如,ReentrantLock 是可重入的鎖。ReentrantLock 類實(shí)現(xiàn)了可重入鎖的概念,允許同一個(gè)線程在持有鎖的情況下多次獲取同一個(gè)鎖,而不會被阻塞。

在 ReentrantLock 中,每個(gè)鎖都關(guān)聯(lián)著一個(gè)持有計(jì)數(shù)器和一個(gè)擁有者線程。當(dāng)一個(gè)線程首次獲取鎖時(shí),持有計(jì)數(shù)器會增加;當(dāng)該線程再次獲取鎖時(shí),持有計(jì)數(shù)器會繼續(xù)增加。每次釋放鎖時(shí),計(jì)數(shù)器會相應(yīng)減少。只有當(dāng)持有計(jì)數(shù)器減為零時(shí),鎖才會完全釋放,其他線程才有機(jī)會獲取鎖。

這樣一來,當(dāng)線程嘗試獲取鎖時(shí),它會檢查當(dāng)前鎖的持有計(jì)數(shù)器以及擁有者線程。如果鎖未被任何線程持有,或者當(dāng)前線程是鎖的擁有者,那么鎖將立即分配給當(dāng)前線程。否則,當(dāng)前線程會被阻塞,直到鎖被釋放。

10.Spring用到的設(shè)計(jì)模式

  • 單例模式(Singleton Pattern):Spring 中的 Bean 默認(rèn)是單例的,即每個(gè) Bean 只有一個(gè)實(shí)例。這種方式可以提高性能并減少資源消耗。
  • 工廠模式(Factory Pattern):Spring 使用工廠模式來創(chuàng)建和管理 Bean。它提供了幾種不同類型的工廠,比如 BeanFactory 和 ApplicationContext,用于創(chuàng)建和管理 Bean 對象。
  • 代理模式(Proxy Pattern):Spring AOP(面向切面編程)功能基于代理模式實(shí)現(xiàn)。它允許通過在運(yùn)行時(shí)為目標(biāo)對象創(chuàng)建代理對象來添加橫切關(guān)注點(diǎn)(如日志記錄、性能監(jiān)控等)。
  • 裝飾者模式(Decorator Pattern):Spring 中的 BeanPostProcessor 接口就是一個(gè)裝飾器模式的例子。它允許在 Bean 初始化過程中動(dòng)態(tài)地添加新的功能。
  • 觀察者模式(Observer Pattern):Spring 的事件機(jī)制就是基于觀察者模式實(shí)現(xiàn)的。它允許 Bean 發(fā)布事件,其他 Bean 可以注冊監(jiān)聽器來響應(yīng)這些事件。
  • 策略模式(Strategy Pattern):Spring 的 IOC(控制反轉(zhuǎn))和 DI(依賴注入)功能基于策略模式實(shí)現(xiàn)。它允許將不同的實(shí)現(xiàn)注入到一個(gè)接口或抽象類中,以便在運(yùn)行時(shí)選擇不同的行為。
  • 模板模式(Template Pattern):Spring 的 JdbcTemplate 和 HibernateTemplate 等模板類就是模板模式的應(yīng)用。它們封裝了一些常見的操作,使開發(fā)者可以通過簡單的方法調(diào)用來執(zhí)行數(shù)據(jù)庫操作。
  • 適配器模式(Adapter Pattern):Spring 的 AOP 功能和 Spring MVC 框架都使用了適配器模式。它們允許將現(xiàn)有的類與新的接口進(jìn)行適配,以便實(shí)現(xiàn)新的功能。
  • 建造者模式(Builder Pattern):Spring 中的 BeanDefinitionBuilder 和 BeanFactoryBuilder 等構(gòu)建器模式的應(yīng)用。它們用于構(gòu)建復(fù)雜的對象,并且允許逐步設(shè)置對象的屬性。

11.聊聊SPI

SPI,其實(shí)就是Service Provider Interface,是Java提供的一種機(jī)制,用于在運(yùn)行時(shí)動(dòng)態(tài)裝載實(shí)現(xiàn)模塊,使得應(yīng)用程序能夠擴(kuò)展、替換特定的服務(wù)或?qū)崿F(xiàn)。

SPI的工作原理主要包括以下幾個(gè)關(guān)鍵點(diǎn):

  • 接口定義:首先,需要定義一個(gè)接口,該接口定義了一組操作或服務(wù)。這個(gè)接口通常由Java核心庫或者第三方庫提供。
  • 服務(wù)提供者:其次,可以有多個(gè)服務(wù)提供者來實(shí)現(xiàn)這個(gè)接口。這些服務(wù)提供者通常是獨(dú)立的模塊或者庫,它們在自己的jar包中提供了實(shí)現(xiàn)。
  • 配置文件:在Java中,SPI通過在META-INF/services目錄下的特定配置文件中指定實(shí)現(xiàn)類的方式來實(shí)現(xiàn)動(dòng)態(tài)裝載。具體來說,SPI機(jī)制要求服務(wù)提供者在這個(gè)目錄下創(chuàng)建一個(gè)以接口全限定名為文件名的文件,文件中列出了具體的實(shí)現(xiàn)類名。
  • 動(dòng)態(tài)加載:當(dāng)應(yīng)用程序需要某個(gè)服務(wù)時(shí),Java運(yùn)行時(shí)會動(dòng)態(tài)加載配置文件中指定的實(shí)現(xiàn)類,并實(shí)例化它們。這種機(jī)制使得應(yīng)用程序能夠在不修改源代碼的情況下靈活地替換或擴(kuò)展特定的服務(wù)實(shí)現(xiàn)。

在實(shí)際應(yīng)用中,我們可以利用SPI來實(shí)現(xiàn)插件化架構(gòu)、模塊化開發(fā)等,從而提高代碼的可維護(hù)性和可擴(kuò)展性。

12.排行榜怎么設(shè)計(jì)

可以考慮:數(shù)據(jù)庫的order by、或者Redis 的zset

大家可以看下我的這篇文章哈:

如何設(shè)計(jì)一個(gè)排行榜

對于游戲排行榜,如果數(shù)據(jù)量達(dá)到億萬級別,需要考慮的確實(shí)是分桶策略,而桶排序則是分桶策略的一種可能實(shí)現(xiàn)方式之一。

桶排序是一種基于這種分桶策略的排序算法,它將數(shù)據(jù)劃分到若干個(gè)有序的桶中,然后分別對每個(gè)桶中的數(shù)據(jù)進(jìn)行排序,最后將所有桶中的數(shù)據(jù)按照順序合并起來,得到排好序的結(jié)果。

在游戲排行榜系統(tǒng)中,可以根據(jù)玩家的分?jǐn)?shù)范圍、等級、地區(qū)等因素來設(shè)計(jì)分桶策略,將玩家數(shù)據(jù)劃分到不同的桶中。這樣可以使得每個(gè)桶內(nèi)的數(shù)據(jù)量相對較小,提高了排序的效率。

13.SpringBoot 中注解實(shí)現(xiàn)緩存用過沒?實(shí)現(xiàn)原理是什么。

常見的緩存注解包括 @Cacheable、@CachePut、@CacheEvict 等。這些注解的實(shí)現(xiàn)原理基于Spring提供的緩存抽象。

是的,Spring Boot 中的緩存注解常用于提升系統(tǒng)性能,減少重復(fù)計(jì)算,常見的緩存注解包括 @Cacheable、@CachePut、@CacheEvict 等。這些注解的實(shí)現(xiàn)原理基于 Spring Framework 提供的緩存抽象。

實(shí)現(xiàn)原理:

  1. 代理機(jī)制:
  • Spring Boot 使用 AOP(面向切面編程)的方式,在運(yùn)行時(shí)動(dòng)態(tài)地為帶有緩存注解的方法生成代理對象。
  • 當(dāng)調(diào)用帶有緩存注解的方法時(shí),實(shí)際上是調(diào)用代理對象的方法。
  1. 緩存管理器:
  • Spring Boot 提供了多種緩存管理器的實(shí)現(xiàn),比如基于 ConcurrentHashMap 的 SimpleCacheManager、基于 Ehcache的緩存管理器。
  • 緩存管理器負(fù)責(zé)真正地操作緩存,將數(shù)據(jù)存儲到緩存中或者從緩存中獲取數(shù)據(jù)。
  1. 緩存注解:
  • @Cacheable:標(biāo)記在方法上,表示方法的返回值將會被緩存,當(dāng)方法被調(diào)用時(shí),首先從緩存中查找數(shù)據(jù),如果緩存中存在數(shù)據(jù),則直接返回,否則執(zhí)行方法并將結(jié)果存儲到緩存中。
  • @CachePut:標(biāo)記在方法上,表示方法的返回值將會被存儲到緩存中,即使緩存中已經(jīng)存在相同 key 的數(shù)據(jù),也會重新存儲。
  • @CacheEvict:標(biāo)記在方法上,表示清除緩存中的數(shù)據(jù),可以根據(jù)條件來清除指定的緩存數(shù)據(jù)。
  1. 緩存 key 的生成:
  • 默認(rèn)情況下,緩存 key 是由方法的參數(shù)組成的,默認(rèn)的 key 生成器是 SimpleKeyGenerator。
  • 可以通過自定義 key 生成器來生成復(fù)雜的緩存 key。
  1. 緩存注解的執(zhí)行流程:
  • 當(dāng)方法被調(diào)用時(shí),首先會根據(jù)方法參數(shù)生成緩存 key。
  • 然后從緩存中根據(jù)緩存 key 查找數(shù)據(jù),如果找到則返回,否則執(zhí)行方法并將結(jié)果存儲到緩存中。
  • 在 @CachePut 和 @CacheEvict 注解中,會根據(jù)條件清除緩存中的數(shù)據(jù)或者將數(shù)據(jù)存儲到緩存中。

14.深分頁優(yōu)化

我們可以通過減少回表次數(shù)來優(yōu)化。一般有標(biāo)簽記錄法和延遲關(guān)聯(lián)法。

14.1 標(biāo)簽記錄法

就是標(biāo)記一下上次查詢到哪一條了,下次再來查的時(shí)候,從該條開始往下掃描。就好像看書一樣,上次看到哪里了,你就折疊一下或者夾個(gè)書簽,下次來看的時(shí)候,直接就翻到啦。

假設(shè)上一次記錄到100000,則SQL可以修改為:

select  id,name,balance FROM account where id > 100000 limit 10;

這樣的話,后面無論翻多少頁,性能都會不錯(cuò)的,因?yàn)槊辛薸d索引。但是這種方式有局限性:需要一種類似連續(xù)自增的字段。

14.2 延遲關(guān)聯(lián)法

延遲關(guān)聯(lián)法,就是把條件轉(zhuǎn)移到主鍵索引樹,然后減少回表。假設(shè)原生SQL是這樣的的,其中id是主鍵,create_time是普通索引

select id,name,balance from account where create_time> '2020-09-19' limit 100000,10;

使用延遲關(guān)聯(lián)法優(yōu)化,如下:

select  acct1.id,acct1.name,acct1.balance FROM account acct1 INNER JOIN 
(SELECT a.id FROM account a WHERE a.create_time > '2020-09-19' limit 100000, 10) 
AS acct2 on acct1.id= acct2.id;

優(yōu)化思路就是,先通過idx_create_time二級索引樹查詢到滿足條件的主鍵ID,再與原表通過主鍵ID內(nèi)連接,這樣后面直接走了主鍵索引了,同時(shí)也減少了回表。

15.分布式鎖如何進(jìn)一步提升性能,答了Redis的實(shí)現(xiàn)思路好像不是面試官想聽的

redis分布式鎖,我覺得可以從一下這幾個(gè)方向來回答:

  • Pipeline 批量操作:使用 Redis 的 Pipeline 功能可以將多個(gè) Redis 命令打包發(fā)送給服務(wù)器,減少網(wǎng)絡(luò)延遲,提高性能。在獲取鎖、釋放鎖等操作時(shí),可以考慮使用 Pipeline 來批量執(zhí)行多個(gè)命令。
  • Lua 腳本:Redis 支持 Lua 腳本,可以將多個(gè)操作封裝在一個(gè)腳本中,在服務(wù)器端原子性地執(zhí)行,減少了網(wǎng)絡(luò)通信的開銷??梢詫@取鎖和釋放鎖的邏輯封裝在 Lua 腳本中,以提高性能。
  • 降級策略:在高并發(fā)情況下,可以考慮引入降級策略,當(dāng)獲取鎖失敗時(shí),可以使用備用方案或者默認(rèn)值來處理,而不是一直等待鎖的釋放。
  • 監(jiān)控和優(yōu)化:通過監(jiān)控 Redis 的性能指標(biāo),如連接數(shù)、命令執(zhí)行時(shí)間等,可以及時(shí)發(fā)現(xiàn)性能瓶頸,并進(jìn)行優(yōu)化??梢酝ㄟ^ Redis 的監(jiān)控工具或者第三方監(jiān)控工具來實(shí)現(xiàn)監(jiān)控。
  • 合理設(shè)置鎖的過期時(shí)間:根據(jù)業(yè)務(wù)場景的特點(diǎn)和需求,合理設(shè)置鎖的過期時(shí)間,避免鎖被長時(shí)間占用而影響系統(tǒng)性能。可以根據(jù)操作的耗時(shí)和鎖的競爭情況來動(dòng)態(tài)調(diào)整鎖的過期時(shí)間。
  • 使用 Redlock 算法:Redlock 是一種基于多個(gè) Redis 節(jié)點(diǎn)的分布式鎖算法,可以提高分布式鎖的可靠性和性能??梢钥紤]使用 Redlock 算法來實(shí)現(xiàn)分布式鎖。
責(zé)任編輯:武曉燕 來源: 撿田螺的小男孩
相關(guān)推薦

2023-01-28 08:24:28

MySQL索引B+樹

2023-04-26 07:40:34

MySQL索引類型存儲

2024-07-04 11:06:47

2022-05-14 21:19:22

ThreadLocaJDKsynchroniz

2022-04-01 12:40:13

MySQL數(shù)據(jù)庫

2022-12-28 08:33:32

字節(jié)國際支付

2023-03-10 08:45:15

SQL優(yōu)化統(tǒng)計(jì)

2021-07-12 07:08:52

TCP協(xié)議面試

2021-11-09 09:30:52

OkHttp面試Android

2020-05-11 15:01:16

JavaJKD 15工具

2020-12-21 07:53:38

財(cái)務(wù)研發(fā)考勤

2020-09-30 18:19:27

RedisJava面試

2023-04-07 08:34:31

2023-02-13 08:18:15

數(shù)據(jù)庫索引,

2022-02-14 08:25:50

Go語言面試

2024-12-04 08:53:44

Docker腳本命令

2011-06-13 09:29:56

英特爾芯片15nm

2024-05-06 00:00:00

2011-09-30 10:36:07

系統(tǒng)管理測試

2021-12-01 11:50:50

HashMap面試Java
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號