원본: http://www.micel.cn/article.asp?id=1698
.Net의 가비지 수집 메커니즘을 알고 계십니까? GC가 어떻게 작동하는지 간략하게 설명해주실 수 있나요? 메모리를 어떻게 효과적으로 관리할 수 있나요? Using 문의 본문에서 인스턴스화된 개체의 역할은 무엇입니까?
이 섹션은 다음과 같이 구성됩니다. 1. Net 유형 및 메모리 할당 2. GC 가비지 수집기 작동 방식 3. 관리되지 않는 리소스란 무엇입니까 4. 객체 리소스를 효과적으로 해제하는 방법. 요약 이제 이 섹션에 대한 연구를 시작하겠습니다.
1..Net 유형 및 메모리 할당
Net의 모든 형식은 System.Object 형식에서 직접 또는 간접적으로 파생됩니다.
CTS의 유형은 메모리 힙에 할당되는 참조 유형(참조 유형, 관리 유형이라고도 함)과 값 유형이라는 두 가지 범주로 나뉩니다. 값 유형은 스택에 할당됩니다. 그림과 같이
값 유형은 먼저 들어오고 나중에 나가는 방식으로 스택에 있습니다. 이렇게 하면 값 유형 변수가 범위 밖으로 푸시되기 전에 리소스가 해제됩니다. 참조 유형보다 더 간단하고 효율적입니다. 스택은 높은 주소에서 낮은 주소로 메모리를 할당합니다.
참조형은 관리형 힙(Managed Heap)에 할당되고, 객체를 생성하기 위해 new를 사용하면 이 변수에 객체의 주소가 저장된다. 반대로 관리되는 힙은 그림과 같이 낮은 주소에서 높은 주소로 메모리를 할당합니다.
2. GC 가비지 수집기 작동 방식
위 그림에서는 dataSet이 만료되면 객체의 소멸을 표시하지 않으며, 힙에 있는 객체는 계속 존재하며 GC 재활용을 기다리고 있습니다.
가비지 수집기가 생성을 통한 개체 에이징을 지원하는 것이 권장되지만 필수는 아닙니다. 세대는 메모리에 상대적인 연령이 있는 개체의 단위입니다. 물체
코드명이나 연령은 해당 개체가 속한 세대를 식별합니다. 애플리케이션의 라이프사이클에서 더 최근에 생성된 객체는 새로운 세대에 속하며 이전에 생성된 객체보다 더 높은 가치를 갖습니다.
하위 코드 번호가 낮습니다. 가장 최근 세대의 개체 코드는 0입니다.
새로운 객체를 생성할 때 먼저 Free Linked List를 검색하여 가장 적합한 메모리 블록을 찾아 할당하고, 메모리 블록 연결 리스트를 조정하고 조각을 병합해야 합니다. 새 작업은 거의 O(1) 시간 내에 완료될 수 있으며 힙의 최상위 포인터에 1을 추가합니다. 작동 원리는 다음과 같습니다. 관리되는 힙의 남은 공간이 부족하거나 생성기 0의 공간이 가득 차면 GC가 실행되어 메모리 회수를 시작합니다. 가비지 컬렉션 시작 시 GC는 힙 메모리를 압축 및 조정하며 객체는 상위에 집중됩니다. GC는 가비지를 스캔할 때 일정량의 CPU 시간을 차지합니다. 원래 GC 알고리즘은 실제로 전체 힙을 스캔하므로 비효율적입니다. 현재 GC는 힙의 개체를 3세대로 나눕니다. 힙에 가장 최근 항목이 0세대이고 그 다음이 1세대, 2세대입니다. 첫 번째 GC는 0세대만 검색합니다. 회수된 공간이 현재 사용하기에 충분하다면 다른 세대의 개체를 스캔할 필요가 없습니다. 따라서 GC는 C++보다 더 효율적으로 개체를 생성하며 전체 힙 공간을 검색할 필요가 없습니다. 스캐닝 전략과 메모리 관리 전략으로 인한 성능 향상은 GC가 차지하는 CPU 시간을 보상하기에 충분합니다.
3. 관리되지 않는 리소스란 무엇입니까?
일반적인 관리되지 않는 리소스는 파일, 창 또는 네트워크 연결과 같은 운영 체제 리소스를 래핑하는 개체입니다. 이러한 리소스의 경우 가비지 수집기는 관리되지 않는 리소스를 래핑하는 개체의 수명을 추적할 수 있지만 이러한 리소스를 정리하세요. 다행히 .net Framework에서 제공하는 Finalize() 메서드를 사용하면 가비지 수집기가 해당 리소스를 재활용하기 전에 관리되지 않는 리소스를 적절하게 정리할 수 있습니다. 다음은 관리되지 않는 몇 가지 일반적인 리소스입니다. 브러시, 스트림 개체, 구성 요소 개체 및 기타 리소스(Object, OdbcDataReader, OleDBDataReader, Pen, Regex, Socket, StreamWriter, ApplicationContext, Brush,
구성 요소, 구성 요소 디자이너, 컨테이너, 컨텍스트, 커서, FileStream,
글꼴, 아이콘, 이미지, 매트릭스, 타이머, 도구 설명). (MSDN 참조)
4. 관리되지 않는 리소스를 효과적으로 해제하는 방법.
GC는 관리되지 않는 리소스를 관리할 수 없는데, 관리되지 않는 리소스를 해제하는 방법은 무엇입니까? .Net은 두 가지 방법을 제공합니다.
(1) 소멸자: 가비지 수집기가 관리되지 않는 개체의 리소스를 재활용할 때 개체의 종료 메서드 Finalize()를 호출하여 리소스를 정리합니다. 그러나 GC 작업 규칙의 제한으로 인해 GC는 개체의 Finalize를 호출합니다. 메서드는 리소스가 한 번 해제되지 않으며 두 번째 호출 후에 개체가 삭제됩니다.
(2) IDisposable 인터페이스를 상속하고 Dispose() 메서드를 구현합니다. IDisposable 인터페이스는 언어 수준 지원을 통해 패턴을 정의하고, 관리되지 않는 리소스를 해제하기 위한 특정 메커니즘을 제공하며, 소멸자의 가비지 수집과 관련된 고유한 문제를 방지합니다. 관련 문제.
가비지 수집 메커니즘을 더 잘 이해하기 위해 특별히 코드의 일부를 작성하고 자세한 설명을 추가했습니다. 단일 클래스 FrankClassWithDispose(IDisposable 인터페이스 상속), FrankClassNoFinalize(종료자 없음), FrankClassWithDestructor(소멸자 정의)를 정의합니다.
구체적인 코드는 다음과 같습니다.
암호
1 시스템 사용;
2 System.Collections.Generic을 사용합니다.
3 System.Text를 사용합니다.
4 System.Data를 사용합니다.
5 System.Data.Odbc를 사용합니다.
6 System.드로잉을 사용;
7 // 코드: Frank Xu Lei 2009년 2월 18일
8 // .NET 메모리 관리 연구
9 // 가비지 컬렉터 가비지 컬렉터. 정책에 따라 필요할 때 호스팅된 리소스를 회수할 수 있습니다.
10 // 하지만 GC는 관리되지 않는 리소스를 관리하는 방법을 모릅니다. 네트워크 연결, 데이터베이스 연결, 브러시, 구성요소 등
11 //관리되지 않는 리소스 해제 문제를 해결하는 두 가지 메커니즘입니다. 소멸자, IDispose 인터페이스
12 // COM 참조 횟수
13 // C++ 수동 관리, 신규 삭제
14 // VB 자동 관리
15 네임스페이스 MemoryManagement
16 {
17 // IDisposable 인터페이스를 상속하고, Dispose 메서드를 구현하고, FrankClassDispose의 인스턴스 리소스를 해제합니다.
18 공개 클래스 FrankClassWithDispose: IDisposable
19 {
20 개인 OdbcConnection _odbcConnection = null;
스물하나
22 // 생성자
23 공개 FrankClassWithDispose()
스물넷 {
25 if (_odbcConnection == null )
26 _odbcConnection = 새로운 OdbcConnection();
27 Console.WriteLine( " FrankClassWithDispose가 생성되었습니다. " );
28 }
29 //테스트 방법
30 공공 무효 DoSomething()
31 {
32
33 /**/ /// /무언가를 수행하기 위한 코드
34 반환;
35}
36 // Dispose를 구현하고 이 클래스에서 사용하는 리소스를 해제합니다.
37 공공 무효 Dispose()
38 {
39 if (_odbcConnection != null )
40 _odbcConnection.Dispose();
41 Console.WriteLine( " FrankClassWithDispose가 삭제되었습니다. " );
42 }
43}
44 // Finalize가 구현되지 않았습니다. GC가 FrankClassFinalize의 인스턴스 리소스를 재활용할 때까지 기다렸다가 GC가 실행될 때 직접 재활용합니다.
45 공개 클래스 FrankClassNoFinalize
46 {
47 개인 OdbcConnection _odbcConnection = null ;
48 //생성자
49 공개 FrankClassNoFinalize()
50 {
51 if (_odbcConnection == null )
52 _odbcConnection = 새로운 OdbcConnection();
53 Console.WriteLine( " FrankClassNoFinalize가 생성되었습니다. " );
54 }
55 //테스트 방법
56 공공 무효 DoSomething()
57 {
58
59 // GC.수집();
60 /**/ /// /무언가를 수행하기 위한 코드
61 반환;
62 }
63}
64 // 소멸자를 구현하고 이를 Finalize 메서드로 컴파일한 다음 객체의 소멸자를 호출합니다.
65 // GC가 실행 중일 때 첫 번째 호출에서는 리소스가 해제되지 않고 두 번째 호출에서만 리소스가 해제됩니다.
66 //FrankClassDestructor의 인스턴스 리소스
67 // CLR은 독립 스레드를 사용하여 개체의 Finalize 메서드를 실행합니다. 호출이 잦으면 성능이 저하됩니다.
68 공개 클래스 FrankClassWithDestructor
69 {
70 개인 OdbcConnection _odbcConnection = null ;
71 //생성자
72 공개 FrankClassWithDestructor()
73 {
74 if (_odbcConnection == null )
75 _odbcConnection = 새로운 OdbcConnection();
76 Console.WriteLine( " FrankClassWithDestructor가 생성되었습니다. " );
77 }
78 //테스트 방법
79 공공 무효 DoSomething()
80 {
81 /**/ /// /무언가를 수행하기 위한 코드
82
83 복귀 ;
84}
85 // 소멸자, 관리되지 않는 리소스를 해제합니다.
86 ~ FrankClassWithDestructor()
87 {
88 if (_odbcConnection != null )
89 _odbcConnection.Dispose();
90 Console.WriteLine( " FrankClassWithDestructor가 삭제되었습니다. " );
91 }
92 }
93}
94
관리되지 않는 개체 OdbcConnection의 인스턴스가 사용됩니다. 구축된 클라이언트를 간략하게 테스트했습니다. 클라이언트 코드는 다음과 같습니다.
암호
1 시스템 사용;
2 System.Collections.Generic을 사용합니다.
3 System.Text를 사용합니다.
4 System.Data를 사용합니다.
5 메모리 관리 사용;
6 // 코드: Frank Xu Lei 2009년 2월 18일
7 // .NET 메모리 관리 연구
8 // 관리되지 않는 개체가 회수되었는지 테스트합니다.
9 // 비관리 코드 테스트, 비교
10 // 관리되는 코드의 경우 GC는 보다 전략적으로 자체적으로 재활용하거나 IDisposable을 구현하고 Dispose() 메서드를 호출하여 적극적으로 해제할 수 있습니다.
11 네임스페이스 MemoryManagementClient
12 {
13개 수업 프로그램
14 {
15 정적 무효 Main(string [] args)
16 {
17
18 /**/ //////////////////////////////////////// //(1 ) / /////////////////////////////////////////// //
19 //Dispose() 메서드를 호출하여 적극적으로 해제합니다. 자원, 유연성
20 FrankClassWithDispose _frankClassWithDispose = null ;
21번 시도
스물 둘 {
23 _frankClassWithDispose = 새로운 FrankClassWithDispose();
24 _frankClassWithDispose.DoSomething();
25
26}
27 드디어
28 {
29 if (_frankClassWithDispose != null )
30 _frankClassWithDispose.Dispose();
31 // Console.WriteLine("FrankClassWithDispose 인스턴스가 해제되었습니다.");
32}
33
34 /**/ //////////////////////////////////////// //(2 ) / ///////////////////////////////////////////////
35 // Using 문을 사용하여 관리되지 않는 개체를 만들 수 있습니다. 메서드 실행이 끝나기 전에 해당 개체가 호출됩니다.
36 (FrankClassWithDispose _frankClassWithDispose2 = new FrankClassWithDispose())를 사용하여
37 {
38 // _frankClassWithDispose2.DoSomething();
39 }
40
41 /**/ ///////////////////////////////////////// //(3 ) / /////////////////////////////////////////// //
42 //가비지 컬렉터가 실행 중일 때 리소스가 한 번 해제됩니다.
43 FrankClassNoFinalize _frankClassNoFinalize = 새로운 FrankClassNoFinalize();
44 _frankClassNoFinalize.DoSomething();
45
46 /**/ //////////////////////////////////////////// (4) ////////////////////////////////////////////// / /
47 // 가비지 수집기가 실행 중일 때 리소스를 해제하는 데 두 배의 시간이 걸립니다.
48 FrankClassWithDestructor _frankClassWithDestructor = new FrankClassWithDestructor();
49 _frankClassWithDestructor.DoSomething();
50 /**/ //////////////////////////////////////////// (5) /////////////////////////////////////////////// /
51 // Using 문은 IDispose 인터페이스를 구현하지 않기 때문에 개체를 만드는 데 사용할 수 없습니다.
52 // (FrankClassWithDestructor _frankClassWithDestructor2 = new FrankClassWithDestructor()) 사용
53 // {
54 // _frankClassWithDestructor2.DoSomething();
55 // }
56
57 /**/ //////////////////////////////////////////// /// ////////////////////////////////////////// //
58 // 디버그용
59 Console.WriteLine( " 계속하려면 아무 키나 누르십시오 " );
60 콘솔.ReadLine();
61
62
63}
64}
65 }
66
때때로 리소스는 특정 시간에 해제되어야 합니다. 클래스는 리소스 관리 및 정리 작업 메서드 IDisposable.Dispose를 수행하는 인터페이스 IDisposable을 구현할 수 있습니다.
호출자가 개체를 정리하기 위해 Dispose 메서드를 호출해야 하는 경우 클래스는 계약의 일부로 Dispose 메서드를 구현해야 합니다. 가비지 수집기는 기본적으로 호출하지 않습니다.
Dispose 메서드. 그러나 Dispose 메서드를 구현하면 GC에서 메서드를 호출하여 가비지 수집기의 최종 동작을 조절할 수 있습니다.
Dispose() 메서드를 호출하면 리소스가 적극적으로 해제되고 유연해집니다. Using 문을 사용하여 관리되지 않는 개체를 만들 수 있으며 메서드 실행이 끝나기 전에 호출됩니다.
Dispose() 메서드는 리소스를 해제합니다. 코드의 두 끝의 효과는 동일합니다. 컴파일된 IL을 볼 수 있습니다.
암호
1. 시도해 보세요
2 {
3 IL_0003: 아니요
4 IL_0004: newobj 인스턴스 void [MemoryManagement]MemoryManagement.FrankClassWithDispose::.ctor()
5 IL_0009: stloc 0
6 IL_000a: ldloc.
7 IL_000b: callvirt 인스턴스 void [MemoryManagement]MemoryManagement.FrankClassWithDispose::DoSomething()
8 IL_0010: 아니요
9 IL_0011: 아니요
10 IL_0012: 떠나세요.s IL_0028
11 } // 종료 .try
드디어 12
13 {
14 IL_0014: 아니요
15 IL_0015: ldloc.
16 IL_0016: ldnull
17 IL_0017: ceq
18 IL_0019: stloc.s CS$ 4 $ 0000
19 IL_001b: ldloc.s CS$ 4 $ 0000
20 IL_001d: brtrue.s IL_0026
21 IL_001f: ldloc.
22 IL_0020: callvirt 인스턴스 void [MemoryManagement]MemoryManagement.FrankClassWithDispose::Dispose()
23 IL_0025: 아니요
24 IL_0026: 아니요
25 IL_0027: 마지막으로
26 } // 핸들러 종료
27 IL_0028: 아니요
28 IL_0029: newobj 인스턴스 void [MemoryManagement]MemoryManagement.FrankClassWithDispose::.ctor()
29 IL_002e: stloc 1
30. 시도해 보세요
31 {
32 IL_002f: 아니요
33 IL_0030: 아니요
34 IL_0031: 떠나세요.s IL_0045
35 } // 종료 .try
마침내 36
37 {
38 IL_0033: ldloc 1
39 IL_0034: ldnull
40 IL_0035: ceq
41 IL_0037: stloc.s CS$ 4 $ 0000
42 IL_0039: ldloc.s CS$ 4 $ 0000
43 IL_003b: brtrue.s IL_0044
44 IL_003d: ldloc 1
45 IL_003e: callvirt 인스턴스 void [mscorlib]System.IDisposable::Dispose()
46 IL_0043: 아니요
47 IL_0044: 결국
48 } // 핸들러 종료
49
Using 문은 관리되지 않는 개체 리소스를 해제하는 것과 동일한 효과를 갖습니다. 이는 Using 키워드의 용도 및 유사한 질문과 같은 인터뷰에서 자주 발생합니다. 기본적인 이상적인 대답은 네임스페이스를 참조하고 네임스페이스에 대한 별칭을 설정하는 것 외에도 이 사용이 try finally 블록과 같은 관리되지 않는 개체 리소스의 재활용을 실현한다는 것입니다. 그냥 쓰는 방법은 간단합니다.
Dispose 메서드를 사용하여 관리되지 않는 개체를 해제하는 경우 GC.SuppressFinalize를 호출해야 합니다. 개체가 종료 대기열에 있는 경우 GC.SuppressFinalize는 GC가 Finalize 메서드를 호출하지 못하게 합니다. Finalize 메서드를 호출하면 일부 성능이 저하되기 때문입니다. Dispose 메서드가 이미 위임된 리소스를 정리한 경우 GC가 개체의 Finalize 메서드(MSDN)를 다시 호출할 필요가 없습니다. 참조용 MSDN 코드가 첨부되어 있습니다.
암호
공개 클래스 BaseResource: IDisposable
{
//관리되지 않는 외부 리소스를 가리킵니다.
개인 IntPtr 핸들;
// 이 클래스에서 사용하는 다른 관리 리소스입니다.
개인 구성 요소 구성 요소;
// .Dispose 메서드 호출 여부를 추적하고, 비트에 플래그를 지정하고, 가비지 수집기의 동작을 제어합니다.
비공개 bool 삭제됨 = false ;
//건설자
공개 BaseResource()
{
// 여기에 적절한 생성자 코드를 삽입합니다.
}
// IDisposable 인터페이스를 구현합니다.
// 가상 메소드로 선언할 수 없습니다.
// 서브클래스는 이 메서드를 재정의할 수 없습니다.
공공 무효 처리()
{
폐기( true );
//완료 큐에서 나가기
//객체의 차단 종료자 코드 설정
//
GC.SuppressFinalize( this );
}
// Dispose(bool disposing)는 두 가지 상황에서 실행됩니다.
// disposing이 true이면 메서드가 호출된 것입니다.
// 또는 사용자 코드에 의해 간접적으로 호출되는 관리 코드와 비관리 코드 모두 해제될 수 있습니다.
// disposing이 false인 경우 종료자가 내부적으로 메서드를 호출한 것입니다.
// 다른 개체를 참조할 수 없으며 관리되지 않는 리소스만 해제할 수 있습니다.
보호된 가상 무효 처리(bool disposing)
{
// Dispose가 호출되었는지 확인합니다.
if ( ! this .disposed)
{
// true이면 관리되는 리소스와 관리되지 않는 리소스를 모두 해제합니다.
만약 (처분)
{
// 관리되는 리소스를 해제합니다.
구성요소.Dispose();
}
// 처리가 false인 경우 관리되지 않는 리소스를 해제합니다.
// 아래 코드만 실행됩니다.
CloseHandle(핸들);
핸들 = IntPtr.Zero;
// 이는 스레드로부터 안전하지 않습니다.
// 관리되는 리소스가 해제된 후 개체를 삭제하기 위해 다른 스레드를 시작할 수 있습니다.
// 그러나 폐기된 플래그가 true로 설정되기 전
// 스레드 안전성이 필요한 경우 클라이언트는 이를 구현해야 합니다.
}
폐기 = 사실;
}
//interop을 사용하여 메서드 호출
// 관리되지 않는 리소스를 지웁니다.
[System.Runtime.InteropServices.DllImport( " Kernel32 " )]
private extern static Boolean CloseHandle(IntPtr 핸들);
//C# 소멸자를 사용하여 종료자 코드 구현
// Dispose 메서드가 호출되지 않은 경우에만 호출하고 실행할 수 있습니다.
// 기본 클래스에 마무리할 기회를 주는 경우.
// 서브클래스에 소멸자를 제공하지 마세요.
~베이스리소스()
{
// 정리 코드를 다시 생성하지 마세요.
// 안정성과 유지 관리성을 고려할 때 Dispose(false)를 호출하는 것이 가장 좋은 방법입니다.
폐기( false );
}
// Dispose 메서드를 여러 번 호출할 수 있습니다.
// 그러나 객체가 해제된 경우 예외가 발생합니다.
// 객체를 처리할 때와 상관없이 객체가 해제되었는지 확인합니다.
// 폐기되었는지 확인합니다.
공공 무효 DoSomething()
{
if(이 .disposed)
{
새로운 ObjectDisposedException()을 던져보세요;
}
}
Dispose 메서드보다 Close 메서드를 호출하는 것이 더 자연스러운 형식의 경우 기본 클래스에 Close 메서드를 추가할 수 있습니다.
Close 메서드는 매개 변수를 사용하지 않으며 적절한 정리 작업을 수행하는 Dispose 메서드를 호출합니다.
다음 예제에서는 Close 메서드를 보여줍니다.
// 메소드를 가상으로 설정하지 마세요.
// 상속된 클래스는 이 메서드를 재정의할 수 없습니다.
공개 무효 닫기()
{
// 매개변수 없이 Dispose 매개변수를 호출합니다.
처분();
}
공개 정적 무효 Main()
{
//여기에 코드를 삽입하여 생성합니다.
// BaseResource 객체를 사용합니다.
}