에덴 스페이스
그래서 내 질문은 이것이 사실 일 수 있고, 그렇다면 왜 자바의 힙 할당이 훨씬 빠르 냐는 것입니다.
Java GC가 매우 흥미로워 서 작동하는 방식에 대해 조금 연구했습니다. 나는 항상 C와 C ++에서 메모리 할당 전략 모음을 확장하려고 노력하고 있으며 (C와 비슷한 것을 구현하려는 데 관심이 있음) 많은 객체를 버스트 방식으로 버스트 방식으로 할당하는 매우 빠르고 매우 빠른 방법입니다 실용적인 관점이지만 주로 멀티 스레딩으로 인해 발생합니다.
Java GC 할당의 작동 방식은 초저가의 할당 전략을 사용하여 초기에 "Eden"공간에 객체를 할당하는 것입니다. 내가 알 수있는 것은 순차 풀 할당자를 사용하고 있습니다.
알고리즘 측면 malloc
에서 C 또는 기본값의 일반적인 용도보다 강제적 인 페이지 결함을 줄이면서 operator new
C ++을 던지는 것보다 훨씬 빠릅니다 .
그러나 순차적 할당자는 눈에 띄는 약점을 가지고 있습니다. 가변 크기 청크를 할당 할 수는 있지만 개별 청크를 해제 할 수는 없습니다. 정렬을 위해 패딩을 사용하여 연속적인 순차 방식으로 할당하며 한 번에 할당 한 모든 메모리 만 제거 할 수 있습니다. 그것들은 일반적으로 C와 C ++에서 프로그램이 시작되고 반복적으로 검색되거나 새로운 키가 추가 될 때만 빌드 해야하는 검색 트리와 같이 요소 삽입 만 필요하고 요소 제거가 필요없는 데이터 구조를 구성하는 데 유용합니다 ( 키가 제거되지 않음).
또한 요소를 제거 할 수있는 데이터 구조에도 사용할 수 있지만 이러한 요소는 개별적으로 할당을 해제 할 수 없으므로 실제로 메모리에서 해제되지 않습니다. 순차 할당자를 사용하는 이러한 구조 는 데이터가 별도의 순차 할당자를 사용하여 새로운 압축 사본으로 복사되는 지연된 패스가 없으면 (그리고 고정 할당자가이기는 경우에 매우 효과적인 기술입니다) 지연 되지 않는 한 더 많은 메모리를 소비 합니다. 어떤 이유로 든하지 마십시오. 데이터 구조의 새 사본을 순차적으로 할당하고 이전 구조의 모든 메모리를 덤프하십시오.
수집
위의 데이터 구조 / 순차 풀 예제에서와 같이 Java GC가 많은 개별 청크의 버스트 할당에 매우 빠르더라도이 방법으로 만 할당하면 큰 문제가됩니다. 소프트웨어가 종료 될 때까지 아무 것도 해제 할 수 없으며,이 시점에서 모든 메모리 풀을 한 번에 해제 (퍼지) 할 수 있습니다.
따라서 단일 GC주기 후에 "Eden"공간의 기존 객체를 통해 패스가 생성되고 (순차적으로 할당 됨) 여전히 참조 된 객체는 개별 청크를 해제 할 수있는보다 일반적인 할당기를 사용하여 할당됩니다. 더 이상 참조되지 않는 것은 제거 과정에서 단순히 할당 해제됩니다. 따라서 기본적으로 "아직 참조 된 경우 Eden 공간에서 객체를 복사 한 다음 제거"입니다.
일반적으로 비용이 많이 들기 때문에 별도의 백그라운드 스레드에서 수행되어 원래 모든 메모리를 할당 한 스레드가 크게 중단되는 것을 방지합니다.
메모리가 Eden 공간에서 복사되고 초기 GC주기 후 개별 청크를 해제 할 수있는이 더 비싼 체계를 사용하여 할당되면 객체는보다 영구적 인 메모리 영역으로 이동합니다. 그런 다음 해당 청크는 참조가 중단되면 후속 GC주기에서 해제됩니다.
속도
간단히 말해서 Java GC가 힙 할당에서 C 또는 C ++보다 성능이 우수한 이유는 메모리 할당을 요청하는 스레드에서 가장 비싸고 완전히 퇴화되지 않은 할당 전략을 사용하기 때문입니다. 그런 다음 malloc
다른 스레드에 대해 직선형과 같은보다 일반적인 할당자를 사용할 때 일반적으로 수행해야하는 비용이 많이 드는 작업을 저장합니다 .
따라서 개념적으로 GC는 실제로 전체적으로 더 많은 작업을 수행해야하지만 전체 스레드가 단일 스레드로 선불로 지불되지 않도록 스레드 전체에 분배합니다. 메모리를 할당하는 스레드가 매우 저렴하게 작업을 수행 한 다음 개별 개체를 실제로 다른 스레드로 해제 할 수 있도록 작업을 수행하는 데 필요한 실제 비용을 연기합니다. C 또는 C ++에서 malloc
또는 호출 할 때 operator new
동일한 스레드 내에서 선불 비용을 선불로 지불해야합니다.
이것이 가장 큰 차이점이며 Java가 순진한 호출을 사용 malloc
하거나 operator new
여러 개의 작은 덩어리를 개별적으로 할당 하여 C 또는 C ++보다 성능이 우수한 이유는 무엇입니까 ? 물론 GC 사이클이 시작될 때 일반적으로 약간의 원자 연산과 잠재적 잠금이 있지만, 아마도 약간 최적화되어있을 것입니다.
기본적으로 간단한 설명은 단일 스레드에서 더 많은 비용을 malloc
지불하는 것 ( )과 단일 스레드 에서 더 저렴한 비용을 지불 한 다음 병렬로 실행될 수있는 다른 스레드에 더 많은 비용을 지불하는 것으로 요약됩니다 GC
. 이 방법을 사용하는 단점은 할당자가 기존 객체 참조를 무효화하지 않고 메모리를 복사 / 이동시킬 수 있도록 객체 참조에서 객체로 가져 오는 두 가지 간접 지시가 필요하다는 것을 의미하며 객체 메모리가 일단 확보되면 공간적 로컬 성을 잃을 수 있습니다 "Eden"공간에서 나갔습니다.
마지막으로 C ++ 코드는 일반적으로 힙에 개별적으로 객체의 보트로드를 할당하지 않기 때문에 비교가 약간 불공평합니다. 적절한 C ++ 코드는 인접한 블록이나 스택의 많은 요소에 메모리를 할당하는 경향이 있습니다. 무료 상점에 한 번에 하나씩 작은 물체의 보트로드를 할당하면 코드가 혼란스러워집니다.