cuốn sách gpt4 ai đã làm

Thảo luận ngắn gọn về các nguyên tắc cơ bản của hệ thống loại .NETCore (TypesSystem)

In lại Tác giả: Sahara Thời gian cập nhật: 24-12-2024 12:47:42 58 4
mua khóa gpt4 Nike

C# type system

C# là một ngôn ngữ được nhập mạnh mẽ. báo phương thức định nghĩa tên, loại và loại (giá trị, tham chiếu hoặc đầu ra) cho từng tham số đầu vào và trả giá trị về. cũng bao gồm các tệp hệ thống, mạng kết nối, bộ sưu tập và đối tượng cũng như tháng. cấu hình điển hình sử dụng các kiểu từ lớp thư viện, cũng như các kiểu do người dùng định nghĩa để mô hình hóa các kiểu khái niệm cụ thể có thể chọn vấn đề miền của chương trình.

Nói một cách đơn giản, nó đề cập đến: các lớp, tham số, trường, thuộc tính, phương thức, mô-đun, tập hợp và các phần tử khác.

nhìn thấy là tin tưởng

//Không có tên (mô-đun) => gõ không gian tên hệ thống Ví dụ_4_1 { //Lớp thuộc về loại lớp hệ thống UserLogin { // Các trường, (string user name, chuỗi mật khẩu) { this.username = tên người dùng;//Parameter=>Nhập hệ thống this.password = mật khẩu } //Phương thức=>Gõ bool công khai hệ thống Đăng nhập(string inputUsername, string inputPassword) { if (inputUsername == tên người dùng && inputPassword == mật khẩu) { return true; } else { return false } } } }

Trình biên dịch sử dụng loại thông tin để đảm bảo rằng tất cả các thao tác được thực hiện trong mã đều an toàn về kiểu, ví dụ:

int a = 5; int b = a + 2; //OK bool test = true; // Toán tử '+' không thể áp dụng cho loại toán 'int' và 'bool'.

IL loại hệ thống

Trình biên dịch nhúng loại thông tin vào chương trình dưới dạng siêu dữ liệu. "siêu dữ liệu" ở cấp IL.

nhìn thấy là tin tưởng

hình ảnh

CLR loại hệ thống

CLR sử dụng siêu dữ liệu trong thời gian chạy để đảm bảo an toàn hơn cho loại và tránh lỗi chuyển đổi loại.

nhìn thấy là tin tưởng

Hãy sử dụng windbg để tìm hiểu.

  1. AppDomain là nền tảng nền tảng như nền tảng .net So với .net framework, chỉ còn lại hai, Miền hệ thống và Miền 1.

  2. Cuộc đối thoại.

  3. Mô-đun.

  4. Lớp Chúng ta có thể sử dụng lệnh phụ để hiển thị các loại (lớp) được xác định trong mô-đun và các loại được xác định module-tham chiếu.

  5. Phương thức cũng hiển thị phương thức của lớp cha objcet.

  6. EEClass trong bảng Phương thức xuất ra trường lấy các trường của lớp.

Loại hệ thống ánh xạ CLR và loại hệ thống C++

Có một câu nói trên Internet rằng # trong C# thực ra là ++++. Tương đương với C++++, siêu bộ của C++. Từ quan điểm của CLR, tất cả các loại trong CLR đều có sự tương ứng một-một trong C++.

https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/appdomain.cpp https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/class.cpp https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/field.cpp.

Loại giá trị và loại tham chiếu

Trong các bài luận phỏng vấn bao gồm phần tám, có một câu hỏi thường được đặt ra: Sự khác biệt giữa loại giá trị và Các loại giá By vì no mô tả hai khái niệm từ góc thực hiện, nó tương đương với việc bắn một mũi tên trước và vẽ mục tiêu. hơn là dựa trên sự khác biệt đặc biệt là vốn có của hai loại.

Định nghĩa thực sự của ECMA335 về hai loại

Loại giá trị: Một loại giá trị này có thể hiển thị chứa tất cả dữ liệu của nó theo cách trực tiếp Giá trị của loại giá. Loại tham chiếu mô tả một giá trị để biết vị trí của các giá trị khác.

loại giá trị tham chiếu kiểu
Trọn đời Chứa tất cả dữ liệu của nó, kín, tự động giải thích. phiên bản chính đó Mô tả vị trí của các giá trị khác Thời gian tồn tại của các giá trị khác không phụ thuộc vào tham số giá trị chính reference.
Khả năng chia sẻ Theo mặc định, ngữ nghĩa "truyền theo giá trị" được sử dụng áp dụng, sao chép byte và giá trị ban đầu không bị ảnh hưởng. Có thể chia sẻ nếu chúng tôi muốn sử dụng nó ở nơi khác. use.
bình đẳng Chúng tôi chỉ được coi là giống nhau nếu chúng giống nhau về phân tích chuỗi giá trị nhị phân. Khi các vị trí được chỉ định bởi chúng giống nhau thì chúng sẽ giống nhau.

Như có thể thấy từ định nghĩa, không có chỗ nào để nói ai được lưu trữ trên ngăn xếp và ai được lưu trữ trên heap. Trong thực tế, các loại giá trị được phân bổ trên ngăn xếp và các loại tham chiếu được phân bổ trên heap. Đó chỉ là quyết định thiết kế được Microsoft đưa ra dựa trên điều kiện thực tế khi thiết kế chuẩn CLI. Vì đây thực sự là một quyết định rất đúng đắn nên Microsoft đã áp dụng quyết định này khi triển khai các CLI khác nhau. Nhưng hãy nhớ rằng đây không phải là viên đạn bạc. Các nền tảng phần cứng khác nhau có thiết kế và kiểu triển khai lưu trữ trên thực tế khác nhau, điều này chủ yếu được phản ánh trong thiết kế của quá trình biên dịch JIT. Trình biên dịch JIT tồn tại trên nền tảng phần cứng x86/x64 vì có ngăn xếp, vùng nhớ heap và thanh ghi. JIT có thể sử dụng nó theo ý muốn. Nó có thể phân bổ các loại giá trị trên heap hoặc trong các thanh ghi. Miễn là nó không vi phạm định nghĩa về loại thì có gì sai?

Bố cục bộ nhớ loại giá trị

Nếu bạn chỉ bắt đầu từ định nghĩa, việc lưu tất cả các loại giá trị vào heap là hoàn toàn khả thi, nhưng sẽ quá thuận tiện khi sử dụng các thanh ghi ngăn xếp hoặc CPU. Vì vậy, hai yếu tố tuổi thọ và chia sẻ chủ yếu được xem xét và đặt nó trong không gian ngăn xếp sẽ thích hợp hơn.

nhìn thấy là tin tưởng

    lớp nội bộ Chương trình { static void Main(string[] args) { var myStruct = new MyStruct(); myStruct.x = 100; myStruct.y = 102 } } struct MyStruct { public int x;

hình ảnh

Có thể thấy rằng bố cục bộ nhớ của loại giá trị không có bất kỳ chi phí bổ sung nào. Nó được phân bổ trực tiếp trong ngăn xếp luồng và được giải phóng cùng với ngăn xếp luồng. Hoàn toàn phù hợp với các khái niệm về tuổi thọ/khả năng chia sẻ.

Bố trí bộ nhớ của các loại tham chiếu

Bởi vì các kiểu tham chiếu có thể chia sẻ dữ liệu nên thời gian tồn tại của chúng không được xác định. Vì vậy, việc suy nghĩ về nơi lưu trữ các loại tham chiếu đơn giản hơn nhiều so với các loại giá trị. Nói chung, theo định nghĩa, việc lưu trữ kiểu tham chiếu trên ngăn xếp không phải là điều hiển nhiên và rõ ràng là nơi lưu trữ kiểu tham chiếu đó.

nhìn thấy là tin tưởng

    lớp nội bộ Chương trình { static void Main(string[] args) { var myClass = new MyClass(); myClass.y = 102; window.Break(); int y;

So sánh nó với tập hợp các loại giá trị, có thể thấy rõ sự khác biệt.

  1. Việc tập hợp các loại giá trị gán trực tiếp 100 và 102 cho phần bù của thanh ghi rbp, từ đó thực hiện việc giải phóng cùng với không gian ngăn xếp.
  2. Tập hợp kiểu tham chiếu trước tiên tạo một không gian bộ nhớ trong vùng nhớ được quản lý, sau đó gán địa chỉ bộ nhớ cho thanh ghi rax, sau đó thực hiện thao tác gán dựa trên độ lệch của địa chỉ bộ nhớ. Sẽ không được phát hành cùng với không gian ngăn xếp

nhìn thấy là tin tưởng

Cũng có thể xác minh bằng cách sử dụng Windbg rằng đối tượng MyClass được phân bổ trong vùng được quản lý và được cấu trúc dưới dạng objHeader + phương thức + chính đối tượng đó.

Các loại giá trị có phải được lưu trữ trong không gian ngăn xếp không?

Ngoài việc được lưu trữ trên "ngăn xếp", các loại giá trị còn có thể được lưu trữ trong "các thanh ghi" và "các vùng được quản lý".

Thấy thì tin: Các loại giá trị có trong sổ đăng ký

    lớp nội bộ Ví dụStruct { public int Main(int i) { var myStruct = new MyStruct(); myStruct.vaule1 = i; return Helper(myStruct); } [MethodImpl(MethodImplOptions.AggressiveInliner)]//Yêu cầu trình biên dịch tạo phương thức linh hoạt nhất có thể nội tuyến Private int Helper(MyStruct arg) { return arg.vaule1; } } struct MyStruct { public int vaule1; public int vaule2; public int vaule4 }

Ở chế độ phát hành 64 bit, vì phương thức Trình trợ giúp được nội tuyến. Do đó, phương thức Trợ giúp sẽ không được gọi nên việc truyền dữ liệu Struct sang phương thức Trợ giúp sẽ bị bỏ qua. Trình biên dịch JIT tối ưu hóa toàn bộ hoạt động để chỉ hoạt động trên các thanh ghi CPU.

Đây có thể là một vấn đề về môi trường. Phiên bản phát hành của tôi không bao giờ được nội tuyến, điều này khiến cho nó không thể sao chép được trong Windbg. Bạn bè quan tâm có thể tham khảo trang 168 của <.NET Memory Management Guide>.

Nhìn thấy là tin tưởng: các loại giá trị trong vùng được quản lý

    lớp nội bộ Ví dụStruct2 { public void Main() { var myStruct = new MyStruct2 { vaule1 = 100, vaule2 = 102 }; //Vì đại biểu là loại tham chiếu nên loại tham chiếu nội bộ đề cập đến một loại giá trị, điều này cũng sẽ thúc đẩy loại giá trị vào vùng nhớ được quản lý var f = () => { Console.WriteLine(t.vaule1); f(); window.Break(); } } struct MyStruct2 { public int vaule2; public int vaule3;

hình ảnh

Có thể thấy rằng khi một kiểu giá trị được giữ bởi một kiểu tham chiếu thì nó cũng sẽ được cấp phát trong vùng heap.

Các loại tham chiếu có nhất thiết phải được lưu trữ trong vùng heap không?

Trước .NET9, tuyên bố này đúng vì không gian ngăn xếp không tuân theo định nghĩa của loại tham chiếu.

Nhưng sau .NET9, khái niệm này đã thay đổi. Đầu tiên chúng ta hãy nghĩ về một đoạn mã.

    lớp công khai Ví dụClass { public void Main() { var myClass = new MyClass() { vaule1 = 100, vaule2 = 102 }; { public int vaule1; public int vaule2; public int vaule3; }

Mặc dù MyClass là một kiểu tham chiếu, nhưng trong phương thức, myClass thực sự không còn được sử dụng khi quá trình thực thi phương thức Main hoàn tất. Do đó, việc đặt myClass vào vùng nhớ heap sẽ gây ra gánh nặng cho GC và lãng phí bộ nhớ. JIT có thể được làm thông minh hơn không? Nếu vòng đời của loại tham chiếu tương tự như vòng đời của ngăn xếp luồng, thì nó có thể được đặt trong không gian ngăn xếp không?

Câu trả lời là có, và nó đã được sử dụng trong JAVA trong nhiều năm. Đây là phân tích thoát nổi tiếng.

Phân tích thoát .NET9 (phân tích thoát)

.NET9 mới kích hoạt tính năng này nên phạm vi có phần hạn chế. Tuy nhiên, trong tương lai, tôi tin rằng phạm vi sẽ được mở rộng hơn nữa để đạt được mức phân bổ bộ nhớ hiệu suất cao hơn.

Phân bổ bộ nhớ .NET 9: Phân bổ bộ nhớ .NET 8

Hãy trích dẫn một ví dụ chính thức khác https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-9/#object-stack-allocation.

// dotnet run -c Release -f net8.0 --filter "*" --runtimes net8.0 net9.0 sử dụng BenchmarkDotNet.Attribution; sử dụng BenchmarkDotNet.Running; (args); [Trình chẩn đoán bộ nhớ(false)] [Trình chẩn đoán tháo gỡ] [HideColumns("Job", "Error", "StdDev", "Median", "RatioSD")] Kiểm tra lớp công khai { [Điểm chuẩn] public int GetValue() => new MyObj(42).Value riêng tư MyObj { public MyObj(int value) => Value = value; public int Value { get;

Việc lắp ráp trong .net 8 như sau

; Tests.GetValue() đẩy rax mov rdi,offset MT_Tests+MyObj gọi CORINFO_HELP_NEWSFAST mov dword ptr [rax+8],2A mov eax,[rax+8] add rsp,8 ret ;

Phân bổ bộ nhớ trong .net 8 như sau.

Quá trình biên dịch trong .net 9 như sau.

; Tests.GetValue() mov eax,2A ret ;

Phân bổ bộ nhớ trong .net 9 như sau.

Có thể thấy rằng .NET9 trực tiếp thúc đẩy new MyObj(42).Value trả về 42 thông qua phương thức nội tuyến. Đối tượng MyObj sẽ không được tạo trong heap mà sẽ được gán trực tiếp trong không gian ngăn xếp.

Tóm tắt

Như bạn có thể thấy từ ví dụ trên, cốt lõi của loại giá trị và loại tham chiếu là liệu vòng đời của chúng có thể kiểm soát được hay không và liệu chúng có được chia sẻ bởi các luồng khác không? Bất kể loại nào, miễn là tuổi thọ của nó lớn hơn ngăn xếp luồng hoặc được chia sẻ bởi các luồng khác. Sau đó nó sẽ được phân bổ vào heap. Nếu không, nó sẽ được phân bổ vào ngăn xếp hoặc thanh ghi. Nói một cách đơn giản hơn, nếu JIT không biết khi nào đối tượng được giải phóng thì chắc chắn nó sẽ được cấp phát vào không gian heap. Nếu nó biết khi nào nó được giải phóng, nó sẽ được phân bổ vào không gian ngăn xếp hoặc thậm chí vào một thanh ghi càng nhiều càng tốt.

Cuối cùng, bài viết này về các nguyên tắc cơ bản của hệ thống loại .NETCore (TypesSystem) kết thúc tại đây. Nếu bạn muốn biết thêm về các nguyên tắc cơ bản của hệ thống loại .NETCore (TypesSystem), vui lòng tìm kiếm các bài viết CFSDN hoặc tiếp tục Duyệt qua các bài viết liên quan, tôi hy vọng vậy. bạn sẽ ủng hộ blog của tôi trong tương lai! .

58 4 0
Chứng chỉ ICP Bắc Kinh số 000000
Hợp tác quảng cáo: 1813099741@qq.com 6ren.com
Xem sitemap của VNExpress