C에서는 malloc()
힙에 메모리 영역을 할당하고 이에 대한 포인터를 반환합니다. 그게 다야 메모리가 초기화되지 않았으며 메모리가 모두 0이거나 다른 것을 보증하지 않습니다.
Java에서 호출 new
은과 같은 힙 기반 할당을 수행 malloc()
하지만 추가 편의성 (또는 원하는 경우 오버 헤드)도 많이 얻습니다. 예를 들어 할당 할 바이트 수를 명시 적으로 지정할 필요가 없습니다. 컴파일러는 할당하려는 객체의 유형에 따라이를 파악합니다. 또한 객체 생성자가 호출됩니다 (초기화 방법을 제어하려는 경우 인수를 전달할 수 있음). new
반환 되면 초기화 된 객체가 보장됩니다.
그러나 네, 전화 모두의 결과의 끝에 malloc()
와 것은 new
단순히 힙 기반 데이터의 일부 덩어리에 대한 포인터입니다.
질문의 두 번째 부분은 스택과 힙의 차이점에 대해 묻습니다. 컴파일러 디자인에 대한 과정을 읽거나 책을 읽음으로써 훨씬 더 포괄적 인 답변을 찾을 수 있습니다. 운영 체제에 대한 과정도 도움이 될 것입니다. 스택 및 힙에 대한 SO에 대한 많은 질문과 답변도 있습니다.
그렇게 말하면서, 나는 너무 장황하지 않기를 바라는 일반적인 개요를 줄 것이며 상당히 높은 수준에서 차이점을 설명하는 것을 목표로합니다.
기본적으로 두 개의 메모리 관리 시스템, 즉 힙과 스택을 갖는 주된 이유는 효율성 때문 입니다. 두 번째 이유는 각각이 다른 유형보다 특정 유형의 문제에서 더 낫기 때문입니다.
스택은 개념으로 이해하기가 다소 쉬우므로 스택부터 시작합니다. C 에서이 기능을 고려해 봅시다 ...
int add(int lhs, int rhs) {
int result = lhs + rhs;
return result;
}
위의 내용은 매우 간단합니다. 우리는 이름이 붙은 함수를 정의 add()
하고 왼쪽과 오른쪽에 추가합니다. 이 함수는 그것들을 더하고 결과를 반환합니다. 발생할 수있는 오버플로와 같은 모든 대소 문자를 무시하십시오.이 시점에서 논의와 밀접한 관련이 없습니다.
이 add()
기능의 목적은 매우 간단 해 보이지만 수명주기에 대해 무엇을 알 수 있습니까? 특히 메모리 사용이 필요합니까?
가장 중요한 것은, 컴파일러는 데이터 유형이 얼마나 크며 얼마나 많은 데이터를 사용할 것인지에 대한 우선 순위 (컴파일 타임)를 알고 있다는 것입니다. lhs
및 rhs
인수는 sizeof(int)
4 각 바이트. 변수 result
도 sizeof(int)
입니다. 컴파일러는 add()
함수가 4 bytes * 3 ints
총 12 바이트의 메모리를 사용함을 알 수 있습니다 .
때 add()
함수가 호출 될 때, 하드웨어 레지스터는 호출 스택 포인터는 스택의 상단을 가리 거기에 주소를해야합니다. add()
함수를 실행하는 데 필요한 메모리를 할당하기 위해 모든 함수 입력 코드는 하나의 단일 어셈블리 언어 명령을 실행하여 스택 포인터 레지스터 값을 12 씩 줄입니다. 이렇게하면 스택에 3 개의 스토리지가 생성됩니다. ints
하나 각 lhs
, rhs
및 result
. 단일 명령을 실행하여 필요한 메모리 공간을 확보하는 것은 단일 클럭이 1 클록 틱 (1 GHz CPU에서 10 억분의 1 초)으로 실행되는 경향이 있기 때문에 속도면에서 엄청난 승리입니다.
또한 컴파일러의 관점에서 배열을 인덱싱하는 것처럼 보이는 변수에 대한 맵을 만들 수 있습니다.
lhs: ((int *)stack_pointer_register)[0]
rhs: ((int *)stack_pointer_register)[1]
result: ((int *)stack_pointer_register)[2]
다시,이 모든 것이 매우 빠릅니다.
add()
기능이 종료 되면 정리해야합니다. 스택 포인터 레지스터에서 12 바이트를 빼서이 작업을 수행합니다. 호출과 비슷 free()
하지만 하나의 CPU 명령어 만 사용하며 하나의 틱만 걸립니다. 매우 빠릅니다.
이제 힙 기반 할당을 고려하십시오. 이것은 필요한 메모리 양을 사전에 알지 못할 때 작동 합니다 (즉, 런타임시에만 배울 것입니다).
이 기능을 고려하십시오.
int addRandom(int count) {
int numberOfBytesToAllocate = sizeof(int) * count;
int *array = malloc(numberOfBytesToAllocate);
int result = 0;
if array != NULL {
for (i = 0; i < count; ++i) {
array[i] = (int) random();
result += array[i];
}
free(array);
}
return result;
}
이 addRandom()
함수는 컴파일 타임에 count
인수 의 값이 무엇인지 알지 못합니다 . 이 때문에 다음 array
과 같이 스택에 넣을 때와 같이 정의하려고 시도하는 것이 합리적이지 않습니다 .
int array[count];
경우 count
거대 그것은 우리의 스택이 너무 크고 덮어 쓰기 다른 프로그램 세그먼트를 성장의 원인이 될 수 있습니다. 이 스택 오버플로 가 발생 하면 프로그램이 충돌합니다.
따라서 런타임까지 필요한 메모리 양을 모르는 경우을 사용 malloc()
합니다. 그런 다음 필요할 때 필요한 바이트 수를 물어볼 수 있고, malloc()
그 바이트를 많이 공급할 수 있는지 확인합니다. 그것이 가능하다면, 우리는 그것을 얻습니다. 그렇지 않다면, malloc()
실패한 호출을 알려주는 NULL 포인터를 얻습니다 . 그러나 프로그램이 충돌하지 않습니다! 물론 프로그래머는 리소스 할당에 실패하면 프로그램을 실행할 수 없다고 결정할 수 있지만 프로그래머가 시작한 종료는 가짜 충돌과 다릅니다.
이제 효율성을 보러 돌아와야합니다. 스택 할당자는 매우 빠릅니다. 할당하는 명령 하나, 할당 해제 할 명령 하나는 컴파일러에 의해 수행되지만 스택은 알려진 크기의 로컬 변수와 같은 것을 의미하므로 상당히 작습니다.
반면 힙 할당자는 몇 배 더 느립니다. 사용자가 원하는 메모리 양을 공급할 수있는 충분한 여유 메모리가 있는지 확인하려면 테이블을 찾아야합니다. 다른 사람이 해당 블록을 사용할 수 없도록하기 위해 메모리를 공급 한 후에 해당 테이블을 업데이트해야합니다 (이 부기에는 할당자가 할당하려는 것 외에 할당자가 자체적 으로 메모리를 예약해야 할 수도 있음 ). 할당자는 스레드 안전 방식으로 메모리를 공급할 수 있도록 잠금 전략을 사용해야합니다. 그리고 기억이 마침내free()
d는 다른 시간에 발생하며 일반적으로 예측할 수있는 순서로 발생하지 않습니다. 할당자는 연속적인 블록을 찾아 다시 합쳐서 힙 조각화를 복구해야합니다. 그것이 모든 것을 달성하기 위해 단일 CPU 명령 이상을 필요로하는 것처럼 들리면 맞습니다! 매우 복잡하고 시간이 걸립니다.
그러나 힙이 큽니다. 스택보다 훨씬 큽니다. 우리는 그들로부터 많은 메모리를 얻을 수 있으며 컴파일 타임에 얼마나 많은 메모리가 필요한지 알지 못할 때 좋습니다. 그래서 우리는 너무 큰 것을 할당하려고 할 때 충돌하는 대신 정중하게 거절하는 관리되는 메모리 시스템의 속도를 상쇄합니다.
귀하의 질문에 대한 답변이 되었기를 바랍니다. 위의 내용 중 어느 것이라도 명확하게 알려면 알려주십시오.