JVM性能優化簡介

来源:https://www.cnblogs.com/jjpbk/archive/2019/10/23/11728459.html
-Advertisement-
Play Games

01. JVM是什麼 概述: 大白話: 全稱Java Virtual Machine(Java虛擬機), 它是一個虛構出來的電腦, 通過實際的電腦來模擬各種電腦的功能. 專業版: JVM是一個進程, 用來模擬計算單元, 將.class位元組碼文件轉成電腦能夠識別的指令. //這裡可以聯想以前大 ...


01. JVM是什麼
    概述:
        大白話:
            全稱Java Virtual Machine(Java虛擬機), 它是一個虛構出來的電腦, 通過實際的電腦來模擬各種電腦的功能.
        專業版:
            JVM是一個進程, 用來模擬計算單元, 將.class位元組碼文件轉成電腦能夠識別的指令.
        //這裡可以聯想以前大家學的"VM ware", 它也是一個虛擬機.
        //它們的區別就在於: VM Ware是你能看見的, JVM是你看不見的.
        
    回顧:
        我們以前寫的Java程式是: 編寫 --> 編譯 --> 運行三個階段的.
        .class文件是Java語言獨有的, 只有JVM能識別, 其他任何軟體都識別不了.
        所以Java語言的"跨平臺性(一次編譯到處運行)"就是由JVM來保證的.
        
    畫圖演示:
        JVM把.class位元組碼文件 轉成 電腦能夠識別的指令的過程.
        
    代碼演示:
        D:\compile\Worker.java文件, 通過"jps"命令查看啟動的進程.
        
    
02. JVM虛擬機運行的流程
    JVM是一個進程,接下來我們來研究它的: 工作機制, 這個問題是很深奧的, 不亞於研究一個完整VM Ware虛擬機,但是諸如"硬碟, CD/DVD這些部分和我們都沒關係", 所以研究JVM的工作機制就是在研究它的: 運算機制.
    首先, 請你思考一個問題: 如果我給你一個A.class位元組碼文件, 想把它運行起來, 你會做哪些事情?
    
    畫圖演示:
        1. 讀取位元組碼文件所在的路徑.
            //類載入機制
        2. 獲取位元組碼文件中具體的內容.
            //方法區: 用來存放類的描述信息.
        3. 獲取該類的實例(對象)
            //堆(Heap): 用來存儲對象的(所有new出來的內容)
        4. 通過對象名.的方式調用方法.
            //棧(Stack): 用來存放局部變數及所有代碼執行的.
        
    今天我們的學習順序, 就是按照這個流程來走的.


03. JVM虛擬機類載入機制(一):運行順序
    首先, 我們先來研究JVM的類載入機制, 類載入機制就是把類給讀取出來, 我們來看一下它是如何運行的.
    畫圖演示:
        JVM底層載入類依靠三大組件:
            BootStrapClassLoader    //啟動類載入器
                //負責載入: jre\lib\rt.jar        //rt: runtime, 運行的意思
                //windows最早不支持java, 沒有JRE, 後來Sun公司打官司贏了, windows開始預設支持JRE.
            ExtClassLoader:            //擴展類載入器
                //負責載入: jre\lib\ext\* 文件夾下所有的jar包
                //這兩個載入器執行完畢後, JVM虛擬機基本上就初始化完畢了.
            APPClassLoader:            //應用程式類載入器
                //負責載入: 用戶自定義的類的.
                //就是載入: 用戶配置的classpath環境變數值的.
            //UserClassLoader        //自定義類載入器
                //自定義類載入器就是自定義一個類繼承ClassLoader, 然後重寫findClass(), loadClass()兩個方法即可.
                
        載入順序是:     BootStrap --> ExtClassLoader --> AppClassLoader --> UserClassLoader
        
    代碼演示:
        1) 隨便編寫一個A類, 然後演示: jar包的載入過程(rt.jar, ext\*等相關的jar包)
        2) 列印類載入器對象:
             //1. 獲取當前線程的類載入器
            ClassLoader load = Thread.currentThread().getContextClassLoader();
            //2. 列印當前線程的類載入器.
            System.out.println(load);                           //AppClassLoader
            //3. 列印當前線程的類載入器的父類(載入器).
            System.out.println(load.getParent());               //ExtClassLoader
            //4. 列印當前線程的類載入器的父類的父類(載入器).
            System.out.println(load.getParent().getParent());   //null: 其實應該是BootStrapClassLoader, 但是它是C語言寫的, 所以列印不出來.
        
        
04) JVM虛擬機類載入機制(二):檢查順序
    剛纔我們學完了JVM類載入機制的"載入循序", 現在, 我們來研究下它的"檢查順序", 請你思考,
    假設: D:\compile, ext\*.jar, rt.jar三類中都有 A.class, 那麼A.class是否會被載入3次, 如果不會, 它的載入順序是什麼樣的?
        不會, BootStrap會載入A.class.
        
    運行順序是:
        bootstrap --> ext --> app
        1) bootstrap先載入 A.class
        2) ext檢查A.class是否載入:
            是: 不載入A.class
            否: 載入A.class
        3) app檢查A.class是否載入:
            是: 不載入A.class
            否: 載入A.class
        
    例如:
        UserClassLoader
        APPClassLoader
        ExtClassLoader
        BootStrapClassLoader
    總結:
        自上而下檢查, 自下而上運行.
    
    
05) JVM的記憶體模型(方法區, 堆區, 棧區, 程式計數器)
    到目前為止我們已經知道類載入器是用來載入位元組碼文件的, 那載入完位元組碼文件之後, 是不是要運行起來啊?
    那它是怎麼運行的呢? 在我的課件中有一個"JVM運行時記憶體數據區", 接下來我們詳細的來學習一下.
    
    1) A.class位元組碼文件被載入到記憶體.
        //存儲在方法區中, 並且方法區中也包含常量池.
        
    2) 創建本類的實例對象, 存儲在堆中(heap)
    
    3) 通過對象名.的形式調用方法, 方法執行過程是在: 虛擬機棧中完成的.
        //一個線程對應一個虛擬機棧, 每一個方法對應一個: 虛擬機棧中的棧幀
    
    4) 程式計數器區域記錄的是當前程式的執行位置, 例如:
        線程1: print(), 第3行
    
    5) 將具體要執行的代碼交給: 執行引擎來執行.
    
    6) 執行引擎調用: 本地庫介面, 本地方法庫來執行具體的內容.    
        //這部分瞭解即可, 用native修飾的方法都是本地方法.
        
    7) 本地方法棧: 顧名思義, 就是本地方法執行的區域.(C語言, 外部庫運行的空間)
        //瞭解即可.
    
    8) 直接記憶體: 大白話翻譯, 當JVM記憶體不夠用的時候, 會找操作系統"借點"記憶體.
        //瞭解即可.
    
06) JVM的一個小例子    
    1) 編寫源代碼.
        //創建一個A類, 裡邊有個print()方法.
        public class A {
            public void print() {
                System.out.println("h");
                System.out.println("e");
                System.out.println("l");
                System.out.println("l");    
                System.out.println("o");
            }
        }
    
    2) 在A類中, 編寫main()函數, 創建兩個線程, 分別調用A#print()方法.
        /*
            java A  //運行Java程式
            載入類:
                1) bootstrap 載入rt.jar
                2) ext 載入 jre\lib\ext\*.jar
                3) app 載入 A.class
            具體運行:
                1) 主函數運行. 棧中有個主線程, 調用MainThread.main();
                2) 執行第23行,  A a = new A(); 將a對象存儲到堆區.
                3) 執行第24行, 調用a.print()方法, 生成一個棧幀, 壓入主線程棧.
                -----> 執行, 運行print()方法的5行代碼.

                4) 棧中有個新的線程, t1,
                    t1 --> run棧幀 --> print棧幀
                5) 棧中有個新的線程, t2,
                    t2 --> run棧幀 --> print棧幀

         */
        public class A {
            public void print() {
                System.out.println("h");
                System.out.println("e");
                System.out.println("l");
                System.out.println("l");
                System.out.println("o");
            }

            public static void main(String[] args) {
                A a = new A();
                a.print();

                //創建兩個線程對象, 調用A#print();
                //線程是CPU運行的基本單位, 創建銷毀由操作系統執行.
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        a.print();
                    }
                }).start();

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        a.print();
                    }
                }).start();
            }
        }

    3) 畫圖演示此代碼的執行流程.
    4) 時間夠的情況下, 演示下: 守護線程和非守護線程.
        
        
        
07) 線程安全和記憶體溢出的問題
    到目前為止, 大家已經知道了JVM的記憶體模型, 也知道了各個模塊的作用,
    接下來, 請你思考一個問題: 上述的模塊中, 哪些模塊會出現線程安全的問題,
    哪些模塊有記憶體溢出的問題?
    
    舉例:
        public class A{
            int i;
            
            public void add() {
                i++;
            }
        }
        //當兩個線程同時調用add()方法修改變數i的值時, 就會引發線程安全問題.
    畫圖演示上述代碼.
    
    結論:
        1) 存線上程安全問題的模塊.
            堆: 會.         //多線程, 併發, 操作同一數據.
            棧:    不會.        //線程棧之間是相互獨立的.
            方法區:    不會.    //存儲常量, 類的描述信息(.class位元組碼文件).
            程式計數器:不會.//記錄程式的執行流程.
        
        2) 存在記憶體溢出問題的模塊.
            堆: 會.         //不斷創建對象, 記憶體被撐爆.
            棧: 會.         //不斷調用方法, 記憶體被撐爆.
            方法區: 會.     //常量過多, jar包過大, 記憶體被撐爆.
            程式計數器: 會. //理論上來講會, 因為線程過多, 導致計數器過多, 記憶體被撐爆.
        
    其實我們研究JVM性能優化, 研究的就是這兩個問題, 這兩個問題也是常見面試題.
    //面試題:說一下你對 線程安全和記憶體溢出這兩個問題的看法.
    
    總結:
        研究這兩個問題, 其實主要研究的還是"堆(Heap)記憶體".
        
        
08) JDK1.7的堆記憶體的垃圾回收演算法
    JDK1.7 將堆記憶體劃分為3部分: 年輕代, 年老代, 持久代(就是方法區).
    年輕代又分為三個區域:    //使用的是 複製演算法(需要有足夠多的空閑空間).
        Eden: 伊甸園
            //存儲的新生對象, 當伊甸園滿的時候, 會將存活對象複製到S1區.
            //並移除那些垃圾對象(空指針對象).
        Survivor: 幸存者區1
            //當該區域滿的時候, 會將存活對象複製到S2區
            //並移除那些垃圾對象.
        Survivor: 幸存者區2
            //當該區域滿的時候, 會將存活對象複製到S1區.
            //並移除那些垃圾對象.
        大白話翻譯:
            s1區 和 s2區是來回互相複製的.
    
    年老代:    //使用的是標記清除演算法, 標記整理演算法.
        //當對象在S1區和S2區之間來回覆制15次, 才會被載入到: 年老代.
        //當年輕代和年老代全部裝滿的時候, 就會報: 堆記憶體溢出.
    
    持久代:    //就是方法區
        存儲常量, 類的描述信息(也叫: 元數據).
    
        
09) JDK1.7預設垃圾回收器    //所謂的回收器, 就是已經存在的產品, 可以直接使用.
    Serial收集器:
        單線程收集器, 它使用一個CPU或者一個線程來回收對象,
        它在垃圾收集的時候, 必須暫停其他工作線程, 直到垃圾回收完畢.
        //類似於: 國家領導人出行(封路), 排隊點餐(遇到插隊現象)
        //假設它在回收垃圾的時候用了3秒, 其他線程就要等3秒, 這樣做效率很低.
        
    ParNew收集器:
        多線程收集器, 相當於:  Serial的多線程版本.
        
    
    Parallel Scavenge收集器:
        是一個新生代的收集器,並且使用複製演算法,而且是一個並行的多線程收集器.
        其他收集器是儘量縮短垃圾收集時用戶線程的停頓時間,而Parallel Scavenge收集器的目標是達到一個可控制的吞吐量:
            吞吐量 = 運行用戶代碼時間 / (運行用戶代碼時間+垃圾收集時間)
            (虛擬機總共運行100分鐘,垃圾收集時間為1分鐘,那麼吞吐量就是99%)
        //因為虛擬機會根據系統運行情況進行自適應調節, 所以不需要我們設置.
        
    CMS收集器:    //主要針對於年老代.
        整個過程分為:
            初始標記;    //用戶線程等待
            併發標記;    //用戶線程可以執行
            重新標記;    //用戶線程等待
            併發清除;    //用戶線程可以執行
        可以理解為是:
            精細化運營, 前邊的垃圾收集器都是一刀切(在回收垃圾的時候, 其他線程等待), 而CMS是儘可能的降低等待時間, 並行執行程式, 提高運行效率.
    以上為JDK1.7及其以前的垃圾回收器, JDK1.8的時候多了一個: G1.
    G1在JDK1.9的時候, 成為了預設的垃圾回收器.
    
    
    
10) VM巨集觀結構梳理
    1) Java程式的三個階段:
        編寫: A.java
        編譯: javac A.java
        運行: java A.class
        
    2) 類載入器
        bootstrap
        ext
        app
        
    3) JVM的記憶體結構
        堆:
            年輕代
            年老代
            持久代(也就是方法區)
                元數據(類的描述信息, 也就是.class位元組碼文件), 常量池
            
        棧:
            有n個線程棧, 每個線程棧又會有n個棧幀(一個棧幀就是一個方法)
            
        程式計數器:
            用來記錄程式的執行流程的.
            
        本地方法棧:
            C語言, 外部程式運行空間.
            
11) G1垃圾回收器
    在上個圖解上做優化, 用G1新圖解, 覆蓋之前堆中的內容.
    
    1) 將記憶體劃分為同樣大小的region(區域).
    2) 每個region既可以是年輕代, 也可以是老年代, 還可以是幸存者區.
    3) 程式運行前期, 創建大量對象的時候, 可以將每個region看做是: Eden(伊甸園).
    4) 程式運行中期, 可以將eden的region變成old的region.
    5) 程式運行後期, 可以縮短Eden, Survivor的區域, 變成Old區域.
        //這樣做的好處是: 儘可能大的利用堆記憶體空間.
    6) H: 存儲大對象的.
    7) G1是JDK1.8出來的, 在JDK1.9的時候變成了: 預設垃圾處理器.
    
    
12) G1中的持久代(方法區)不見了
    方法區從JVM模型中遷移出去了, 完全使用系統的記憶體.
    方法區也改名叫: 元數據區.
    
    
13) 記憶體溢出的代碼演示
    1) 堆記憶體溢出演示: main.java.heap.PrintGC_demo.java
        //創建對象多, 導致記憶體溢出.
        
    2) 棧記憶體溢出演示:
        main.java.stack.StackOverFlow(遞歸導致的)
        //不設置的話在5000次左右, 設置256K後在1100次左右.
        
        main.java.stack.Thread(不斷創建線程導致的)
        //這個自行演示即可, 電腦太卡, 影響上課效果.
        
    3) 方法區記憶體溢出演示:
        main.java.method.MethodOOM        //常量過多
        main.java.direct.DirectMenOOM    //jar包過大, 直接溢出.
        
    總結:
        可能你未來的10年都碰不到JVM性能調優這個事兒, 先不說能不能調優, 而是大多數的
        公司上來就擼代碼, 很少會有"JVM調優"這個動作, 即使遇到了"JVM調優", 公司裡邊
        還有架構師呢, 但是我們馬上要找工作了, 把這些相關的題瞭解瞭解, 看看, 對面試會
        比較有幫助.
        //JVM調優一般是只看, 不用, 目前只是為了面試做準備.
        
14) 引用地址值比較
    直接演示src.main.method.ATest類中的代碼即可.
    //講解==比較引用類型的場景.
        
    
15) JVM調優案例賞析
    百度搜索 --> JVM調優實踐, 一搜一大堆的案例.
    
    
16) GC的調優工具jstat        //主要針對於GC的.
    1) 通過Dos命令運行 D:\compile\Worker.java
        
    2) 重新開啟一個Dos視窗:
        //可以通過jps指令查看pid值.
        jstat -class 2041(Java程式的PID值)        //查看載入了多少個類
        jstat -compiler 2041(Java程式的PID值)    //查看編譯的情況
        jstat -gc 2041(Java程式的PID值)            //查看垃圾回收的統計        
        jstat -gc 2041 1000 5                    //1秒列印1次, 總共列印5次
        
17) GC的調優工具jmap        //主要針對於記憶體使用情況的.
    1) 通過Dos命令運行 D:\compile\Worker.java
    
    2) jmap -heap 2041(Java程式的PID值)            //查看記憶體使用情況
       jmap -histo 2041 | more                    //查看記憶體中對象數量及大小
       jmap -dump:format=b,file=d:/compile/dump.dat 2041    //將記憶體使用情況dump到文件中
       jhat -port 9999 d:/compile/dump.dat                  //通過jhat對dump文件進行分析
            //埠號可以自定義, 然後在瀏覽器中通過127.0.0.1:9999就可以訪問了.
            
            
18) GC的調優工具jstack-死鎖      //針對於線程的.  
    1) 線程的六種狀態:
        新建, 就緒, 運行(運行的時候會發生等待或者阻塞), 死亡.
        
    2) 編寫一個死鎖的代碼.
        //兩個線程, 兩把鎖, 一個先拿鎖1, 再拿鎖2, 另一個先拿鎖2, 在拿鎖1.
        
    3) 通過jstack命令可以查看Java程式狀態.  
        jstack 2041        //查看死鎖狀態
        
                                                
19) GC的可視化調優工具    //jstat, jmap, jstack
    1) 本地調優.
        1.1) 該工具位於 JDK安裝目錄/bin/jvisualvm.exe
            //雙擊可以直接使用.
            
        1.2) 以IntelliJ Platform為例, 演示下各個模塊的作用.
            
        1.3) 該工具涵蓋了上述所有的命令.
        
    2) 遠程調優.        //自行測試(目前先瞭解即可).
        java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.port=9999 DeadLock
        
        這幾個參數的意思是:
            -Dcom.sun.management.jmxremote :允許使用JMX遠程管理
            -Dcom.sun.management.jmxremote.port=9999 :JMX遠程連接埠
            -Dcom.sun.management.jmxremote.authenticate=false :不進行身份認證,任何用戶都可以連接
            -Dcom.sun.management.jmxremote.ssl=false :不使用ssl
    
    
20) JVM的總結
    1) 什麼是JVM?
    2) JVM類載入機制.
        //bootstrap, ext, app
    3) JVM記憶體模型.
    4) 垃圾回收演算法.
        複製演算法:
            針對於年輕代.
            
        標記清除演算法:
        標記整理演算法:
            針對於老年代
    5) JVM垃圾回收器.
        Serial單線程.
        ParNew多線程.
        Parallel Scavenge: 併發多線程.
        CMS: 以獲取"最短垃圾回收停頓時間"為目標的收集器.
        G1: JDK1.8出現的, JDK1.9被設置成預設垃圾回收器.
    6)     JVM調優工具:
        jstat, jmap, jstack, 可視化調優工具(jvisualvm.exe).
    
    
//以下內容是為了面試用, 找工作前一周, 看看下麵的題即可.
21) JVM的線程安全與鎖的兩種方式
    線程安全:
        多線程, 併發, 操作同一數據, 就有可能引發安全問題, 需要用到"同步"解決.
        
    "同步"分類:
        同步代碼塊:
            格式:
                synchronized(鎖對象) {
                    //要加鎖的代碼
                }
            註意:
                1) 同步代碼塊的鎖對象可以是任意類型的對象.
                    //對象多, 類鎖均可.
                2) 必須使用同一把鎖, 否則可能出現鎖不住的情況.                //String.class
                
        同步方法:
            靜態同步方法:
                鎖對象是: 該類的位元組碼文件對象.        //類鎖
                
            非靜態同步方法:
                鎖對象是: this                        //對象鎖
    
22) 臟讀-高圓圓是男的
    1) 演示main.java.thread.DirtyRead.java類的代碼即可.
    
    2) 自定義線程修改姓名後, 要休眠3秒, 而主線程休眠1秒後即調用getValue()列印姓名和年齡,
       如果getValue()方法沒加同步, 會出現"臟讀"的情況.
       
23) 瞭解Lock鎖.
    1) Lock和synchronized的區別
         1.1) synchronized是java內置的語言,是java的關鍵字
        1.2) synchronized不需要手動去釋放鎖,當synchronized方法或者synchronized代碼塊執行完畢。
            系統會自動釋放對該鎖的占用。
            而lock必須手動的釋放鎖,如果沒有主動的釋放鎖,則可能造成死鎖的問題
    2) 示例代碼
        public class Demo02 {
            private Lock lock = new ReentrantLock();

            public void method01() {
                lock.lock();
                System.out.print("i");
                System.out.print("t");
                System.out.print("c");
                System.out.print("a");
                System.out.print("s");
                System.out.print("t");
                System.out.println();
                lock.unlock();
            }


            public void method02() {
                lock.lock();
                System.out.print("我");
                System.out.print("愛");
                System.out.print("你");
                System.out.print("中");
                System.out.print("國");
                System.out.println();
                lock.unlock();
            }
        }

    


https://blogs.oracle.com/jonthecollector/our-collectors
  
    
        


        
        
        
        
        
        
        
        
        
    
   


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 下載安裝rtpMIDI (網路MIDI驅動程式) 打開rtpMIDI,在“My session”那裡按下+,就會自動顯示你的電腦的名字 檢查Bonjour服務正常運行,iPad與pc網路正常連接 iPad端運行midi鍵盤程式,進入midi設置,設置為網路連接(可用於iPad的midi模擬應用Pia ...
  • 背景 最近在Linux官網下載了Linux內核,下載下來的是一個尾碼為.tar.xz的壓縮包,於是在毫不知情的情況下隨隨便便解壓了,解壓過程中出現了很多問題。 其中一個問題就是在Windows下,不區分大小寫的文件名,但是在Ubuntu下就可以區分。比如,abc.txt和ABC.txt會被認為是同一 ...
  • 點擊【虛擬機】選項中的【安裝VMware Tools】,此時在Ubuntu的桌面上就會出現一個光碟圖標。 如果之前已經安裝過了,【虛擬機】選項中應為【重新安裝VMware Tools】。 如果【重新安裝VMware Tools】選項是灰色的,則需要點擊【虛擬機】選項中的【設置】,在【設置】中點擊【C ...
  • 正文: Setup 1 系統信息 安裝 貼下系統信息 [圖1] 下麵是已經安裝完成的Telnet版本查詢,如果還沒有安裝的是沒有包信息顯示的。[圖2] Telnet 遠程登陸工具,Windows裡面常常用來測試埠用(- .- !),Xinetd 第一次看見,百科里說是監視網路需求的守護進程(不曉得 ...
  • 資料庫基礎 第一章:資料庫的簡介 資料庫 資料庫(dataBase,DB)是指長期存儲在電腦內的,有組織,可共用的數據的集合。 資料庫中的數據按一定的數學模型組織、描述和存儲,具有較小的冗餘,較高的數據獨立性和易擴展性,並可為各種用戶共用。資料庫管理系統軟體 資料庫管理系統(Database Ma ...
  • 阿裡雲RDS for SQL Server的賬號管理有不少小Bug,而且有一個很嚴重的Bug:任何普通賬號,都能創建資料庫。註意,我這裡是說任意普通賬號,任意任意普通賬號!任意任意普通賬號!重要的事情說三遍。 例如,下麵測試環境所示,RDS for SQL Server的資料庫版本為SQL Serv... ...
  • mysql 無法連接提示 Authentication plugin 'caching_sha2_password' cannot be loaded 可能是密碼沒有設置或者,密碼設置類型不符,可參考如下步驟解決 1 查看當前賬戶,和密碼 或者是否設置了authentication_string 2 ...
  • 這是一篇自學MySQL的小案例,下麵是部分數據信息:goods表 1、查詢cate_name為‘超級本’的商品名稱、價格 SELECT `name`, priceFROM goodsWHERE cate_name like '超級本'; -- 2、查詢商品的種類 SELECT cate_nameFR ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...