CFSDN nhấn mạnh vào việc tạo ra giá trị thông qua mã nguồn mở. Chúng tôi cam kết xây dựng một nền tảng chia sẻ tài nguyên để mọi người làm CNTT có thể tìm thấy thế giới tuyệt vời của riêng mình tại đây.
Bài đăng trên blog CFSDN về C# Assembly.Unload này được biên soạn bởi tác giả. Nếu bạn quan tâm đến bài viết này, vui lòng thích nó.
Trình quản lý đơn vị sản phẩm CLR (Unit Manager) Jason Zander đã giải thích trong một bài viết cách đây vài ngày Tại sao không có phương thức Assembly.Unload? tại sao hiện tại không có phương thức Assembly.Unload trong CLR triển khai chức năng tương tự như hàm UnloadLibrary trong Win32 API. Ông tin rằng lý do triển khai chức năng Assembly.Unload chủ yếu là để giải phóng không gian và cập nhật phiên bản. Phương pháp đầu tiên sẽ thu hồi các tài nguyên mà Assembly chiếm dụng sau khi sử dụng, trong khi phương pháp thứ hai sẽ dỡ bỏ phiên bản hiện tại và tải phiên bản đã cập nhật. Ví dụ, bản cập nhật động của chương trình Assembly được trang trong ASP.NET sử dụng là một ví dụ sử dụng hay. Nhưng nếu cung cấp hàm Assembly.Unload thì sẽ phát sinh một số vấn đề:
1. Để đảm bảo rằng các địa chỉ mã được tham chiếu bởi mã trong gói CLR là hợp lệ, các ứng dụng đặc biệt như đối tượng GC và COM CCW phải được theo dõi. Nếu không, sau khi Assembly được dỡ tải, vẫn có thể có các đối tượng CLR hoặc thành phần COM sử dụng mã hoặc địa chỉ dữ liệu của Assembly này, điều này sẽ gây ra ngoại lệ truy cập. Để tránh loại theo dõi lỗi này, hiện tại nó được thực hiện ở cấp AppDomain. Nếu hỗ trợ Assembly.Unload được thêm vào, mức độ chi tiết của việc theo dõi phải được giảm xuống cấp Assembly. Mặc dù điều này khả thi về mặt kỹ thuật, nhưng chi phí lại quá cao.
2. Nếu Assembly.Unload được hỗ trợ, bạn phải theo dõi các xử lý được sử dụng bởi mỗi mã Assembly và các tham chiếu đến mã được quản lý hiện có. Ví dụ, khi JITer biên dịch một phương thức, mã được tạo ra đều nằm trong một vùng thống nhất. Nếu bạn muốn hỗ trợ việc dỡ Assembly, bạn phải biên dịch từng Assembly một cách độc lập. Ngoài ra, còn có một số vấn đề sử dụng tài nguyên tương tự. Mặc dù về mặt kỹ thuật có thể tách biệt việc theo dõi, nhưng chi phí lại cao, đặc biệt là trên các hệ thống có tài nguyên hạn chế như WinCE.
3. CLR hỗ trợ tối ưu hóa tải Assembly giữa nhiều AppDomain, tức là tối ưu hóa trung lập miền, để nhiều AppDomain có thể chia sẻ một mã và tăng tốc độ tải. Hiện tại, v1.0 và v1.1 không thể xử lý việc dỡ mã loại trung lập miền. Điều này cũng làm cho việc triển khai toàn bộ ngữ nghĩa của Assembly.Unload trở nên khó khăn.
Dựa trên những vấn đề trên, Jason Zander khuyên bạn nên sử dụng các phương pháp thiết kế khác để tránh sử dụng tính năng này. Ví dụ, AppDomain và Shadow Copy, được Junfeng Zhang giới thiệu trên blog của mình, là cách ASP.NET giải quyết các vấn đề tương tự.
Khi xây dựng AppDomain, hãy bật chính sách ShadowCopy bằng cách đặt AppDomainSetup.ShadowCopyFiles thành "true" trong tham số AppDomainSetup của phương thức AppDomainSetup.CreateDomain; sau đó đặt AppDomainSetup.ShadowCopyDirectories thành thư mục đích sao chép; đặt AppDomainSetup.CachePath + AppDomainSetup.ApplicationName để chỉ định đường dẫn bộ đệm và tên tệp. Bằng cách này, ngữ nghĩa của Assembly.Unload có thể được mô phỏng. Việc triển khai là tải Assembly cần được quản lý vào một AppDomain được thiết lập động, sau đó gọi các hàm của nó thông qua một proxy trong suốt trên các AppDomain, sử dụng AppDomain.Unload để mô phỏng ngữ nghĩa của Assembly.Unload. chornbe cung cấp một lớp bao bọc đơn giản, mã cụ thể nằm ở cuối bài viết.
Mặc dù về cơ bản có thể mô phỏng được về mặt ngữ nghĩa nhưng vẫn có nhiều vấn đề và chi phí:
1. Hiệu suất: Trong CLR, AppDomain là một khái niệm logic tương tự như quy trình hệ điều hành. Giao tiếp giữa các AppDomain phải tuân theo nhiều hạn chế, giống như giao tiếp giữa các quy trình trước đó. Mặc dù các đối tượng proxy trong suốt có thể được sử dụng để triển khai các chức năng tương tự như các cuộc gọi đối tượng COM xử lý chéo và tự động hoàn tất các hoạt động sắp xếp tham số, nhưng chúng phải trả giá khá đắt. Trong ví dụ do Dejan Jelovic đưa ra (Các cuộc gọi liên ứng dụng cực kỳ chậm), các cuộc gọi chỉ sử dụng các kiểu tích hợp mất khoảng 1ms trên P4 1.7G. Điều này quá tốn kém đối với một số chức năng cần được gọi thường xuyên. Như ông đã đề cập, để triển khai một plug-in vẽ, việc vẽ 200 điểm trong OnPaint cần chi phí cuộc gọi là 200ms. Mặc dù có thể đạt được khả năng tối ưu hóa thông qua các lệnh gọi hàng loạt, nhưng việc giảm hiệu quả của các lệnh gọi xuyên AppDomain chắc chắn là không thể tránh khỏi. May mắn thay, người ta nói rằng trong Whidbey, các kiểu tích hợp trong các cuộc gọi liên AppDomain có thể được tối ưu hóa mà không cần Marshal, do đó tốc độ cuộc gọi có thể nhanh hơn 7 lần so với triển khai hiện tại... Tôi không biết nên khen triển khai của Whidbey hay nên chê phiên bản hiện tại là tệ, haha.
2. Dễ sử dụng: Các kiểu trong Assembly cần được gỡ cài đặt riêng có thể không hỗ trợ Marshal, do đó bạn cần tự mình xử lý việc quản lý kiểu.
3. Phiên bản: Cách đóng gói phiên bản tải chính xác trong nhiều AppDomain.
Ngoài ra còn có vấn đề về an ninh. Đối với Assembly.Load thông thường, Assembly đã tải chạy dưới sự chứng minh của trình tải, đây chắc chắn là một rủi ro bảo mật. Nó có thể dễ bị tấn công tương tự như chương trình trên Unix tràn để đọc và ghi các tệp có quyền root để ghi lại các tệp hệ thống. Bằng cách tải Assembly vào một AppDomain duy nhất, bạn có thể thiết lập quyền CAS riêng biệt và giảm quyền thực thi. Do cơ chế kiểm soát quyền bốn cấp theo kiến trúc CLR nên mức độ chi tiết tốt nhất chỉ có thể là AppDomain. May mắn thay, Whidbey được cho là sẽ bổ sung hỗ trợ cho việc tải cụm với các bằng chứng khác nhau.
Qua những thảo luận này, chúng ta có thể thấy rằng ngữ nghĩa của Assembly.Unload rất quan trọng đối với các chương trình dựa trên mô hình plug-in. Tuy nhiên, hiện tại và trong các phiên bản gần đây, việc mô phỏng ngữ nghĩa của nó thông qua AppDomain là lựa chọn phù hợp hơn. Mặc dù có vấn đề về hiệu suất và khả năng sử dụng, nhưng nó có thể kiểm soát các yếu tố như chức năng và bảo mật ở mức độ lớn hơn. Về lâu dài, việc triển khai Assembly.Unload hoàn toàn khả thi. Việc dỡ bỏ các lớp trong Java là ví dụ tốt nhất. Các lý do trước đây thực sự là vấn đề về khối lượng công việc và độ phức tạp, và không có vấn đề kỹ thuật nào không thể giải quyết được.
chornbe
Bạn cũng phải đóng gói assembly đã tải vào một lớp khác, được tải bởi appdomain mới. Đây là mã khi nó hoạt động với tôi: (Tôi đã tạo một số loại ngoại lệ tùy chỉnh và bạn sẽ thấy tôi đã lấy lại chúng - chúng không phải là hậu duệ của MarshalByRefObject nên tôi không thể chỉ ném chúng từ mã được đóng gói).
--- cắt tập hồ sơ hạng nhất.
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
hai mươi mốt
hai mươi hai
hai mươi ba
hai mươi bốn
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
sử dụng
Hệ thống;
sử dụng
Hệ thống.Phản chiếu;
sử dụng
Hệ thống.Bộ sưu tập;
không gian tên
Bộ nạp{
công cộng
lớp học
ObjectLoader: IDisposable {
được bảo vệ
Tên miền có thể băm =
mới
Bảng băm();
được bảo vệ
Bộ tải bảng băm =
mới
Bảng băm();
công cộng
Trình tải đối tượng() {
}
công cộng
sự vật
Lấy đối tượng(
sợi dây
Tên dll,
sợi dây
loạiTên,
sự vật
[] hàm tạoParms ){
Loader.AssemblyLoader al =
vô giá trị
;
sự vật
ồ =
vô giá trị
;
thử
{
al = (Loader.AssemblyLoader)loaders[dllName];
}
nắm lấy
(Ngoại lệ){}
nếu như
(và ==
vô giá trị
){
Thiết lập AppDomainSetup =
mới
Cài đặt AppDomain();
thiết lập.ShadowCopyFiles =
"ĐÚNG VẬY"
;
Tên miền AppDomain = AppDomain.CreateDomain(dllName,
vô giá trị
, cài đặt );
domains.Add(dllName, domain );
sự vật
[] parms = {tên dll};
Liên kết BindingFlags = BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.Public;
thử
{
al = (Loader.AssemblyLoader)domain.CreateInstanceFromAndUnwrap(
"Loader.dll"
,
"Loader.AssemblyLoader"
,
ĐÚNG VẬY
, ràng buộc,
vô giá trị
, parms,
vô giá trị
,
vô giá trị
,
vô giá trị
);
}
nắm lấy
(Ngoại lệ){
ném
mới
Lỗi AssemblyLoadFailureException();
}
nếu như
( và !=
vô giá trị
){
nếu như
( !loaders.ContainsKey(dllName ) ){
loaders.Add(dllName, al);
}
khác
{
ném
mới
AssemblyAlreadyLoadedException();
}
}
khác
{
ném
mới
Lỗi AssemblyNotLoadedException();
}
}
nếu như
( và !=
vô giá trị
){
o = al. GetObject(kiểuTên, constructorParms);
nếu như
( hoặc !=
vô giá trị
&& hoặc
là
AssemblyNotLoadedException (Lắp ráp không được tải)
ném
mới
Lỗi AssemblyNotLoadedException();
}
nếu như
( hoặc ==
vô giá trị
|| hoặc
là
Ngoại lệ ObjectLoadFailure){
sợi dây
tin nhắn =
"Không thể tải đối tượng. Kiểm tra tên loại đó"
+ loạiTên +
" và các tham số của hàm tạo là chính xác. Đảm bảo rằng tên kiểu "
+ loạiTên +
" tồn tại trong hội đồng "
+ tên dll +
"."
;
ném
mới
ObjectLoadFailureException(tin nhắn);
}
}
trở lại
ôi;
}
công cộng
vô hiệu
Dỡ bỏ(
sợi dây
Tên dll){
nếu như
(domain.ContainsKey(dllName) ){
Tên miền AppDomain = (AppDomain)domains[dllName];
AppDomain.Unload(tên miền);
tên miền.Xóa(dllName);
}
}
~Trình tải đối tượng(){
vứt bỏ(
SAI
);
}
công cộng
vô hiệu
Bỏ đi(){
vứt bỏ(
ĐÚNG VẬY
);
}
riêng tư
vô hiệu
vứt bỏ(
bool
xử lý){
nếu như
( xử lý ){
bộ nạp. Clear();
foreach
(
sự vật
ôi
TRONG
miền.Khóa){
sợi dây
dllName = o.ToString();
Gỡ bỏ(dllName);
}
tên miền.Clear();
}
}
}
}
|
--- cắt cuối.
--- cắt tập tin lớp thứ hai.
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
hai mươi mốt
hai mươi hai
hai mươi ba
hai mươi bốn
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
sử dụng
Hệ thống;
sử dụng
Hệ thống.Phản chiếu;
không gian tên
Bộ nạp {
nội bộ
lớp học
AssemblyLoader: MarshalByRefObject, IDisposable {
#khu vực khai báo cấp lớp
riêng tư
Lắp ráp a =
vô giá trị
;
#khuvựccuối
#region các hàm tạo và hàm hủy
công cộng
Lắp rápLoader
sợi dây
Đường dẫn đầy đủ){
nếu như
( một ==
vô giá trị
){
a = Assembly.LoadFrom(fullPath);
}
}
~Trình tải lắp ráp(){
vứt bỏ(
SAI
);
}
công cộng
vô hiệu
Bỏ đi(){
vứt bỏ(
ĐÚNG VẬY
);
}
riêng tư
vô hiệu
vứt bỏ(
bool
xử lý){
nếu như
( xử lý ){
một =
vô giá trị
;
Hệ thống. GC. Thu thập();
Hệ thống.GC.WaitForPendingFinalizers();
Hệ thống.GC.Thu thập( 0 );
}
}
#khuvựccuối
#chức năng công cộng của vùng
công cộng
sự vật
Lấy đối tượng(
sợi dây
tên loại,
sự vật
[]ctorParms){
Cờ BindingFlags = BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.Public;
sự vật
ồ =
vô giá trị
;
nếu như
( một !=
vô giá trị
){
thử
{
o = a.CreateInstance(kiểu tên,
ĐÚNG VẬY
, cờ,
vô giá trị
, ctorParms,
vô giá trị
,
vô giá trị
);
}
nắm lấy
(Ngoại lệ){
ồ =
mới
Ngoại lệ ObjectLoadFailure();
}
}
khác
{
ồ =
mới
Lỗi AssemblyNotLoadedException();
}
trở lại
ôi;
}
công cộng
sự vật
Lấy đối tượng(
sợi dây
tên kiểu){
trở lại
Lấy đối tượng(tên kiểu,
vô giá trị
);
}
#khuvựccuối
}
}
|
--- cắt cuối.
Một số tài nguyên liên quan: Tại sao không có phương thức Assembly.Unload? http://blogs.msdn.com/jasonz/archive/2004/05/31/145105.aspx .
AppDomains ("miền ứng dụng") http://blogs.msdn.com/cbrumme/archive/2003/06/01/51466.aspx .
AppDomain và Shadow Copy http://blogs.msdn.com/junfeng/archive/2004/02/09/69919.aspx .
Đây là phần cuối của bài viết này về giải thích chi tiết của C# về Assembly.Unload. Để biết thêm nội dung liên quan về C# về Assembly.Unload, vui lòng tìm kiếm các bài viết trước của tôi hoặc tiếp tục duyệt các bài viết liên quan sau. Tôi hy vọng bạn sẽ ủng hộ tôi trong tương lai! .
Liên kết gốc: https://www.cnblogs.com/ccBoy/archive/2004/07/13/23636.html.
Cuối cùng, bài viết này về giải thích chi tiết của C# về Assembly.Unload kết thúc tại đây. Nếu bạn muốn biết thêm về giải thích chi tiết của C# về Assembly.Unload, vui lòng tìm kiếm các bài viết CFSDN hoặc tiếp tục duyệt các bài viết liên quan. Tôi hy vọng bạn sẽ ủng hộ blog của tôi trong tương lai! .
Tôi là một lập trình viên xuất sắc, rất giỏi!