>백엔드 개발 >C#.Net 튜토리얼 >Java 및 C#의 가비지 수집 메커니즘에 대한 자세한 소개

Java 및 C#의 가비지 수집 메커니즘에 대한 자세한 소개

黄舟
黄舟원래의
2017-03-03 13:24:131139검색

(1) 가비지 컬렉터의 기본 가정

(1) 최근에 메모리 공간을 할당받은 객체는 다음 작업이 필요할 가능성이 가장 높습니다. 석방되다. 메서드를 실행하기 전에 일반적으로 메서드에서 사용하는 개체에 대한 메모리 공간을 할당해야 합니다. 최근에 할당된 개체 모음을 검색하면 최소한의 작업으로 여유 메모리 공간을 최대한 확보하는 데 도움이 됩니다.

(2) 수명이 가장 긴 물체는 방출될 가능성이 가장 적습니다. 여러 라운드의 가비지 수집 후에도 여전히 존재하는 개체는 다음 가비지 수집 라운드에서 해제될 수 있는 임시 개체가 아닐 가능성이 높습니다. 이러한 메모리 블록을 검색하려면 많은 작업이 필요한 경우가 많지만 메모리 공간의 작은 부분만 사용할 수 있습니다. 출시된. .

(3) 동시에 메모리를 할당받은 객체는 일반적으로 동시에 사용됩니다. 동시에 메모리를 할당하는 객체 스토리지 위치를 서로 연결하면 캐시 성능을 향상시키는 데 도움이 될 수 있습니다.

(2) 여러 가지 가비지 수집 메커니즘

(1) 마크-스윕 수집기

이 수집기는 먼저 객체 그래프를 순회하여 도달 가능한 객체를 표시한 다음 스택에서 표시되지 않은 객체를 검색하고 메모리를 해제합니다. 이 유형의 수집기는 일반적으로 단일 스레드를 사용하여 작업하고 다른 작업을 중지합니다.

(2) Mark-Compact Collector

Mark-Sweep-Compact Collector라고도 하는데, 이는 Mark- 스윕 수집기. 동일한 마킹 단계. 두 번째 단계에서는 스택을 압축하기 위해 표시된 개체가 스택의 새로운 영역에 복사됩니다. 이 수집기는 다른 작업도 중지합니다.

(3) 복사 컬렉터

이 컬렉터는 스택을 하프스페이스라고도 불리는 두 개의 도메인으로 나눕니다. 매번 공간의 절반만 사용되며 jvm에 의해 생성된 새 객체는 공간의 나머지 절반에 배치됩니다. gc가 실행되면 도달 가능한 객체를 공간의 나머지 절반에 복사하여 스택을 압축합니다. 이 방법은 수명이 짧은 개체에 적합합니다. 수명이 긴 개체를 계속 복사하면 효율성이 떨어집니다.

(4) 증분 수집기

증분 수집기는 스택을 여러 도메인으로 나누어 한 번에 하나의 도메인에서만 쓰레기를 수집합니다. 이로 인해 약간의 애플리케이션 중단이 발생합니다.

(5) 세대별 컬렉터

이 컬렉터는 스택을 두 개 이상의 도메인으로 나누어 서로 다른 Lifespan 객체를 저장합니다. jvm에 의해 생성된 새 객체는 일반적으로 필드 중 하나에 배치됩니다. 시간이 지남에 따라 살아남은 개체는 유용한 수명을 얻고 수명이 더 긴 도메인으로 이전됩니다. 세대별 수집기는 성능을 최적화하기 위해 다양한 도메인에 대해 다양한 알고리즘을 사용합니다.

(6) Concurrent Collector

Concurrent Collector는 애플리케이션과 동시에 실행됩니다. 이러한 수집기는 일반적으로 특정 작업을 완료하기 위해 특정 시점(압축 등)에서 다른 작업을 중지해야 하지만, 다른 애플리케이션이 다른 백그라운드 작업을 수행할 수 있기 때문에 다른 처리를 중단하는 실제 시간이 크게 줄어듭니다.

(7) 병렬 수집기

병렬 수집기는 일부 기존 알고리즘을 사용하고 여러 스레드를 사용하여 작업을 병렬로 수행합니다. 다중 CPU 시스템에서 다중 스레딩 기술을 사용하면 Java 애플리케이션의 확장성을 크게 향상시킬 수 있습니다.

(3) .NET Framework 가비지 수집 메커니즘

.NET Framework에는 관리되는 힙이 모두 포함되어 있습니다. 언어는 참조 유형 객체를 할당할 때 이를 사용합니다. 값 유형과 같은 경량 객체는 항상 스택에 할당되지만 모든 클래스 인스턴스와 배열은 관리되는 힙인 메모리 풀에서 생성됩니다.

.NET 프레임워크의 가비지 수집기를 세대별 가비지 수집기(Generational Garbage Collector)라고 합니다. 이는 할당된 개체가 세 가지 범주로 나뉘거나 "세대"에 해당함을 의미합니다. 각각 0, 1, 2세대에 해당하는 관리되는 힙의 초기 크기는 각각 256K, 2M, 10M입니다. 가비지 수집기는 크기를 변경하면 성능이 향상된다고 판단되면 관리되는 힙의 크기를 변경합니다. 예를 들어 애플리케이션이 많은 작은 개체를 초기화하고 이러한 개체가 빠르게 재활용되는 경우 가비지 수집기는 0세대의 관리되는 힙을 128K로 늘리고 재활용 빈도를 높입니다. 상황이 역전되어 가비지 수집기가 0세대에서 관리되는 힙의 많은 공간을 회수할 수 없다는 사실을 발견하면 관리되는 힙 크기가 늘어납니다. 애플리케이션이 초기화될 때까지 관리되는 힙의 모든 수준은 비어 있습니다. 개체가 초기화되면 초기화된 순서대로 0세대의 관리되는 힙에 배치됩니다.

최근 메모리 공간이 할당된 개체는 0세대에 배치됩니다. 0세대는 프로세서의 두 번째 수준(L2) 캐시에 들어갈 만큼 작기 때문에 0세대는 개체에 대한 빠른 액세스를 제공할 수 있습니다. 그 안에. 한 라운드의 가비지 수집 후에는 여전히 0세대에 있는 개체가 1세대로 이동됩니다. 또 다른 가비지 수집 라운드 후에는 아직 1세대에 있는 개체가 2세대로 이동됩니다. 2세대에는 두 번 이상의 수집 라운드를 거친 수명이 긴 개체가 포함되어 있습니다.

C# 프로그램이 객체에 메모리를 할당하면 관리되는 힙이 새 객체에 필요한 메모리를 거의 즉시 반환할 수 있습니다. 관리되는 힙이 이렇게 효율적인 메모리 할당 성능을 가질 수 있는 이유입니다. 관리되는 힙은 상대적으로 간단한 데이터 구조이기 때문입니다. 관리되는 힙은 사용 가능한 첫 번째 메모리 공간에 대한 포인터가 있는 간단한 바이트 배열과 유사합니다.

객체가 블록을 요청하면 위의 포인터 값이 호출 함수에 반환되고 포인터는 다음 사용 가능한 메모리 공간을 가리키도록 재조정됩니다. 관리되는 메모리 블록을 할당하는 것은 포인터 값을 증가시키는 것보다 약간 더 복잡합니다. 이는 관리되는 힙의 성능 최적화 중 하나이기도 합니다. 많은 가비지 수집이 필요하지 않은 애플리케이션에서는 관리되는 힙이 기존 힙보다 더 나은 성능을 발휘합니다.

이러한 선형 메모리 할당 방법으로 인해 C# 애플리케이션에서 동시에 할당된 개체는 일반적으로 관리되는 힙에서 서로 인접하여 할당됩니다. 이 배열은 메모리 블록 크기를 기반으로 하는 기존의 힙 메모리 할당과 완전히 다릅니다. 예를 들어 동시에 할당된 두 개체가 힙에서 멀리 떨어져 위치하여 캐시 성능이 저하될 수 있습니다. 따라서 메모리 할당이 빠르더라도 일부 중요한 프로그램에서는 0세대에서 사용 가능한 메모리가 완전히 소모될 가능성이 높습니다. 0세대는 L2 버퍼에 들어갈 만큼 작으며 사용되지 않은 메모리는 자동으로 해제되지 않습니다. 0세대에 할당할 수 있는 유효한 메모리가 없으면 0세대에서 가비지 수집 라운드가 트리거됩니다. 이 가비지 수집 라운드에서는 더 이상 참조되지 않는 모든 개체가 삭제되고 현재 사용 중인 개체는 삭제되어 1세대로 이동되었습니다. 0세대 가비지 수집은 가장 일반적인 수집 유형이며 매우 빠릅니다. 0세대 가비지 메모리 수집이 충분한 메모리를 효과적으로 요청할 수 없는 경우 1세대 가비지 메모리 수집이 시작됩니다. 2세대 가비지 수집은 1세대 및 0세대 가비지 수집이 충분한 메모리를 제공할 수 없는 경우에만 최후의 수단으로 사용해야 합니다. 각 세대의 가비지 수집 후에도 여전히 사용 가능한 메모리가 없으면 OutOfMemeryException이 발생합니다.

(4) Java 가비지 수집 메커니즘

Java 프로그램 실행 시 메모리는 어떻게 배치되나요? 『Java 프로그래밍 사고』라는 책에는 여섯 곳이 언급되어 있습니다:

(1) Register(Register)

(2) Stack( Stack )

(3) Heap : 모든 Java 객체를 저장하는데 사용

(4) Static Storage ) : 존재하는 데이터를 저장하는데 사용 " 프로그램 실행 중". statci로 수정되었습니다.

(5) 상시 저장

(6) Non-RAM 저장 공간 : 디스크 저장 영역으로 이해하는데, 즉, 비메모리 영역.

Sun HotSpot 1.4.1은 힙을 새 도메인, 기존 도메인, 영구 도메인의 세 가지 주요 도메인으로 나누는 세대별 수집기를 사용합니다. Jvm에 의해 생성된 모든 새 개체는 새 도메인에 배치됩니다. 객체가 특정 수의 가비지 수집 주기를 거치면 수명을 획득하고 이전 도메인에 들어갑니다. 영구 도메인에서 jvm은 클래스 및 메소드 객체를 저장합니다. 구성을 위해 영구 도메인은 별도의 도메인이며 힙의 일부로 간주되지 않습니다. 이러한 관점에서 HotSpot 엔진 기술을 사용하는 JVM은 .NET 프레임워크와 유사한 가비지 수집 메커니즘인 세대별 가비지 수집 방법을 채택해야 합니다.

이러한 도메인의 크기를 제어하는 ​​방법은 다음과 같습니다. -Xms 및 -Xmx를 사용하여 전체 힙의 원래 크기 또는 최대 크기를 제어할 수 있습니다.

다음 명령은 초기 크기를 128M로 설정합니다.

java –Xms128m

- Xmx256m 새 도메인의 크기를 제어하려면 -XX:NewRatio를 사용하여 힙에서 새 도메인의 비율을 설정할 수 있습니다.

다음 명령은 전체 힙을 128m로 설정하고 새 도메인 비율을 3으로 설정합니다. 즉, 새 도메인과 기존 도메인의 비율이 1:3이고, 새 도메인은 힙의 1/4 또는 32M입니다.

java –Xms128m –Xmx128m

–XX:NewRatio =3 -XX:NewSize 및 -XX:MaxNewsize를 사용하여 새 도메인의 초기 값과 최대 값을 설정할 수 있습니다.

다음 명령은 새 도메인의 초기 및 최대값을 64m으로 설정합니다.

java –Xms256m –Xmx256m –Xmn64m

영구 도메인의 기본 크기는 4m입니다. 프로그램을 실행할 때 jvm은 필요에 맞게 영구 도메인의 크기를 조정합니다. 조정할 때마다 jvm은 힙에서 완전한 가비지 수집을 수행합니다.

영구 도메인 크기를 늘리려면 -XX:MaxPerSize 플래그를 사용하세요. WebLogic Server 애플리케이션이 더 많은 클래스를 로드하면 영구 도메인의 최대 크기를 늘려야 하는 경우가 많습니다. jvm이 클래스를 로드하면 영구 도메인의 객체가 급격히 증가하여 jvm이 영구 도메인 크기를 지속적으로 조정하게 됩니다. 조정을 방지하려면 -XX:PerSize 플래그를 사용하여 초기 값을 설정하십시오.

다음으로 영구 도메인의 초기값을 32m, 최대값을 64m로 설정합니다.

java -Xms512m -Xmx512m -Xmn128m -XX:PermSize=32m -XX:MaxPermSize=64m

기본적으로 HotSpot은 복제를 수행합니다. 컬렉터는 도메인에서 사용됩니다. 도메인은 일반적으로 세 부분으로 나누어집니다. 첫 번째 부분은 새로운 객체를 생성하는 데 사용되는 Eden입니다. 나머지 두 부분은 구조 공간이라고 합니다. Eden이 가득 차면 수집기는 애플리케이션을 중지하고 구조 공간의 전류가 가득 차면 도달 가능한 모든 개체를 현재의 구조 공간에 복사합니다. 구출 공간. 구출 공간. 구조 공간에서 역할을 교환합니다. 살아 있는 객체는 만료될 때까지 회수 공간에 복제되어 이전 도메인으로 전송됩니다. -XX:SurvivorRatio를 사용하여 새 도메인 하위 공간의 크기를 제어합니다.

SurvivorRation은 NewRation과 마찬가지로 Eden 공간에 대한 특정 구조 도메인의 비율을 지정합니다. 예를 들어 다음 명령은 새 도메인을 64m로 설정하고 Eden은 32m를 차지하고 각 복구 도메인은 16m을 차지합니다.

java -Xms256m -Xmx256m -Xmn64m -XX:SurvivorRation =2

앞에서 언급했듯이 기본적으로 HotSpot은 새 도메인에는 복사 수집기를 사용하고 이전 도메인에는 mark-sweep-compact 수집기를 사용합니다. 응용 프로그램에서 생성된 대부분의 개체는 수명이 짧기 때문에 새 도메인에서 복사 수집기를 사용하는 것은 많은 의미가 있습니다. 이상적으로는 모든 전환 객체가 에덴 공간 밖으로 이동할 때 수집됩니다. 이것이 가능하고, Eden 공간 밖으로 이동된 객체의 수명이 긴 경우, 이론적으로는 구조 공간에서 반복 복사를 피하기 위해 즉시 Old 공간으로 이동할 수 있습니다. 그러나 응용 프로그램은 중간에서 오래 지속되는 개체의 비율이 적기 때문에 이러한 이상적인 상태에 적합할 수 없습니다. 이전 도메인을 압축하는 것보다 개체의 작은 부분을 복사하는 것이 더 저렴하기 때문에 이러한 중장기 개체를 새 도메인에 유지하는 것이 좋습니다. 새 도메인의 객체 복사를 제어하려면 -XX:TargetSurvivorRatio를 사용하여 복구 공간의 비율을 제어할 수 있습니다. (이 값은 복구 공간의 사용 비율을 설정하는 값입니다. 예를 들어 복구 공간 비트는 1M, 값 50은 500K를 사용할 수 있음을 의미합니다. 값은 백분율이며 기본값은 50입니다. 더 큰 스택이 더 낮은 sruvivorratio를 사용하는 경우 구제 공간을 더 잘 활용하려면 이 값을 80~90으로 늘려야 합니다. -XX:maxtenuring 임계값을 사용하여 상한을 제어합니다.

모든 복제가 발생하고 객체가 eden에서 이전 도메인으로 확장되도록 하려면 MaxTenuring Threshold를 0으로 설정합니다. 설정이 완료되면 실제로는 Rescue 공간을 더 이상 사용하지 않으므로 Eden 공간을 최대화하려면 SurvivorRatio를 최대값으로 설정해야 합니다. 설정은 다음과 같습니다.

java ... -XX:MaxTenuringThreshold= 0 –XX:SurvivorRatio=50000 …

후기: "Java Virtual Machine 10년"에서 언급했듯이 " 지난 5년간, 즉 (JVM)은 최적화를 계속하는 방법에는 여러 가지가 있습니다. 하나는 새로운 샘플링 알고리즘을 연구하는 것입니다. 샘플링은 다양한 최적화 전략과 관련되어 있으며 상대적으로 큰 영향을 미치기 때문입니다. 전반적인 성능을 연구합니다. 세 번째는 가비지 수집 알고리즘을 연구하는 것입니다. 가비지 수집으로 인해 프로그램이 잠시 중단되어 부정적인 사용자 경험이 발생합니다. 따라서 가비지 수집의 효율성을 향상시키기 위한 다양한 알고리즘이 등장했습니다. "언어의 실행 속도와 효율성을 높이는 것은 디자이너와 개발자가 항상 추구하는 목표이므로 가비지 컬렉션 알고리즘도 시간이 지남에 따라 발전할 것입니다. 어떤 면접관도 감히 C#이나 Java의 가비지 수집 메커니즘에 대해 이야기해 달라고 요청하지는 않을 것 같습니다(적어도 저는 아직까지 접해본 적이 없습니다). 일단 토론이 심도 있게 진행되면 많은 문제로 충분합니다. 긴 책. 그러나 깊이 성찰하여 그 유래를 추적하는 것은 참으로 아름다운 일이다. 공자는 동산에 올라 작은 노가 되었고, 태산에 올라 작은 세계가 되었다.

위 내용은 Java와 C#의 가비지 컬렉션 메커니즘에 대한 자세한 소개입니다. 더 많은 관련 내용은 PHP 중국어 홈페이지(www.php.cn)를 참고해주세요!


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.