JVM常見面試題解析

来源:https://www.cnblogs.com/wfd360/archive/2020/06/11/13090855.html
-Advertisement-
Play Games

前言 總結了JVM一些經典面試題,分享出我自己的解題思路,希望對大家有幫助,有哪裡你覺得不正確的話,歡迎指出,後續有空會更新。 1.什麼情況下會發生棧記憶體溢出。 思路: 描述棧定義,再描述為什麼會溢出,再說明一下相關配置參數,OK的話可以給面試官手寫是一個棧溢出的demo。 我的答案: 棧是線程私有 ...


前言

總結了JVM一些經典面試題,分享出我自己的解題思路,希望對大家有幫助,有哪裡你覺得不正確的話,歡迎指出,後續有空會更新。

1.什麼情況下會發生棧記憶體溢出。

思路: 描述棧定義,再描述為什麼會溢出,再說明一下相關配置參數,OK的話可以給面試官手寫是一個棧溢出的demo。

我的答案:

  • 棧是線程私有的,他的生命周期與線程相同,每個方法在執行的時候都會創建一個棧幀,用來存儲局部變數表,操作數棧,動態鏈接,方法出口等信息。局部變數表又包含基本數據類型,對象引用類型
  • 如果線程請求的棧深度大於虛擬機所允許的最大深度,將拋出StackOverflowError異常,方法遞歸調用產生這種結果。
  • 如果Java虛擬機棧可以動態擴展,並且擴展的動作已經嘗試過,但是無法申請到足夠的記憶體去完成擴展,或者在新建立線程的時候沒有足夠的記憶體去創建對應的虛擬機棧,那麼Java虛擬機將拋出一個OutOfMemory 異常。(線程啟動過多)
  • 參數 -Xss 去調整JVM棧的大小

2.詳解JVM記憶體模型

思路: 給面試官畫一下JVM記憶體模型圖,並描述每個模塊的定義,作用,以及可能會存在的問題,如棧溢出等。

我的答案:

  • JVM記憶體結構

程式計數器:當前線程所執行的位元組碼的行號指示器,用於記錄正在執行的虛擬機位元組指令地址,線程私有。

Java虛擬棧:存放基本數據類型、對象的引用、方法出口等,線程私有。

Native方法棧:和虛擬棧相似,只不過它服務於Native方法,線程私有。

Java堆:java記憶體最大的一塊,所有對象實例、數組都存放在java堆,GC回收的地方,線程共用。

方法區:存放已被載入的類信息、常量、靜態變數、即時編譯器編譯後的代碼數據等。(即永久帶),回收目標主要是常量池的回收和類型的卸載,各線程共用

3.JVM記憶體為什麼要分成新生代,老年代,持久代。新生代中為什麼要分為Eden和Survivor。

思路: 先講一下JAVA堆,新生代的劃分,再談談它們之間的轉化,相互之間一些參數的配置(如: –XX:NewRatio,–XX:SurvivorRatio等),再解釋為什麼要這樣劃分,最好加一點自己的理解。

我的答案:

1)共用記憶體區劃分

  • 共用記憶體區 = 持久帶 + 堆

  • 持久帶 = 方法區 + 其他

  • Java堆 = 老年代 + 新生代

  • 新生代 = Eden + S0 + S1

2)一些參數的配置

  • 預設的,新生代 ( Young ) 與老年代 ( Old ) 的比例的值為 1:2 ,可以通過參數 –XX:NewRatio 配置。
  • 預設的,Edem : from : to = 8 : 1 : 1 ( 可以通過參數 –XX:SurvivorRatio 來設定)
  • Survivor區中的對象被覆制次數為15(對應虛擬機參數 -XX:+MaxTenuringThreshold)

3)為什麼要分為Eden和Survivor?為什麼要設置兩個Survivor區?

  • 如果沒有Survivor,Eden區每進行一次Minor GC,存活的對象就會被送到老年代。老年代很快被填滿,觸發Major GC.老年代的記憶體空間遠大於新生代,進行一次Full GC消耗的時間比Minor GC長得多,所以需要分為Eden和Survivor。
  • Survivor的存在意義,就是減少被送到老年代的對象,進而減少Full GC的發生,Survivor的預篩選保證,只有經歷16次Minor GC還能在新生代中存活的對象,才會被送到老年代。
  • 設置兩個Survivor區最大的好處就是解決了碎片化,剛剛新建的對象在Eden中,經歷一次Minor GC,Eden中的存活對象就會被移動到第一塊survivor space S0,Eden被清空;等Eden區再滿了,就再觸發一次Minor GC,Eden和S0中的存活對象又會被覆制送入第二塊survivor space S1(這個過程非常重要,因為這種複製演算法保證了S1中來自S0和Eden兩部分的存活對象占用連續的記憶體空間,避免了碎片化的發生)

4. JVM中一次完整的GC流程是怎樣的,對象如何晉升到老年代

思路: 先描述一下Java堆記憶體劃分,再解釋Minor GC,Major GC,full GC,描述它們之間轉化流程。

我的答案:

  • Java堆 = 老年代 + 新生代
  • 新生代 = Eden + S0 + S1
  • 當 Eden 區的空間滿了, Java虛擬機會觸發一次 Minor GC,以收集新生代的垃圾,存活下來的對象,則會轉移到 Survivor區。
  • 大對象(需要大量連續記憶體空間的Java對象,如那種很長的字元串)直接進入老年態
  • 如果對象在Eden出生,並經過第一次Minor GC後仍然存活,並且被Survivor容納的話,年齡設為1,每熬過一次Minor GC,年齡+1,若年齡超過一定限制(15),則被晉升到老年態。即長期存活的對象進入老年態
  • 老年代滿了而無法容納更多的對象,Minor GC 之後通常就會進行Full GC,Full GC 清理整個記憶體堆 – 包括年輕代和年老代
  • Major GC 發生在老年代的GC清理老年區,經常會伴隨至少一次Minor GC,比Minor GC慢10倍以上

5.你知道哪幾種垃圾收集器,各自的優缺點,重點講下cms和G1,包括原理,流程,優缺點。

思路: 一定要記住典型的垃圾收集器,尤其cms和G1,它們的原理與區別,涉及的垃圾回收演算法。

我的答案:

1)幾種垃圾收集器:

  • Serial收集器: 單線程的收集器,收集垃圾時,必須stop the world,使用複製演算法。
  • ParNew收集器: Serial收集器的多線程版本,也需要stop the world,複製演算法。
  • Parallel Scavenge收集器: 新生代收集器,複製演算法的收集器,併發的多線程收集器,目標是達到一個可控的吞吐量。如果虛擬機總共運行100分鐘,其中垃圾花掉1分鐘,吞吐量就是99%。
  • Serial Old收集器: 是Serial收集器的老年代版本,單線程收集器,使用標記整理演算法。
  • Parallel Old收集器: 是Parallel Scavenge收集器的老年代版本,使用多線程,標記-整理演算法。
  • CMS(Concurrent Mark Sweep) 收集器: 是一種以獲得最短回收停頓時間為目標的收集器,標記清除演算法,運作過程:初始標記,併發標記,重新標記,併發清除,收集結束會產生大量空間碎片。
  • G1收集器: 標記整理演算法實現,運作流程主要包括以下:初始標記,併發標記,最終標記,篩選標記。不會產生空間碎片,可以精確地控制停頓。

2)CMS收集器和G1收集器的區別:

  • CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用;
  • G1收集器收集範圍是老年代和新生代,不需要結合其他收集器使用;
  • CMS收集器以最小的停頓時間為目標的收集器;
  • G1收集器可預測垃圾回收的停頓時間
  • CMS收集器是使用“標記-清除”演算法進行的垃圾回收,容易產生記憶體碎片
  • G1收集器使用的是“標記-整理”演算法,進行了空間整合,降低了記憶體空間碎片。

6.JVM記憶體模型的相關知識瞭解多少,比如重排序,記憶體屏障,happen-before,主記憶體,工作記憶體。

思路: 先畫出Java記憶體模型圖,結合例子volatile ,說明什麼是重排序,記憶體屏障,最好能給面試官寫以下demo說明。

我的答案:

1)Java記憶體模型圖:

Java記憶體模型規定了所有的變數都存儲在主記憶體中,每條線程還有自己的工作記憶體,線程的工作記憶體中保存了該線程中是用到的變數的主記憶體副本拷貝,線程對變數的所有操作都必須在工作記憶體中進行,而不能直接讀寫主記憶體。不同的線程之間也無法直接訪問對方工作記憶體中的變數,線程間變數的傳遞均需要自己的工作記憶體和主存之間進行數據同步進行。

2)指令重排序。

在這裡,先看一段代碼

public class PossibleReordering {
static int x = 0, y = 0;
static int a = 0, b = 0;

public static void main(String[] args) throws InterruptedException {
    Thread one = new Thread(new Runnable() {
        public void run() {
            a = 1;
            x = b;
        }
    });

    Thread other = new Thread(new Runnable() {
        public void run() {
            b = 1;
            y = a;
        }
    });
    one.start();other.start();
    one.join();other.join();
    System.out.println(“(” + x + “,” + y + “)”);
}
複製代碼

運行結果可能為(1,0)、(0,1)或(1,1),也可能是(0,0)。因為,在實際運行時,代碼指令可能並不是嚴格按照代碼語句順序執行的。大多數現代微處理器都會採用將指令亂序執行(out-of-order execution,簡稱OoOE或OOE)的方法,在條件允許的情況下,直接運行當前有能力立即執行的後續指令,避開獲取下一條指令所需數據時造成的等待3。通過亂序執行的技術,處理器可以大大提高執行效率。而這就是指令重排

3)記憶體屏障

記憶體屏障,也叫記憶體柵欄,是一種CPU指令,用於控制特定條件下的重排序和記憶體可見性問題。

  • LoadLoad屏障:對於這樣的語句Load1; LoadLoad; Load2,在Load2及後續讀取操作要讀取的數據被訪問前,保證Load1要讀取的數據被讀取完畢。
  • StoreStore屏障:對於這樣的語句Store1; StoreStore; Store2,在Store2及後續寫入操作執行前,保證Store1的寫入操作對其它處理器可見。
  • LoadStore屏障:對於這樣的語句Load1; LoadStore; Store2,在Store2及後續寫入操作被刷出前,保證Load1要讀取的數據被讀取完畢。
  • StoreLoad屏障:對於這樣的語句Store1; StoreLoad; Load2,在Load2及後續所有讀取操作執行前,保證Store1的寫入對所有處理器可見。它的開銷是四種屏障中最大的。 在大多數處理器的實現中,這個屏障是個萬能屏障,兼具其它三種記憶體屏障的功能。

4)happen-before原則

  • 單線程happen-before原則:在同一個線程中,書寫在前面的操作happen-before後面的操作。 鎖的happen-before原則:同一個鎖的unlock操作happen-before此鎖的lock操作。
  • volatile的happen-before原則:對一個volatile變數的寫操作happen-before對此變數的任意操作(當然也包括寫操作了)。
  • happen-before的傳遞性原則:如果A操作 happen-before B操作,B操作happen-before C操作,那麼A操作happen-before C操作。
  • 線程啟動的happen-before原則:同一個線程的start方法happen-before此線程的其它方法。
  • 線程中斷的happen-before原則 :對線程interrupt方法的調用happen-before被中斷線程的檢測到中斷發送的代碼。
  • 線程終結的happen-before原則: 線程中的所有操作都happen-before線程的終止檢測。
  • 對象創建的happen-before原則: 一個對象的初始化完成先於他的finalize方法調用。

7.簡單說說你瞭解的類載入器,可以打破雙親委派麽,怎麼打破。

思路: 先說明一下什麼是類載入器,可以給面試官畫個圖,再說一下類載入器存在的意義,說一下雙親委派模型,最後闡述怎麼打破雙親委派模型。

我的答案:

1) 什麼是類載入器?

類載入器 就是根據指定全限定名稱將class文件載入到JVM記憶體,轉為Class對象。

  • 啟動類載入器(Bootstrap ClassLoader):由C++語言實現(針對HotSpot),負責將存放在<JAVA_HOME>\lib目錄或-Xbootclasspath參數指定的路徑中的類庫載入到記憶體中。
  • 其他類載入器:由Java語言實現,繼承自抽象類ClassLoader。如:
  • 擴展類載入器(Extension ClassLoader):負責載入<JAVA_HOME>\lib\ext目錄或java.ext.dirs系統變數指定的路徑中的所有類庫。
  • 應用程式類載入器(Application ClassLoader)。負責載入用戶類路徑(classpath)上的指定類庫,我們可以直接使用這個類載入器。一般情況,如果我們沒有自定義類載入器預設就是用這個載入器。

2)雙親委派模型

雙親委派模型工作過程是:

如果一個類載入器收到類載入的請求,它首先不會自己去嘗試載入這個類,而是把這個請求委派給父類載入器完成。每個類載入器都是如此,只有當父載入器在自己的搜索範圍內找不到指定的類時(即ClassNotFoundException),子載入器才會嘗試自己去載入。

雙親委派模型圖:

3)為什麼需要雙親委派模型?

在這裡,先想一下,如果沒有雙親委派,那麼用戶是不是可以自己定義一個java.lang.Object的同名類java.lang.String的同名類,並把它放到ClassPath中,那麼類之間的比較結果及類的唯一性將無法保證,因此,為什麼需要雙親委派模型?防止記憶體中出現多份同樣的位元組碼

4)怎麼打破雙親委派模型?

打破雙親委派機制則不僅要繼承ClassLoader類,還要重寫loadClass和findClass方法。

8.說說你知道的幾種主要的JVM參數

思路: 可以說一下堆棧配置相關的,垃圾收集器相關的,還有一下輔助信息相關的。

我的答案:

1)堆棧配置相關

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k 
-XX:MaxPermSize=16m -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxTenuringThreshold=0
複製代碼

-Xmx3550m: 最大堆大小為3550m。

-Xms3550m: 設置初始堆大小為3550m。

-Xmn2g: 設置年輕代大小為2g。

-Xss128k: 每個線程的堆棧大小為128k。

-XX:MaxPermSize: 設置持久代大小為16m

-XX:NewRatio=4: 設置年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代)。

-XX:SurvivorRatio=4: 設置年輕代中Eden區與Survivor區的大小比值。設置為4,則兩個Survivor區與一個Eden區的比值為2:4,一個Survivor區占整個年輕代的1/6

-XX:MaxTenuringThreshold=0: 設置垃圾最大年齡。如果設置為0的話,則年輕代對象不經過Survivor區,直接進入年老代。

2)垃圾收集器相關

-XX:+UseParallelGC
-XX:ParallelGCThreads=20
-XX:+UseConcMarkSweepGC 
-XX:CMSFullGCsBeforeCompaction=5
-XX:+UseCMSCompactAtFullCollection:
複製代碼

-XX:+UseParallelGC: 選擇垃圾收集器為並行收集器。

-XX:ParallelGCThreads=20: 配置並行收集器的線程數

-XX:+UseConcMarkSweepGC: 設置年老代為併發收集。

-XX:CMSFullGCsBeforeCompaction:由於併發收集器不對記憶體空間進行壓縮、整理,所以運行一段時間以後會產生“碎片”,使得運行效率降低。此值設置運行多少次GC以後對記憶體空間進行壓縮、整理。

-XX:+UseCMSCompactAtFullCollection: 打開對年老代的壓縮。可能會影響性能,但是可以消除碎片

3)輔助信息相關

-XX:+PrintGC
-XX:+PrintGCDetails
複製代碼

-XX:+PrintGC 輸出形式:

[GC 118250K->113543K(130112K), 0.0094143 secs] [Full GC 121376K->10414K(130112K), 0.0650971 secs]

-XX:+PrintGCDetails 輸出形式:

[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs] [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs

9.怎麼打出線程棧信息。

思路: 可以說一下jps,top ,jstack這幾個命令,再配合一次排查線上問題進行解答。

我的答案:

  • 輸入jps,獲得進程號。
  • top -Hp pid 獲取本進程中所有線程的CPU耗時性能
  • jstack pid命令查看當前java進程的堆棧狀態
  • 或者 jstack -l > /tmp/output.txt 把堆棧信息打到一個txt文件。
  • 可以使用fastthread 堆棧定位,fastthread.io/

10.強引用、軟引用、弱引用、虛引用的區別?

思路: 先說一下四種引用的定義,可以結合代碼講一下,也可以擴展談到ThreadLocalMap里弱引用用處。

我的答案:

1)強引用

我們平時new了一個對象就是強引用,例如 Object obj = new Object();即使在記憶體不足的情況下,JVM寧願拋出OutOfMemory錯誤也不會回收這種對象。

2)軟引用

如果一個對象只具有軟引用,則記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體空間不足了,就會回收這些對象的記憶體。

SoftReference<String> softRef=new SoftReference<String>(str);     // 軟引用
複製代碼

用處: 軟引用在實際中有重要的應用,例如瀏覽器的後退按鈕。按後退時,這個後退時顯示的網頁內容是重新進行請求還是從緩存中取出呢?這就要看具體的實現策略了。

(1)如果一個網頁在瀏覽結束時就進行內容的回收,則按後退查看前面瀏覽過的頁面時,需要重新構建

(2)如果將瀏覽過的網頁存儲到記憶體中會造成記憶體的大量浪費,甚至會造成記憶體溢出

如下代碼:

Browser prev = new Browser();               // 獲取頁面進行瀏覽
SoftReference sr = new SoftReference(prev); // 瀏覽完畢後置為軟引用        
if(sr.get()!=null){ 
    rev = (Browser) sr.get();           // 還沒有被回收器回收,直接獲取
}else{
    prev = new Browser();               // 由於記憶體吃緊,所以對軟引用的對象回收了
    sr = new SoftReference(prev);       // 重新構建
}
複製代碼

3)弱引用

具有弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的記憶體區域的過程中,一旦發現了只具有弱引用的對象,不管當前記憶體空間足夠與否,都會回收它的記憶體。

String str=new String("abc");    
WeakReference<String> abcWeakRef = new WeakReference<String>(str);
str=null;
等價於
str = null;
System.gc();
複製代碼

4)虛引用

如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。虛引用主要用來跟蹤對象被垃圾回收器回收的活動。

11.待更新

參考與感謝


作者:Jay_huaxiao
鏈接:https://juejin.im/post/5d35ca5b518825449c64bc31
來源:掘金
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。 推薦: 系統化的學習jvm視頻教程:點擊馬上學習
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 引言 🏂 1.寫這篇文章的緣由是上周在公司前端團隊的code review時,看了一個實習小哥哥的代碼後,感覺一些剛入行不久的同學,對於真實項目中的一些js處理不是很熟練,缺乏一些技巧。 2.因此整理了自己開發中常用的一些js技巧,靈活的運用,會增強你解決問題的能力,也會對你的代碼簡潔性有很大的改 ...
  • 前言 星期六閑著沒事,就想著寫寫原生js玩玩,在網上看了幾個效果後決定做這個效果,並且使用了prototype和eventEmitter封裝成了庫。 最終效果 分析 1.看到這個效果我們首先應該想到和拖動有關的api: onmousedown, onmousemove, onmouseup 其次要支 ...
  • 通過HTML與JS實現點擊button按鈕,實現切換效果,可以在js裡面增加自己相應的業務代碼,代碼如下: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <!-- HTML樣式部分 --> <form acti ...
  • 登高遠眺 天高地迥,覺宇宙之無窮 基礎技術 直播延遲?為什麼流媒體直播的延遲那麼高 隨著通信技術的發展,視頻點播、直播業務也逐漸興起。在這些業務形態中,流媒體技術扮演著重要的角色。但在實際使用中,即便是在優良的網路環境中,主播和觀眾間依然可以體驗到明顯的延遲。為何會產生這種延遲?這篇文章從編碼、傳輸 ...
  • <template> <div class="hello" style="height:100%"> <div class="hader-iframe"> <div :class="logClass"> <img src="../assets/logo.png" alt /> <p>管理系統</p> ...
  • 問題場景:在前端日趨工程化的今天,前端性能優化是一名合格的前端工程師必備的技能,那麼,如何正確的使用性能分析工具呢? 解決方案: 性能分析的流程: 在開發中我一般使用公司開發的測試腳本-kbase-watcher,可以更加直觀的進行頁面的優化,大家也可以在網上找到許多類似的插件 可以生成性能分析報告 ...
  • 在開發工作中,我們常常要將整體的開發內容分解成一些較小的部分,分而治之。 原因不限於以下幾種: 分解和抽象使得開發內容更容易被理解。 可以將分解後的開發內容分配給多人開發。 分解後的開發時間更容易估算,進度更易於衡量,有利於做計劃。 古人說“橫看成嶺側成峰”,意指從不同的角度觀察事物時會得到不同的抽 ...
  • 老孟導讀:大家好,這是【Flutter實戰】系列文章的第二篇,這一篇講解文本組件,文本組件包括文本展示組件(Text和RichText)和文本輸入組件(TextField),基礎用法和五個案例助你快速掌握。 第一篇鏈接:【Flutter實戰】移動技術發展史 Text Text是顯示文本的組件,最常用 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...