Net 高級調試之十一:托管堆佈局架構和對象分配機制

来源:https://www.cnblogs.com/PatrickLiu/archive/2023/12/01/17864600.html
-Advertisement-
Play Games

本項目案例後臺採用.NET6(C#)開發,前端採用React&React Native,數字孿生採用3DMAX&U3D。綜合運用“物、大、智、雲、移”技術,採用雲-邊-端工業互聯網架構,設備端基於工業感測器和物聯網保障動態感知,邊緣側基於工藝機理、專家知識、數據科學等多種技術手段實現工況診斷,大數據... ...


一、簡介
    今天是《Net 高級調試》的第十一篇文章,這篇文章來的有點晚,因為,最近比較忙,就沒時間寫文章了。現在終於有點時間,繼續開始我們這個系列。這篇文章我們主要介紹托管堆的架構,對象的分配機制,我們如何查找在托管堆上的對象,我學完這章,很多以前很模糊的概念,現在很清晰了,知道了對象代的分配,大對象堆和小對象堆的結構,瞭解了對象的生命周期,這些是 Net 框架的底層,瞭解更深,對於我們調試更有利。當然了,第一次看視頻或者看書,是很迷糊的,不知道如何操作,還是那句老話,一遍不行,那就再來一遍,還不行,那就再來一遍,俗話說的好,書讀千遍,其意自現。
     如果在沒有說明的情況下,所有代碼的測試環境都是 Net Framewok 4.8,但是,有時候為了查看源碼,可能需要使用 Net Core 的項目,我會在項目章節里進行說明。好了,廢話不多說,開始我們今天的調試工作。

       調試環境我需要進行說明,以防大家不清楚,具體情況我已經羅列出來。
          操作系統:Windows Professional 10
          調試工具:Windbg Preview(可以去Microsoft Store 去下載)
          開發工具:Visual Studio 2022
          Net 版本:Net Framework 4.8
          CoreCLR源碼:源碼下載

二、基礎知識
    
    1、托管堆和垃圾回收
        1、Windows 記憶體架構
            要瞭解 C# 的記憶體分配機制,首先需要瞭解 Windows 記憶體分配的機制,畢竟 CLR 中的記憶體是從 Windows 上分配過來的。架構圖如下:
            

    2、CLR堆管理器

        2.1、簡介
            CLR 堆管理器托管的記憶體劃分成兩大塊。
            a、按對象大小劃分
                所有小於 85000 byte 的對象都存放在【小對象堆(SOH)】,大於等於 85000 byte 的對象存放在【大對象堆(BOH)】。

            b、按生存期劃分。
                CLR 假設一個新分配的對象往往更容易成為一個垃圾對象,所以回收這些對象的效率會更高,所以在【小對象堆(SOH)】做了一個【代機制】的劃分,也就是 0代、1代、2代。            

        2.2、托管堆佈局圖
            托管堆是由很多的段(segment)組成的,新生成的 segment 叫做臨時段,其他的叫 segment 年長段。小對象堆有臨時段的概念,大對象堆是沒有的。結構如圖:
            
            托管堆是由段組成的,在小對象堆中,最新創建的是臨時段,其他則是記憶體段,依次類推。所有的0代對象和1代對象都會分配在臨時段上,2代對象會有一部分放在臨時段上,其他的段,比如:記憶體段,永遠存放的是2代對象。

        2.3、工作站和伺服器GC
            CLR 有兩種 GC 模式,分別是:工作站 GC 和 伺服器GC。
            a)、工作站GC
                工作站GC 一般指具有視窗類的應用程式,比如:WinForm,WPF,SliverLight,Console,這樣的程式只有一個托管堆。
                效果如圖:
                  
            b)、伺服器GC
                對於 Web 類的程式,一般預設使用 伺服器GC,它的托管堆個數和當前機器 CPU 的核數一致。
                如圖:
                

    3、對象分配
        當我們在對象堆中分配一個對象時,大致流程如下:
          a)、將一個對象分配到托管堆上。
          b)、如果托管堆的空間不足,將會觸發 GC。
          c)、GC 觸發之後,如果空間足夠的話,就會存放對象。
          d)、如果垃圾對象帶有析構函數,那麼將會進入到【可終結隊列】中被執行。

          過程如圖:
          

    4、dumpheap 命令介紹
        為了能夠高效的篩選托管堆中的對象,SOS 提供了一個【!dumpheap】命令,這個命令十分強大,可以幫助我們很方便的篩選。 

三、調試過程
    廢話不多說,這一節是具體的調試過程,又可以說是眼見為實的過程,在開始之前,我還是要啰嗦兩句,這一節分為兩個部分,第一部分是測試的源碼部分,沒有代碼,當然就談不上測試了,調試必須有載體。第二部分就是根據具體的代碼來證實我們學到的知識,是具體的眼見為實。

    1、調試源碼
        1.1、Example_11_1_1
 1 namespace Example_11_1_1
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             byte[] byte1 = new byte[10000];
 8             byte[] byet2 = new byte[85000];
 9             Console.WriteLine("Hello world!");
10             Console.ReadLine();
11         }
12     }
13 }
View Code
        1.2、Example_11_1_2
            這個項目很簡單,就是建立一個 Asp.Net WebAPI 的項目,不需要寫任何代碼,使用預設功能就可以。

        1.3、Example_11_1_3
 1 namespace Example_11_1_3
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             byte[] byte1 = new byte[85000];
 8             byte[] byte2 = new byte[1500];
 9             byte[] byet3 = new byte[3500];
10             Console.WriteLine("3 個 byte[] 分配完畢!");
11             Console.ReadLine();
12         }
13     }
14 }
View Code
    2、眼見為實
        項目的所有操作都是一樣的,所以就在這裡說明一下,但是每個測試例子,都需要重新啟動,並載入相應的應用程式,載入方法都是一樣的。流程如下:我們編譯項目,打開 Windbg,點擊【文件】----》【launch executable】附加程式,打開調試器的界面,程式已經處於中斷狀態。

        2.1、我們查看 NT 堆和 GC 堆。
            調試源碼:Example_11_1_1
            1)、我們先來查看一下 NT 堆。
            當我們成功進入調試器界面,使用【g】命令,繼續運行,我們會在12行代碼【Console.ReadLine()】暫停,我們程式列印出了【Hello world】,我們點擊調試器工具欄中的【break】按鈕,就可以調試程式了。
            我們使用【!address -summary 】命令,查看一下具體情況。
 1 0:006> !address -summary
 2 
 3                                      
 4 Mapping file section regions...
 5 Mapping module regions...
 6 Mapping PEB regions...
 7 Mapping TEB and stack regions...
 8 Mapping heap regions...
 9 Mapping page heap regions...
10 Mapping other regions...
11 Mapping stack trace database regions...
12 Mapping activation context regions...
13 
14 --- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
15 Free                                     57          f852b000 (   3.880 GB)           97.00%
16 Image                                   191           2eb9000 (  46.723 MB)  38.06%    1.14%
17 <unknown>                                88           2670000 (  38.438 MB)  31.31%    0.94%
18 MappedFile                               18           1cec000 (  28.922 MB)  23.56%    0.71%
19 Stack                                    21            700000 (   7.000 MB)   5.70%    0.17%
20 Heap                                     10            13b000 (   1.230 MB)   1.00%    0.03%
21 Other                                     7             5c000 ( 368.000 kB)   0.29%    0.01%
22 TEB                                       7             16000 (  88.000 kB)   0.07%    0.00%
23 PEB                                       1              3000 (  12.000 kB)   0.01%    0.00%
24 
25 --- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
26 MEM_IMAGE                               198           2ec3000 (  46.762 MB)  38.09%    1.14%
27 MEM_PRIVATE                             122           2ebc000 (  46.734 MB)  38.07%    1.14%
28 MEM_MAPPED                               23           1d46000 (  29.273 MB)  23.84%    0.71%
29 
30 --- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
31 MEM_FREE                                 57          f852b000 (   3.880 GB)           97.00%
32 MEM_RESERVE                              65           43a4000 (  67.641 MB)  55.10%    1.65%
33 MEM_COMMIT                              278           3721000 (  55.129 MB)  44.90%    1.35%
34 
35 --- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
36 PAGE_EXECUTE_READ                        32           25b2000 (  37.695 MB)  30.70%    0.92%
37 PAGE_READONLY                            90            bae000 (  11.680 MB)   9.51%    0.29%
38 PAGE_WRITECOPY                           30            399000 (   3.598 MB)   2.93%    0.09%
39 PAGE_READWRITE                          102            1f8000 (   1.969 MB)   1.60%    0.05%
40 PAGE_READWRITE | PAGE_GUARD              16             28000 ( 160.000 kB)   0.13%    0.00%
41 PAGE_EXECUTE_READWRITE                    8              8000 (  32.000 kB)   0.03%    0.00%
42 
43 --- Largest Region by Usage ----------- Base Address -------- Region Size ----------
44 Free                                        80010000          7f2f0000 (   1.987 GB)
45 Image                                       6de80000            f55000 (  15.332 MB)
46 <unknown>                                    2f22000            fee000 (  15.930 MB)
47 MappedFile                                   19d4000           133d000 (  19.238 MB)
48 Stack                                        1410000             fd000 (1012.000 kB)
49 Heap                                          f20000             8b000 ( 556.000 kB)
50 Other                                       ff480000             33000 ( 204.000 kB)
51 TEB                                           ce1000              4000 (  16.000 kB)
52 PEB                                           ccc000              3000 (  12.000 kB)
View Code

            輸出的內容還是不少的,列表中【Heap 10 13b000 ( 1.230 MB) 1.00% 0.03%】,這個就是 NT 堆。
            我們也可以使用【!heap -s】命令,查看 NT 堆的詳情。

 1 0:006> !heap -s
 2 
 3 
 4 ************************************************************************************************************************
 5                                               NT HEAP STATS BELOW
 6 ************************************************************************************************************************
 7 NtGlobalFlag enables following debugging aids for new heaps:
 8     tail checking
 9     free checking
10     validate parameters
11 LFH Key                   : 0x0afd8ea9
12 Termination on corruption : ENABLED
13   Heap     Flags   Reserv  Commit  Virt   Free  List   UCR  Virt  Lock  Fast 
14                     (k)     (k)    (k)     (k) length      blocks cont. heap 
15 -----------------------------------------------------------------------------
16 00f20000 40000062    1020    556   1020      6    65     1    0      4      (第一個堆是進程堆,是 Win32函數用的)
17 01200000 40001062      60     12     60      1     2     1    0      0      
18 01570000 40001062      60     12     60      1     2     1    0      0      
19 02f00000 40001062      60      4     60      0     1     1    0      0      
20 01540000 40041062      60      4     60      2     1     1    0      0      
21 -----------------------------------------------------------------------------

            2)、我們查看一下 GC 堆。
            如果我們想查看 GC 堆,可以直接使用【!eeheap -gc】命令。

 1 0:006> !eeheap -gc
 2 Number of GC Heaps: 1
 3 generation 0 starts at 0x02f11018
 4 generation 1 starts at 0x02f1100c
 5 generation 2 starts at 0x02f11000
 6 ephemeral segment allocation context: none
 7  segment     begin  allocated      size
 8 02f10000  02f11000  02f1871c  0x771c(30492)
 9 Large object heap starts at 0x03f11000
10  segment     begin  allocated      size
11 03f10000  03f11000  03f2a180  0x19180(102784)
12 Total Size:              Size: 0x2089c (133276) bytes.
13 ------------------------------
14 GC Heap Size:    Size: 0x2089c (133276) bytes.

        2.2、我們查看大對象和小對象分配機制
            調試源碼:Example_11_1_1
            當我們成功進入調試器界面,使用【g】命令,繼續運行,我們會在12行代碼【Console.ReadLine()】暫停,我們程式列印出了【Hello world】,我們點擊調試器工具欄中的【break】按鈕,就可以調試程式了。
            byte[] byte1 = new byte[10000];這行數組小於85000就應該在小對象堆中,byte[] byet2 = new byte[85000];這個數組對象大於85000 就會分配在大對象堆中。
            我們先使用【!eeheap -gc】查看一下托管堆的情況。
 1 0:006> !eeheap -gc
 2 Number of GC Heaps: 1
 3 generation 0 starts at 0x02f11018
 4 generation 1 starts at 0x02f1100c
 5 generation 2 starts at 0x02f11000
 6 ephemeral segment allocation context: none
 7  segment     begin  allocated      size
 8 02f10000  02f11000  02f1871c  0x771c(30492)
 9 Large object heap starts at 0x03f11000(這個就是大對象堆)
10  segment     begin  allocated      size
11 03f10000  03f11000  03f2a180  0x19180(102784)
12 Total Size:              Size: 0x2089c (133276) bytes.
13 ------------------------------
14 GC Heap Size:    Size: 0x2089c (133276) bytes.

            我們再使用【!dumpheap 03f11000 03f2a180】查看一下大對象堆的情況。

0:006> !dumpheap 03f11000  03f2a180
 Address       MT     Size
03f11000 00f45368       10 Free
03f11010 00f45368       14 Free
03f11020 6dae2788     4872     
03f12328 00f45368       14 Free
03f12338 6dae2788      524     
03f12548 00f45368       14 Free
03f12558 6dae2788     4092     
03f13558 00f45368       14 Free
03f13568 6dae2788     8172     
03f15558 00f45368       14 Free
03f15568 6dae5c40    85012     

Statistics:
      MT    Count    TotalSize Class Name
00f45368        6           80      Free
6dae2788        4        17660 System.Object[]
6dae5c40        1        85012 System.Byte[]
Total 11 objects

            紅色標註的就是我們的 byte 數組,大小是 85012,為什麼多了12,數組是引用類型,引用類型都有兩個附加欄位(8)和一個數組長度(4)的欄位,共12,再加上數組的長度就是85012。
            我們確認了 byet2 確實是在大對象堆中,我們繼續看看 byet1是不是在小對象堆中。使用相同的命令【!dumpheap 02f11000 02f1871c】,這個地址就是小對象堆的起始地址和結束地址,就是【ephemeral segment allocation】臨時段。

 1 0:006> !dumpheap 02f11000 02f1871c
 2  Address       MT     Size
 3 02f11000 00f45368       10 Free
 4 02f1100c 00f45368       10 Free
 5 .....
 6 02f124fc 00f45368       10 Free
 7 02f12508 6dae5c40    10012     
 8 02f14c24 6dae8b20       28     
 9 .....
10 02f174f4 6db49b0c       16     
11 
12 Statistics:
13       MT    Count    TotalSize Class Name
14 ......
15 6dae5c40        4        10818 System.Byte[]
16 Total 325 objects

            紅色標記的就是 byte 數組,我們使用【!DumpHeap /d -mt 6dae5c40】查看詳情。

 1 0:006> !DumpHeap /d -mt 6dae5c40
 2  Address       MT     Size
 3 02f12508 6dae5c40    10012     
 4 02f16710 6dae5c40      526     
 5 02f171cc 6dae5c40      268     
 6 02f174e8 6dae5c40       12     
 7 03f15568 6dae5c40    85012     
 8 
 9 Statistics:
10       MT    Count    TotalSize Class Name
11 6dae5c40        5        95830 System.Byte[]
12 Total 5 objects

            紅色標記的就是我們的 byte1 byte 數組。當然,我們可以使用命令【!do】查看詳情。

1 0:006> !do 02f12508
2 Name:        System.Byte[]
3 MethodTable: 6dae5c40
4 EEClass:     6dbe8ba8
5 Size:        10012(0x271c) bytes
6 Array:       Rank 1, Number of elements 10000, Type Byte (Print Array)
7 Content:     ................................................................................................................................
8 Fields:
9 None

        2.3、如何按生存期查看對象的分配。
            調試源碼:Example_11_1_1
            當我們成功進入調試器界面,使用【g】命令,繼續運行,我們會在12行代碼【Console.ReadLine()】暫停,我們程式列印出了【Hello world】,我們點擊調試器工具欄中的【break】按鈕,就可以調試程式了。
            其實,有關對象代的調試,我們已經做過了,這裡正式測試一下,我們依然使用【!eeheap -gc】命令,就可以看到托管堆中的代了。
 1 0:006> !eeheap -gc
 2 Number of GC Heaps: 1
 3 generation 0 starts at 0x02f11018(0代)
 4 generation 1 starts at 0x02f1100c(1代)
 5 generation 2 starts at 0x02f11000(2代)
 6 ephemeral segment allocation context: none
 7  segment     begin  allocated      size
 8 02f10000  02f11000  02f1871c  0x771c(30492)
 9 Large object heap starts at 0x03f11000
10  segment     begin  allocated      size
11 03f10000  03f11000  03f2a180  0x19180(102784)
12 Total Size:              Size: 0x2089c (133276) bytes.
13 ------------------------------
14 GC Heap Size:    Size: 0x2089c (133276) bytes.

        2.4、查看 Console GC 模式
            調試源碼:Example_11_1_1
            當我們成功進入調試器界面,使用【g】命令,繼續運行,我們會在12行代碼【Console.ReadLine()】暫停,我們程式列印出了【Hello world】,我們點擊調試器工具欄中的【break】按鈕,就可以調試程式了。
            我們可以使用【!eeversion】命令,查看GC模式。
1 0:006> !eeversion
2 4.8.4300.0 retail
3 Workstation mode(工作站模式)
4 SOS Version: 4.8.4300.0 retail build

            我們也可以通過【!eeheap -gc】命令查看托管堆的個數查看。

 1 0:006> !eeheap -gc
 2 Number of GC Heaps: 1(只有一個托管堆,就是工作站模式)
 3 generation 0 starts at 0x02f11018
 4 generation 1 starts at 0x02f1100c
 5 generation 2 starts at 0x02f11000
 6 ephemeral segment allocation context: none
 7  segment     begin  allocated      size
 8 02f10000  02f11000  02f1871c  0x771c(30492)
 9 Large object heap starts at 0x03f11000
10  segment     begin  allocated      size
11 03f10000  03f11000  03f2a180  0x19180(102784)
12 Total Size:              Size: 0x2089c (133276) bytes.
13 ------------------------------
14 GC Heap Size:    Size: 0x2089c (133276) bytes.
            
        2.5、查看 Asp.Net Web API 的 GC 模式
            調試源碼:Example_11_1_2
            這裡測試的源碼時 Web API 項目,直接運行程式,然後我們通過 Windbg 的【attach to Process】命令來查看。附加進程的進程是【iisexpress】,效果如圖:
            

            如果你使用的是【Debug】運行的 WEBAPI,調試會失敗,附加進程有誤,如圖:
            
            在 Windbg中提示的具體錯誤:"The process that you are attempting to attach to is already being debugged. Only one debugger can be invasively attached to a process at a time. A non-invasive attach is still possible when another debugger is attached." ,意思就是:嘗試附加到的進程已在調試中。一次只能將一個調試器侵入性附加到進程。把程式的運行模式改為【Release】模式,不用使用調試模式,快捷鍵:Ctrl+F5,就可以附加進程成功了。
            我們可以使用【!eeheap -gc】命令查看一下伺服器GC模式。

 1 0:037> !eeheap -gc
 2 Number of GC Heaps: 4(有四個堆,這既是伺服器GC模式)
 3 ------------------------------
 4 Heap 0 (000001ff28a2dc70)
 5 generation 0 starts at 0x000001ff29283e08
 6 generation 1 starts at 0x000001ff29121018
 7 generation 2 starts at 0x000001ff29121000
 8 ephemeral segment allocation context: none
 9          segment             begin         allocated              size
10 000001ff29120000  000001ff29121000  000001ff2a79a728  0x1679728(23566120)
11 Large object heap starts at 0x0000020329121000
12          segment             begin         allocated              size
13 0000020329120000  0000020329121000  000002032941f2c0  0x2fe2c0(3138240)
14 Heap Size:               Size: 0x19779e8 (26704360) bytes.
15 ------------------------------
16 Heap 1 (000001ff28a5dc40)
17 generation 0 starts at 0x000002002923b6d0
18 generation 1 starts at 0x0000020029121018
19 generation 2 starts at 0x0000020029121000
20 ephemeral segment allocation context: none
21          segment             begin         allocated              size
22 0000020029120000  0000020029121000  000002002a755fe8  0x1634fe8(23285736)
23 Large object heap starts at 0x0000020339121000
24          segment             begin         allocated              size
25 0000020339120000  0000020339121000  00000203392c8ff0  0x1a7ff0(1736688)
26 Heap Size:               Size: 0x17dcfd8 (25022424) bytes.
27 ------------------------------
28 Heap 2 (000001ff28a87bf0)
29 generation 0 starts at 0x00000201291ebe00
30 generation 1 starts at 0x0000020129121018
31 generation 2 starts at 0x0000020129121000
32 ephemeral segment allocation context: none
33          segment             begin         allocated              size
34 0000020129120000  0000020129121000  000002012993ffe8  0x81efe8(8515560)
35 Large object heap starts at 0x0000020349121000
36          segment             begin         allocated              size
37 0000020349120000  0000020349121000  0000020349121018  0x18(24)
38 Heap Size:               Size: 0x81f000 (8515584) bytes.
39 ------------------------------
40 Heap 3 (000001ff28ab1ba0)
	   

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

-Advertisement-
Play Games
更多相關文章
  • 1、首先先把配置文件從jar中抽離 示例代碼: <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.2.0</version> <confi ...
  • 來源:https://cloud.tencent.com/developer/article/2115373 最近客戶有個新需求,就是想查看網站的訪問情況,由於網站沒有做google的統計和百度的統計,所以訪問情況,只能通過日誌查看,通過腳本的形式給客戶導出也不太實際,給客戶寫個簡單的頁面,咱也做不 ...
  • 因為WordPress要求最低php版本為7.4,故有此篇。 系統版本:CentOS 7.9 第一步:查看已安裝的php的所有包 yum list installed | grep php 第二步:卸載上述包 yum remove php* 第三步:換下載源為remi yum install epe ...
  • 導語 本文介紹了LSTM的相關內容和在股票價格預測上的應用。 LSTM的股票價格預測 LSTM(Long Short Term Memory)是一種 特殊的RNN類型,同其他的RNNs相比可以更加方便地學習長期依賴關係,因此有很多人試圖將其應用於 時間序列的預測問題 上。 匯豐銀行全球資產管理開發副 ...
  • 實現自定義配置源至少需要添加如下成員: 實現IConfigurationSource介面的配置源; 實現IConfigurationProvider介面或虛基類ConfigurationProvider的配置提供程式; 添加配置源的IConfigurationBuilder擴展方法; 如自定義一個T ...
  • 今天跟大家分享一下最近做的一個程式中繪圖功能的實現。 先來看看實現的效果: 具體實現 頁面的設計 繪圖設置頁面的設計如下所示: 4個label控制項,控制項如下所示: 2個DateEdit控制項,控制項如下所示: 1個ComboBoxEdit控制項,控制項如下所示: 1個CheckedListBoxContro ...
  • 環形緩衝區(Circular Buffer 或 Ring Buffer)是一種數據結構,它在邏輯上形成一個閉環。這種結構非常適用於需要固定大小的緩衝區的情況,如音頻處理、網路通信、實時數據傳輸等。環形緩衝區的主要特點和用途包括: 固定大小:環形緩衝區的大小在創建時確定,並且在其生命周期內保持不變。 ...
  • Span<T> 是 C# 7.2 引入的一個強大的數據結構,用於表示記憶體中的一塊連續數據。它可以用於實現高性能的數組操作,而無需額外的記憶體分配。在本文中,我將詳細介紹如何使用 Span<T> 來實現高性能數組操作,並提供一些示例代碼來說明其用法。 什麼是 Span? Span<T> 是 System ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...