이 블로그에서는 소프트웨어 개발 과정에서 성능을 향상시키는 방법에 중점을 둘 것입니다. 이는 소프트웨어 개발이나 연구 개발 과정에서 뿌리 깊은 문제입니다. 이 기사에서는 메모리 할당과 메모리 재활용이라는 두 가지 측면에서 소프트웨어 코드를 작성하는 과정에서 계산이 어떻게 작동하는지 주로 설명합니다. 여기에서는 메모리 관리의 프로세스와 방법을 이해하여 메모리 관리에 주의를 기울이고 향후 소프트웨어 개발에 활용할 수 있습니다.
값 유형에는 int, float, double, bool, 구조체, 참조, 객체 인스턴스를 나타내는 변수가 포함됩니다.
참조 유형에는 클래스 및 배열이 포함됩니다. 문자열, 객체
일반적으로 말하면, 값 유형은 스택에 저장됩니다(클래스의 값 필드, 클래스의 참조 필드, 제어된(힙)에 참조와 함께 저장되는 배열 요소와 같은 참조에 포함된 값 유형 제외) ; 참조 유형은 제어된 힙에 저장됩니다. 이를 제어된 힙이라고 부르는 이유는 아래에서 자세히 설명합니다.
몇 가지 개념:
가상 메모리: 32비트 컴퓨터에서 각 프로세스에는 4G의 가상 메모리가 있습니다.
규제된 힙(관리되는 힙): 누가 규제합니까? 물론 가비지 수집기는 가비지 수집기입니다. 그것을 관리하는 방법은 아래에서 논의 될 것입니다.
쓸데없는 유닛 컬렉터: 관리되는 힙을 압축하고 참조 주소를 업데이트하는 것 외에도 가비지 컬렉터는 관리되는 힙의 정보 목록 등을 유지 관리합니다.
값 유형 저장과 관련하여 먼저 다음 코드를 살펴보세요.
{
int age=20;
double Salary=2000;
}
위에 정의된 두 변수 int age는 스택에 저장되는 age 값을 저장하기 위해 4바이트의 메모리 공간을 할당해야 함을 컴파일러에 알립니다. 스택은 선입후출 방식의 데이터 구조로, 상위 주소부터 하위 주소까지 데이터를 저장합니다. 컴퓨터 레지스터는 항상 스택 하단의 여유 공간 주소를 가리키는 스택 포인터를 유지합니다. int 유형의 값을 정의하면 변수가 외부로 나갈 때 스택 포인터가 4바이트 주소만큼 감소됩니다. 범위, 스택 포인터는 그에 따라 주소를 4바이트씩 증가시킵니다. 단지 스택 포인터를 위아래로 이동하므로 스택의 성능이 상당히 높습니다.
참조 유형의 저장을 살펴보고 먼저 코드를 살펴보겠습니다.
{
Customer customerA;
customerA=new Customer (); // Customer 인스턴스가 32바이트를 차지한다고 가정
}
위 코드의 첫 번째 줄은 먼저 Customer 참조를 선언하고 참조 이름은 customerA이며 스토리지를 할당합니다. 스택에서 이 참조를 위한 공간. 참조만 저장하기 때문에 저장 공간의 크기는 4바이트입니다. 이 참조는 Customer 인스턴스가 저장될 공간 주소를 가리킵니다. 이번에는 공간을 할당하는 것이 전부입니다.
두 번째 줄을 실행하는 동안 .net 환경은 관리되는 힙을 검색하여 클래스 인스턴스에 할당된 사용되지 않은 연속 32바이트 공간을 찾고 customerA가 이 공간을 가리키도록 설정합니다. 최상위 위치(힙 공간은 낮은 것부터 높은 것까지 사용됨) 참조 변수가 범위를 벗어나면 가비지 수집기가 정리할 때까지 인스턴스가 관리되는 힙에 남아 있는 동안 스택의 참조는 유효하지 않게 됩니다.
주의 깊은 독자라면 참조 유형을 정의할 때 객체를 저장할 만큼 큰 메모리 공간을 찾기 위해 컴퓨터가 전체 힙을 검색해야 합니까? 이것은 매우 비효율적일까요? 연속 공간이 충분하지 않으면 어떻게 되나요? "보호"에 관한 내용입니다. 힙은 가비지 컬렉터에 의해 관리됩니다. 가비지 컬렉터가 실행되면 .net은 해제할 수 있는 모든 개체를 해제하고 다른 개체를 압축한 다음 모든 여유 공간을 결합하여 힙의 맨 위로 이동하여 연속적인 블록을 형성합니다. 동시에 다른 모바일 개체에 대한 참조를 업데이트합니다. 다른 객체 정의가 있는 경우 적합한 공간을 빠르게 찾을 수 있습니다. 관리되는 힙은 스택과 유사하게 작동하는 것으로 보입니다. 힙 포인터를 통해 공간을 할당하고 회수합니다.
위에서는 .net의 메모리 공간 할당 관리 프로세스와 방법에 대해 설명했습니다. 다음으로 메모리 재활용 프로세스에 대해 이야기하겠습니다. 자원 정리에 관해서는 두 가지 개념과 세 가지 방법을 언급해야 한다.
두 가지 개념은 관리되는 리소스와 관리되지 않는 리소스입니다.
관리되는 리소스는 .net 프레임워크의 CLR(공용 언어 런타임)에 의해 관리됩니다. 관리되지 않는 리소스는 CLR에 의해 관리되지 않습니다.
세 가지 방법은 Finalize(), Dispose(), Close()입니다.
1. Finalize()는 관리되지 않는 리소스를 지우는 소멸자 메서드입니다.
클래스에 정의됨:
public ClassName{
~ClassName()
{
//관리되지 않는 리소스 정리( 파일 닫기 및 데이터베이스 연결 등)
}
}
특성은 다음과 같습니다.
1.
가비지 컬렉터에 의해 관리됩니다. 가비지 컬렉터가 작동하면 이 메서드가 호출됩니다.
2. 고성능 오버헤드.
가비지 수집기가 작동하는 방식은 객체가 Finalize() 메서드를 실행하는 경우 처음 실행될 때 가비지 수집기가 특수 대기열에 배치하는 것입니다. 두 번째로 실행될 때 이 개체를 삭제합니다.
3. 정의와 호출은 표시할 수 없으며 정의는 소멸자 메소드 형식입니다.
위의 특성을 바탕으로 클래스에 실제로 필요하거나 다른 두 메서드와 함께 사용되는 경우가 아니면 Finalize() 메서드를 실행하지 않는 것이 가장 좋습니다.
2. Dispose() 메서드는 관리되는 리소스와 관리되지 않는 리소스를 포함하여 지워야 하는 모든 리소스를 지울 수 있습니다.
은 다음과 같이 정의됩니다.
public void Dispose()
{
//정리해야 할 리소스 정리(관리 및 비관리 리소스 포함)
System.GC.SuppressFinalize (this) ; //이 문장은 매우 중요합니다. 그 이유는 아래에서 설명하겠습니다.
}
특징:
1. 모든 클라이언트 코드는 리소스를 해제하려면 이 메서드를 명시적으로 호출해야 합니다.
2. 첫 번째 이유 때문에 일반적으로 백업이 필요하며, 이 백업은 일반적으로 소멸자 방식으로 재생됩니다.
3. 이 메서드를 정의하는 클래스는 IDisposable 인터페이스를 상속해야 합니다.
4. using 구문은 Dispose()를 호출하는 것과 동일합니다. using을 사용하면 기본적으로 Dispose() 메서드가 호출됩니다. 따라서 using을 사용하는 클래스는 IDisposable 인터페이스도 상속해야 합니다.
Dispose() 메서드는 더 유연하며 리소스가 더 이상 필요하지 않을 때 즉시 리소스를 해제합니다. 이는 리소스의 최종 폐기이며 이를 호출하면 해당 개체가 결국 삭제된다는 의미입니다.
3. 리소스 상태를 일시적으로 처리하고 나중에 사용할 수 있는 Close() 메서드입니다. 일반적으로 관리되지 않는 리소스를 처리합니다.
은 다음과 같이 정의됩니다.
public viod Close()
{
//파일 또는 데이터베이스 닫기 등 관리되지 않는 리소스의 상태 설정 연결
}
기능:
관리되지 않는 리소스의 상태를 설정하며 일반적으로 파일이나 데이터베이스 연결을 닫습니다.
각 부분의 기능을 보여주는 코드를 사용하여 아래에 포괄적이고 고전적인 예를 작성하십시오. (문제를 피하기 위해 이 예는 인터넷에서 구성되었으므로 간단히 설명하십시오. 그런 고전적이고 이해하기 쉬운 코드를 작성해주신 코드 작성자에게 감사의 말씀을 전하고 싶습니다. )
공용 클래스 ResourceHolder : System.IDisposable
{
public void Dispose()
{
Dispose(true);
System .GC.SuppressFinalize(this);
// 위의 코드 줄은 "가비지 수집기"가 이 클래스의 소멸자 메서드를 호출하는 것을 방지하는 것입니다
// " ~ResourceHolder () "
// 이를 방지해야 하는 이유는 무엇입니까? 사용자가 Dispose() 메서드 호출을 기억하면
// "Garbage Collector"가 "불필요하게" 필요하지 않기 때문입니다. "관리되지 않는" 리소스 해제
// 사용자가 호출하는 것을 기억하지 못하는 경우 "가비지 수집기"가 "불필요하게" 수행하도록 도와줍니다 ^_^
// 위에서 말한 내용을 이해하지 못하시겠지만 상관없습니다. 아래에 더 자세한 설명이 있습니다!
}
protected virtual void Dispose( bool disposing)
{
if (disposing)
{
// 다음은 "관리되는 리소스"를 정리하는 사용자 코드 조각입니다.
}
// 다음은 "관리되지 않는 리소스"를 정리하는 사용자 코드 조각입니다. 소멸자 메소드의 실제 실행 코드는 다음과 같습니다. 클라이언트 코드에서 Dispose() 메소드 호출을 잊어버리지 않도록 백업했습니다.
}
~ResourceHolder()
{
Dispose(false) // 정리는 다음과 같습니다. " 관리되지 않는 리소스"
}
}
위 코드가 이해되지 않으면 다음 설명을 꼭 읽어보세요. 조심스럽게, 그것은 매우 고전적이며, 보지 않으면 후회할 것입니다.
여기서 사용자는 Dispose(bool) 메서드 대신 Dispose() 메서드를 호출해야 한다는 점을 분명히 해야 합니다. 그러나 여기서 실제로 릴리스 작업을 수행하는 메서드는 Dispose()가 아니라, Dispose(bool)! 이유는 무엇입니까? Dispose()에서 Dispose(true)가 호출되고 매개변수가 "true"인 경우 이 함수는 관리되는 리소스와 관리되지 않는 리소스를 모두 정리해야 합니다. 앞서 말했듯이 소멸자 메서드는 관리되지 않는 리소스를 해제하는 데 사용됩니다. 그러면 Dispose()는 관리되지 않는 리소스를 해제하는 작업을 완료할 수 있으므로 실제로 소멸자 메서드의 용도는 무엇입니까? 소멸자 방법은 단지 "백업"일 뿐입니다!
왜?
공식적으로 말하면 "IDisposable" 인터페이스를 구현하는 모든 클래스는 프로그래머가 코드에서 이 클래스의 개체 인스턴스를 사용하는 한 조만간 이 클래스의 Dispose() 메서드를 호출해야 합니다. 동시에, 클래스에 관리되지 않는 리소스를 사용하려면 관리되지 않는 리소스도 해제되어야 합니다. 불행하게도 관리되지 않는 리소스를 해제하는 코드가 소멸자 메서드에 배치된 경우(위의 예는 "~ResourceHolder()"에 해당합니다.) ), 프로그래머는 이 릴리스 코드를 호출하기를 원하므로(소멸자 메서드는 사용자가 호출할 수 없고 시스템, 정확히 말하면 "가비지 수집기"에서만 호출할 수 있기 때문에) 수행하는 것이 불가능합니다. 위의 예에서 "관리되지 않는 리소스 정리" "사용자 코드 세그먼트"가 ~ResourceHolder()가 아닌 Dispose(bool)에 있는 이유를 알아보세요. 불행히도 모든 프로그래머가 항상 Dispose() 메서드 호출을 기억하는 것은 아닙니다. 프로그래머가 이 메서드를 호출하는 것을 잊어버린 경우 관리되는 리소스에는 문제가 없습니다. 조만간 이를 수집하는 "가비지 수집기"가 있을 것입니다(잠시 지연될 뿐입니다). 관리되지 않는 리소스는 어떻습니까? CLR의 제어를 받지 않는 경우 관리되지 않는 리소스는 절대 해제될 수 없습니다. 물론 그렇지 않습니다! Dispose() 호출을 잊어버린 경우에는 "가비지 수집기"가 필요합니다. 또한 관리되지 않는 리소스를 해제하기 위해 "소멸자 메서드"를 호출합니다. (프로그래머가 Dispose() 호출을 기억하고 있다면 "System.GC.SuppressFinalize(this);" 코드는 "가비지 수집기"가 해제되는 것을 방지할 수 있습니다. 소멸자 메서드를 호출하면 "관리되지 않는 리소스"를 한 번 더 해제할 필요가 없습니다. 따라서 프로그래머가 Dispose() 메서드 호출을 잊어버릴까 두렵지 않습니다.