Net 高級調試之五:如何在托管函數上設置斷點

来源:https://www.cnblogs.com/PatrickLiu/archive/2023/11/03/17804823.html
-Advertisement-
Play Games

最近重構並精簡了Dapper.Lite,然後把不依賴Dapper的版本LiteSql也重構了一下,和Dapper.Lite保持一致。感覺這兩款ORM基本完工,自薦一下。 .NET的ORM雖多,堪用的不多,何為堪用,EF是官方的,質量高,堪用。Dapper用戶量大,現在BUG基本改的差不多了,也基本不 ...


一、簡介
    今天是《Net 高級調試》的第五篇文章。今天這篇文章開始介紹如何在托管方法和非托管方法設置斷點,我們要想調試程式,必須掌握調試的一些命令,動態調試的命令,我們在上一篇文章已經講過了。光有命令也是不行的,要讓這些調試命令有用,必須可以在方法上設置斷點,然後,再使用調試命令,才能完成我們的調試任務。當然了,第一次看視頻或者看書,是很迷糊的,不知道如何操作,還是那句老話,一遍不行,那就再來一遍,還不行,那就再來一遍,俗話說的好,書讀千遍,其意自現,我這是第三遍。
     如果在沒有說明的情況下,所有代碼的測試環境都是 Net Framewok 4.8,但是,有時候為了查看源碼,可能需要使用 Net Core 的項目,我會在項目章節里進行說明。好了,廢話不多說,開始我們今天的調試工作。
    調試環境我需要進行說明,以防大家不清楚,具體情況我已經羅列出來。
          操作系統:Windows Professional 10
          調試工具:Windbg Preview(可以去Microsoft Store 去下載)
          開發工具:Visual Studio 2022
          Net 版本:Net Framework 4.8
          CoreCLR源碼:源碼下載

二、基礎知識
    
    1、非托管函數下斷點
        其實對非托管函數下斷點是十分方便的,因為C/C++函數在編譯之後就成了【機器代碼】了,函數名就進入了【符號表】,比如我們可以非常方便的 notepad 的 SaveFile 函數下斷點。

        操作步驟如下:
            a、打開 notepad 。
            b、使用  x notepad!SaveFile 下斷點。
            c、在 notepad 上保存一下文件,就會觸發斷點。
        
    2、托管函數下斷點
        
        2.1、簡介
            托管函數下斷點是很難的,因為你要下斷點的方法的機器碼在記憶體中可能還沒有生成,也就是 JIT 從來就沒有對該方法進行編譯過,所以在還沒有生成的代碼上下斷點就比較麻煩。雖然比較麻煩,並不代表不能實現,我們還是有三種方法可以對托管函數下斷點的。

        2.2、托管函數下斷點的三種方式

            1)、在編譯後的函數上下斷點
                這是最簡單的一種方式,既然方法已經編譯完成,肯定就已經生成了機器代碼,那在編譯後的函數上下斷點就容易很多了,和非托管的是一樣的。

            2)、在未編譯的函數上下斷點
                a、使用 !bpmd assembly.exe(模塊包含尾碼名) namespace.ClassName.MethodName
                b、使用 sosex 擴展的 mbm 下斷點(只能在 Net framework 下使用,Net Core 是不支持的)

            3)、對泛型方法下斷點
                如果我們想對泛型類型的方法下斷點,最首要的任務就是找到泛型類型的名稱和方法的名稱,找到之後,我們就可以下斷點了。找泛型類型的名稱和方法的名稱有兩種辦法,第一種是通過命令,第二種是我們可以使用 ILSpy 找到。


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

    1、測試源碼        
        1.1、Example_5_1_1
 1 namespace Example_5_1_1
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             var sum = Sum(10, 20);
 8 
 9             Debugger.Break();
10 
11             sum = Sum(100, 200);
12 
13             Console.WriteLine($"sum={sum}");
14         }
15 
16         private static int Sum(int a, int b)
17         {
18             var sum = a + b;
19 
20             return sum;
21         }
22     }
23 }
View Code      
        1.2、Example_5_1_2
 1 namespace Example_5_1_2
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Console.WriteLine("請查看:未編譯形態的 Sum 方法");
 8             Debugger.Break();
 9 
10             var sum = Sum(10,20);
11             Console.WriteLine("請查看:已經編譯形態的 Sum 方法");
12             Debugger.Break();
13 
14             sum = Sum(100, 200);
15 
16             Console.WriteLine($"sum={sum}");
17         }
18 
19         private static int Sum(int a, int b)
20         {
21             var sum = a + b;
22 
23             return sum;
24         }
25     }
26 }
View Code
        1.3、Example_5_1_3
 1 namespace Example_5_1_3
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Debugger.Break();
 8 
 9             var mylist = new MyList<int>();
10 
11             mylist.Add(10);
12 
13             Console.ReadLine();
14         }
15     }
16 
17     public class MyList<T>
18     {
19         public T[] arr = new T[10];
20 
21         public void Add(T t)
22         {
23             arr[0] = t;
24         }
25     }
26 }
View Code

    2、眼見為實
        
        2.1、在非托管函數 Notepad 的 SaveFile 方法上下斷點。
            測試程式:Notepad
            操作流程:我們先要打開 notepad,然後再打開 windbg,點擊【文件】菜單,然後通過【attach to process】附加進程,最後點擊【Attach】按鈕完成附加進程的操作。現在 notepad 是不能操作的,因為斷點斷住了,所以我們執行【g】命令,運行一下。然後我們點擊工具欄【break】按鈕,中斷,然後就可以調試了。
            我們使用【x】命令查找一下 notepad 的 SaveFile 方法。
1 0:002> x notepad!*SaveFile*
2 00007ff6`e46be780 notepad!SaveFile (bool __cdecl SaveFile(struct HWND__ *,class wil::unique_any_t<class wil::details::unique_storage<struct wil::details::resource_policy<unsigned short *,void (__cdecl*)(void *),&void __cdecl CoTaskMemFree(void *),struct wistd::integral_constant<unsigned __int64,0>,unsigned short *,unsigned short *,0,std::nullptr_t> > > &,bool,unsigned short const *))
3 00007ff6`e46d508c notepad!_imp_load_GetSaveFileNameW (__imp_load_GetSaveFileNameW)
4 00007ff6`e46e50b0 notepad!_imp_GetSaveFileNameW = <no type information>

            使用【bp】命令對 notepad!SaveFile 函數下斷點。下斷點後,繼續運行,使用【g】命令。

1 0:002> bp notepad!SaveFile
2 0:002> g

            此時,我們可以操作 notepad,隨便輸入一些內容,然後點擊【保存】,就可以被斷點斷住。

1 .......
2 Breakpoint 0 hit
3 notepad!SaveFile:
4 00007ff6`e46be780 488bc4          mov     rax,rsp

            我們可以使用【k】命令顯示調用棧也能說明問題。

 1 0:000> k
 2  # Child-SP          RetAddr               Call Site
 3 00 00000002`1a1dec58 00007ff6`e46b9336     notepad!SaveFile
 4 01 00000002`1a1dec60 00007ff6`e46badf4     notepad!NPCommand+0x2d2
 5 02 00000002`1a1df240 00007ff8`23e0e338     notepad!NPWndProc+0x844
 6 03 00000002`1a1df570 00007ff8`23e0dd79     USER32!UserCallWinProcCheckWow+0x2f8
 7 04 00000002`1a1df700 00007ff6`e46bb30c     USER32!DispatchMessageWorker+0x249
 8 05 00000002`1a1df780 00007ff6`e46d3b66     notepad!wWinMain+0x29c
 9 06 00000002`1a1df830 00007ff8`23b86fd4     notepad!__scrt_common_main_seh+0x106
10 07 00000002`1a1df870 00007ff8`25bbcec1     KERNEL32!BaseThreadInitThunk+0x14
11 08 00000002`1a1df8a0 00000000`00000000     ntdll!RtlUserThreadStart+0x21

            截圖效果:
              



        2.2、在已經編譯的托管函數上下斷點。
            測試程式:Example_5_1_1
            我們使用 Windbg Preview 調試器,通過【launch executable】菜單載入【Example_5_1_1.exe】項目,通過【g】命令,運行程式,調試器運行代【Debugger.Break()】次會暫停執行。當然,我們可以使用【cls】命令清理一下調試器顯示的過多信息,自己來決定,我是會清理的。我們將在【sum = Sum(100, 200);】這行下斷點。
            當調試器暫停的時候,說明有一部分代碼已經執行了。【var sum = Sum(10, 20);】就是這行代碼已經被執行,也可以說是被 JIT 編譯了。我們如何查看 Sum 方法被編譯的代碼呢?可以使用【!name2ee】命令。
1 0:000> !name2ee Example_5_1_1!Example_5_1_1.Program.Sum
2 Module:      00fc4044
3 Assembly:    Example_5_1_1.exe
4 Token:       06000002
5 MethodDesc:  00fc4d64
6 Name:        Example_5_1_1.Program.Sum(Int32, Int32)
7 JITTED Code Address: 01010908

            紅色標註表示代碼已經編譯,我們也可以使用【!u】命令查看這個代碼的彙編代碼,彙編代碼很多,所以摺疊。

 1 0:000> !u 01010908
 2 Normal JIT generated code
 3 Example_5_1_1.Program.Sum(Int32, Int32)
 4 Begin 01010908, size 3e
 5 
 6 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_5_1_1\Program.cs @ 20:
 7 >>> 01010908 55              push    ebp
 8 01010909 8bec            mov     ebp,esp
 9 0101090b 83ec10          sub     esp,10h
10 0101090e 894dfc          mov     dword ptr [ebp-4],ecx
11 01010911 8955f8          mov     dword ptr [ebp-8],edx
12 01010914 833df042fc0000  cmp     dword ptr ds:[0FC42F0h],0
13 0101091b 7405            je      01010922
14 0101091d e85ef40670      call    clr!JIT_DbgIsJustMyCode (7107fd80)
15 01010922 33d2            xor     edx,edx
16 01010924 8955f0          mov     dword ptr [ebp-10h],edx
17 01010927 33d2            xor     edx,edx
18 01010929 8955f4          mov     dword ptr [ebp-0Ch],edx
19 0101092c 90              nop
20 
21 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_5_1_1\Program.cs @ 21:
22 0101092d 8b45fc          mov     eax,dword ptr [ebp-4]
23 01010930 0345f8          add     eax,dword ptr [ebp-8]
24 01010933 8945f4          mov     dword ptr [ebp-0Ch],eax
25 
26 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_5_1_1\Program.cs @ 23:
27 01010936 8b45f4          mov     eax,dword ptr [ebp-0Ch]
28 01010939 8945f0          mov     dword ptr [ebp-10h],eax
29 0101093c 90              nop
30 0101093d eb00            jmp     0101093f
31 
32 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_5_1_1\Program.cs @ 24:
33 0101093f 8b45f0          mov     eax,dword ptr [ebp-10h]
34 01010942 8be5            mov     esp,ebp
35 01010944 5d              pop     ebp
36 01010945 c3              ret
View Code

            

            我們使用【bp】命令在地址【01010908】地址處下斷點,【g】繼續運行,就會在 Sum 方法的第一行斷住。

0:000> bp 01010908

            繼續運行後,            

1 0:000> g
2 Breakpoint 0 hit
3 eax=00000000 ebx=00aff108 ecx=00000064 edx=000000c8 esi=02b524bc edi=00aff058
4 eip=01010908 esp=00aff03c ebp=00aff068 iopl=0         nv up ei pl zr na pe nc
5 cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
6 Example_5_1_1!COM+_Entry_Point <PERF> (Example_5_1_1+0x920908):
7 01010908 55              push    ebp

            效果截圖:
              


            
        2.3、在未編譯的托管函數上下斷點。
            測試程式:Example_5_1_2
            我們使用 Windbg Preview 調試器,通過【launch executable】菜單載入【Example_5_1_2.exe】項目,通過【g】命令,運行程式,調試器運行代【Debugger.Break()】次會暫停執行,此行代碼在12。當然,我們可以使用【cls】命令清理一下調試器顯示的過多信息,自己來決定,我是會清理的。這次我們的任務依然是在 Sum 方法上下斷點。
            先來一個截圖吧,效果更明顯:
            
              我們還是先使用【!name2ee】命令查找一下 Sum 方法。
1 0:000> !name2ee Example_5_1_2!Example_5_1_2.Program.Sum
2 Module:      00f34044
3 Assembly:    Example_5_1_2.exe
4 Token:       06000002
5 MethodDesc:  00f34d64
6 Name:        Example_5_1_2.Program.Sum(Int32, Int32)
7 Not JITTED yet. Use !bpmd -md 00f34d64 to break on run.

              紅色標註說明,我們的代碼還沒有被JIT編譯。我們聽從他的建議,使用【!bpmd -md】下斷點。

1 0:000> !bpmd -md 00f34d64
2 MethodDesc = 00f34d64
3 Adding pending breakpoints...

              我們可以使用【g】命令,繼續運行,果然在斷點處暫停。我這裡是可以的,但是視頻說是不可以的。

 1 0:000> g
 2 (bb0.3798): CLR notification exception - code e0444143 (first chance)
 3 JITTED Example_5_1_2!Example_5_1_2.Program.Sum(Int32, Int32)
 4 Setting breakpoint: bp 00F8094C [Example_5_1_2.Program.Sum(Int32, Int32)]
 5 Breakpoint: JIT notification received for method Example_5_1_2.Program.Sum(Int32, Int32) in AppDomain 0100da40.
 6 Breakpoint 0 hit
 7 eax=00f80928 ebx=009ef098 ecx=0000000a edx=00000000 esi=02c224bc edi=009eefe8
 8 eip=00f8094c esp=009eefb8 ebp=009eefc8 iopl=0         nv up ei pl zr na pe nc
 9 cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
10 Example_5_1_2!COM+_Entry_Point <PERF> (Example_5_1_2+0x73094c):
11 00f8094c 90              nop

             截圖效果:
              


            1)、使用 !bpmd moduleName namespace.ClassName.MethodName
                這個實現的原理是藉助 JIT 的編譯完成通知事件,在事件中判斷當前編譯的是否是我們下斷點的方法,如果是就轉成 bp 命令下斷點,從輸出的信息也可以看得出來。
                【!bpmd】命令格式一下兩種都對:!bpmd Example_5_1_2.exe Example_5_1_2.Program.Sum 或者 !bpmd Example_5_1_2 Example_5_1_2.Program.Sum
1 0:000> !bpmd Example_5_1_2 Example_5_1_2.Program.Sum
2 Found 1 methods in module 011a4044...
3 MethodDesc = 011a4d64
4 Adding pending breakpoints...

                斷點設置成功,【g】繼續運行,成功在斷點處暫停。

 1 0:000> g
 2 (14c4.1b60): CLR notification exception - code e0444143 (first chance)(表示 JIT 已經編譯好了,CLR 給 Windbg 發出一個異常通知)
 3 JITTED Example_5_1_2!Example_5_1_2.Program.Sum(Int32, Int32)
 4 Setting breakpoint: bp 02DD094C [Example_5_1_2.Program.Sum(Int32, Int32)](Windbg 拿到編譯後的機器碼地址,重新設置斷點)
 5 Breakpoint: JIT notification received for method Example_5_1_2.Program.Sum(Int32, Int32) in AppDomain 011f0fe8.
 6 Breakpoint 0 hit
 7 eax=02dd0928 ebx=00efedd8 ecx=0000000a edx=00000000 esi=02f124bc edi=00efed28
 8 eip=02dd094c esp=00efecf8 ebp=00efed08 iopl=0         nv up ei pl zr na pe nc
 9 cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
10 Example_5_1_2!COM+_Entry_Point <PERF> (Example_5_1_2+0x22d094c):
11 02dd094c 90              nop

                截圖效果:
                


            2)、使用 sosex 擴展的 mbm 下斷點(只能在 Net framework 下使用,Net Core 是不支持的,必須使用.load命令載入 SOSEX.dll)
                mbm 是非托管命令 bm 的托管版本,對方法名下斷點,還支持模糊匹配。
                
1 0:000> !mbm Example_5_1_2!Example_5_1_2.Program.Sum

                設置斷點後,我們可以繼續運行,執行【g】命令。

1 0:000> g
2 Breakpoint: JIT notification received for method Example_5_1_2.Program.Sum(Int32, Int32) in AppDomain 00a61010.
3 Breakpoint set at Example_5_1_2.Program.Sum(Int32, Int32) in AppDomain 00a61010.
4 Breakpoint 5 hit
5 eax=00a20928 ebx=008ff050 ecx=0000000a edx=00000000 esi=029d24bc edi=008fef98
6 eip=00a2094c esp=008fef68 ebp=008fef78 iopl=0         nv up ei pl zr na pe nc
7 cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
8 Example_5_1_2!COM+_Entry_Point <PERF> (Example_5_1_2+0x5a094c):
9 00a2094c 90              nop

                成功斷住,效果如圖:

                
                【mbm】命令很強大,也可以支持模糊查找。這個操作是另外一個過程,需要重新運行調試程式。
1 0:000> !mbm Example_5_1_2!*Sum

                成功在斷點出暫停。

1 0:000> g
2 Breakpoint: JIT notification received for method Example_5_1_2.Program.Sum(Int32, Int32) in AppDomain 01720fb8.
3 Breakpoint set at Example_5_1_2.Program.Sum(Int32, Int32) in AppDomain 01720fb8.
4 Breakpoint 1 hit
5 eax=031c0928 ebx=0137f378 ecx=0000000a edx=00000000 esi=033d24bc edi=0137f2c8
6 eip=031c094c esp=0137f298 ebp=0137f2a8 iopl=0         nv up ei pl zr na pe nc
7 cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
8 Example_5_1_2!COM+_Entry_Point <PERF> (Example_5_1_2+0x21e094c):
9 031c094c 90              nop

              
        2.4、在泛型方法上下斷點。
            測試程式:Example_5_1_3
            我們使用 Windbg Preview 調試器,通過【launch executable】菜單載入【Example_5_1_3.exe】項目,通過【g】命令,運行程式,調試器運行代【Debugger.Break();】次會暫停執行。當然,我們可以使用【cls】命令清理一下調試器顯示的過多信息,自己來決定,我是會清理的。
            這次的任務是,我們要在泛型類型 MyList<T> 的 Add() 方法上下斷點。
            我們想要在泛型類型的方法上下斷點,首要的任務是找到泛型類型的名稱和方法的名稱,這是關鍵。
            效果如圖:
            

            a、我們通過 Windbg 和 SOS 的命令找到類型的名稱。
                編譯程式集後,泛型類型一定在這個程式集的模塊中。然後我們再在這個模塊中列印出所有的類型,就可以找到這個類型了。
                我們現在這個程式集中查找模塊信息,我們可以使用【!dumpdomain】命令。
 1 0:000> !dumpdomain
 2 --------------------------------------
 3 System Domain:      7141caf8
 4 ....
 5 --------------------------------------
 6 Shared Domain:      7141c7a8
 7 ......
 8 
 9 --------------------------------------
10 Domain 1:           00a2da30
11 ......
12 
13 Assembly:           00a87ea8 [E:\Visual Studio 2022\Source\Projects\......\Example_5_1_3\bin\Debug\Example_5_1_3.exe]
14 ClassLoader:        00a873a8
15 SecurityDescriptor: 00a872a0
16   Module Name
17 01004044(模塊地址)    E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_5_1_3\bin\Debug\Example_5_1_3.exe

              我們找到了模塊,就可以將模塊中所有的類型輸出出來,可以使用【!dumpmodule -mt 】命令。

 1 0:000> !dumpmodule -mt 01004044 
 2 Name:       E:\Visual Studio 2022\Source\Projects\...\Example_5_1_3\bin\Debug\Example_5_1_3.exe
 3 Attributes: PEFile 
 4 Assembly:   00a87ea8
 5 LoaderHeap:              00000000
 6 TypeDefToMethodTableMap: 01000038
 7 TypeRefToMethodTableMap: 01000048
 8 MethodDefToDescMap:      01000094
 9 FieldDefToDescMap:       010000a8
10 MemberRefToDescMap:      00000000
11 FileReferencesMap:       010000b8
12 AssemblyReferencesMap:   010000bc
13 MetaData start address:  006220a8 (1680 bytes)
14 
15 Types defined in this module
16 
17       MT  TypeDef Name
18 ------------------------------------------------------------------------------
19 01004d6c 0x02000002 Example_5_1_3.Program
20 01004de8 0x02000003 Example_5_1_3.MyList`1
21 
22 Types referenced in this module
23 
24       MT    TypeRef Name
25 ------------------------------------------------------------------------------
26 6f802734 0x02000010 System.Object
27 6f847540 0x02000011 System.Diagnostics.Debugger
28 6f808af0 0x02000012 System.Console

              紅色標註的就是我們要查找泛型類型真實的名稱。有了類型,我們繼續可以使用【!dumpmt -md ...】命令,輸出它所有方法。

 1 0:000> !dumpmt -md 01004de8
 2 EEClass:         01001334
 3 Module:          01004044
 4 Name:            Example_5_1_3.MyList`1
 5 mdToken:         02000003
 6 File:            E:\Visual Studio 2022\Source\Projects\......\Example_5_1_3\bin\Debug\Example_5_1_3.exe
 7 BaseSize:        0xc
 8 ComponentSize:   0x0
 9 Slots in VTable: 6
10 Number of IFaces in IFaceMap: 0
11 --------------------------------------
12 MethodDesc Table
13    Entry MethodDe    JIT Name
14 6fbf97b8 6f7fc838 PreJIT System.Object.ToString()
15 6fbf96a0 6f938978 PreJIT System.Object.Equals(System.Object)
16 6fc021f0 6f938998 PreJIT System.Object.GetHashCode()
17 6fbb4f2c 6f9389a0 PreJIT System.Object.Finalize()
18 02840458 01004dd4   NONE Example_5_1_3.MyList`1..ctor()
19 02840450 01004dcc   NONE Example_5_1_3.MyList`1.Add(!0)

              紅色標記就是我們要查找的 Add 方法,有了方法的地址,我們就可以使用【bpmd】命令為其下斷點了。

1 0:000> !bpmd Example_5_1_3 Example_5_1_3.MyList`1.Add
2 Found 1 methods in module 01004044...
3 MethodDesc = 01004dcc
4 Adding pending breakpoints...

              斷點設置成功後,我們使用【g】命令,程式繼續運行,就可以在斷點處暫停。

 1 0:000> g
 2 (3ab4.2920): CLR notification exception - code e0444143 (first chance)
 3 JITTED Example_5_1_3!Example_5_1_3.MyList`1[[System.Int32, mscorlib]].Add(Int32)
 4 Setting breakpoint: bp 02840942 [Example_5_1_3.MyList`1[[System.Int32, mscorlib]].Add(Int32)]
 5 Breakpoint: JIT notification received for method Example_5_1_3.MyList`1[[System.Int32, mscorlib]].Add(Int32) in AppDomain 00a2da30.
 6 Breakpoint 0 hit
 7 eax=02840928 ebx=007bee20 ecx=

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

-Advertisement-
Play Games
更多相關文章
  • HashMap簡介 HashMap是Java語言中的一種集合類,它實現了Map介面,用於存儲Key-Value對。它基於哈希表數據結構,通過計算Key的哈希值來快速定位Value的位置,從而實現高效的插入、刪除和查找操作。下麵我們對照著JAVA1.8中的HashMap源碼來分析一下它的內部實現邏輯 ...
  • 在Go編程語言中處理數據時,經常會遇到數組和切片。這兩者是不同的數據結構,有各自的特性和用途。本文將對Go中的數組和切片進行比較,以幫助大家更好地理解它們。 1. 長度不同 一個主要的區別是長度。在Go中,數組是具有固定長度的數據結構,一旦創建,其大小不可更改。相比之下,切片具有動態大小,可以在運行 ...
  • 集合類不安全 List不安全 單線程情況下集合類和很多其他的類都是安全的,因為同一時間只有一個線程在對他們進行修改,但是如果是多線程情況下,那麼集合類就不一定是安全的,可能會出現一條線程正在修改的同時另一條線程啟動來對這個集合進行修改,這種情況下就會導致發生併發修改異常(在jdk11的環境下多次測試 ...
  • Spring Boot 是一種廣泛使用且非常流行的企業級高性能框架。以下是一些最佳實踐和一些技巧,我們可以使用它們來改進 Spring Boot 應用程式並使其更加高效。這篇文章會有點長,完整讀完文章需要一些時間。 1.正確的包目錄風格 正確的包目錄將有助於輕鬆理解代碼和應用程式的流程。 我們可以使 ...
  • 傅里葉變換是一種數學變換,它可以將一個函數或信號轉換為另一個函數或信號,它可以將時域信號轉換為頻域信號,也可以將頻域信號轉換為時域信號。在很多的領域都有廣泛的應用,例如信號處理、通信、圖像處理、電腦科學、物理學、生物學等。 它最大的功能是能夠分析和提取信號的特征,將複雜的信號分解為簡單的信號。有人 ...
  • 一、前言 在現代軟體開發中,事務處理是必不可少的一部分。當多個操作需要作為一個整體來執行時,事務可以確保數據的完整性和一致性,並避免出現異常和錯誤情況。在SpringBoot框架中,我們可以使用聲明式事務和編程式事務來管理事務處理。其中事務的坑也是不少,比較常見的就是事務失效,大家可以看看!後面小編 ...
  • 一個數據包在HTP的旅游中的暢游之旅,帶你瞭解HTTPS/HTTP2,及反向代理處理,經歷壓縮限流等奇特的歷險記。 ...
  • PDF 文件可以包含文本、圖片及各種媒體元素,但如果文件太大則會影響傳輸效果同時也會占用過多磁碟空間。通過壓縮PDF文件,能夠有效減小文件大小,從而提高傳輸效率並節省存儲空間。想要通過C#代碼快速有效地壓縮 PDF 文件,下麵是實現思路: 在 C# 中壓縮 PDF 1、在VS中使用 NuGet包管理 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...