Static 파고들기
스태틱이란 뭔가요?
클래스의 인스턴스가 아닌 클래스 자체에 속한 멤버를 의미합니다.
일반적으로 클래스 내부 변수, 메서드는 인스턴스가 생성될때마다 개별적으로 존재하지만
스태틱으로 선언된 멤버는 프로그램 시작시 메모리에 한번에 로드되어 모든 인스턴스가 공유하게 됩니다.
그로 인해, 전역적으로 관리되어야 하는 값, 공용 함수를 스태틱으로 선언해 활용이 가능합니다.
다만 유의할 점은 스태틱 멤버의 메모리 해제 시점이 명확하지 않으므로
씬 전환시 메모리에 남아 예상치 못한 공유 문제가 발생될 수 있습니다.
스태틱 멤버는 어떤 메모리에 할당되나요?
정적 메모리 영역의 Data,BSS 각 영역에 할당되며 이는 프로그램이 종료될때까지 남아있게 됩니다.
스태틱 멤버는 클래스가 처음 활용할때 초기화되며 이후 인스턴스가 활용할 수 있게 됩니다.
Data, BSS 영역은 뭔가요?
스태틱 필드는 기본적으로 정적 메모리 영역에 할당 됩니다.
이때 정적 메모리는 좀 더 세부적으로 Data 영역과 BSS영역으로 나눠질 수 있습니다.
두 영역 다 정적 필드를 담기위한 용도이지만,
Data 영역은 선언과 동시에 특정 값으로 초기화가 된 필드를 저장하는 영역이며
BSS 영역은 스태틱 필드를 선언만 하며 특정값을 설정하지 않은 필드를 저장하는 영역입니다.
이후 두 영역에 할당된 필드들은 값 변경이 발생되더라도 메모리 상 위치가 변경되진 않습니다.
왜 두가지 영역을 따로 구별하는건가요?
첫번째는 파일 크기를 줄이기 위함입니다.
Data 영역의 특정 값으로 초기화된 스태틱 필드는 파일 안에 실제 값이 기록되며
반대로 BSS영역의 스태틱 필드는 디폴트 값이므로 메모리 할당 예약만 해두고, 파일에 값을 저장할 이유가 없습니다.
두번째는 실행 속도 향상을 위함입니다.
모든 필드에 값을 불러오지 않고 BSS영역의 값들은 이미 결정되어 있어
운영체제의 간단한 명령어로 대량의 메모리를 빠르게 디폴트 값으로 설정할 수 있어, 이러한 부분을 활용해
실행 로딩비용을 줄일 수 있습니다.
클래스를 통한 인스턴스는 힙에 할당되는데, 스태틱 멤버는 어디에 저장되나요?
인스턴스는 힙 영역에 할당됩니다. 반면 클래스 내 스태틱 멤버는 정적메모리에 할당되며
정적 메모리 내 저장하는 방식은 위 설명과 동일합니다.
그럼 스태틱 메서드는 어디에 저장되나요?
스태틱메서드는 결국 함수이므로 코드 영역에 저장됩니다.
그럼 스태틱 메서드로 할당되는 코드 영역에서도 Data,BSS처럼 나눠지는게 있을까요?
영역이 따로 나눠져 있지 않습니다. 코드는 단지 CPU가 실행할 명령어이기 때문입니다.
즉, 컴파일러가 만드는 모든 코드는 애초에 초기화되어 있으므로 따로 구분할 필요가 없습니다.
'어딘가에는 이 정적메모리 주소를 가리키는 필드가 있다' 라는 것 같은데, 그 주소관리는, 어디서, 어떻게 되는건가요?
우선 해당 메모리 주소를 직접 관리할 수 없습니다.
이 메모리 관리는 컴파일러, 런타임 시스템이 알아서관리하는 것으로 알고 있습니다.
구체적인 과정은 프로그램이 빌드될때, 이 멤버들을 위한 메모리를 할당하고, 고정된 주소를 할당합니다.
이후 프로그램 코드에서는 변수 이름을 통해 접근하지만, 내부적으론 고정된 주소를 활용하는 방법입니다.
만약 이 영역의 할당량이 너무 많을경우 어떤일이 발생되나요?
정적 메모리 할당량 초과 라는 개념이 있긴하지만, 현대 시스템에선 거의 발생하지 않습니다.
다만, 이 영역 할당량이 클 경우 실행파일의 크기가 커지는 문제와 운영체제에서 확보해야 할 메모리가 커지므로
실행 실패가 발생되거나 성능저하 문제로 이어질 수 있습니다.
정적 메모리 영역 자체는 프로그램마다 크기가 제한되어 있지 않고
운영체제는 프로그램이 요구하는 만큼의 정적 메모리 영역을 확보해 주려합니다.
32비트, 64비트로 크게 나눌 수 있고 운영체제 마다 한 프로세스가 사용할 수 있는 최대 가상메모리 공간은 제한되어 있습니다.
'현대 시스템에선 거의 문제가 없다?'라는 말은 그렇다면 코드 대부분을 스태틱을 활용해도 괜찮다. 라고 이해하면 되나요?
이는 하드웨어 측면에서 특정 문제가 발생할 가능성이 낮다의 의미이며 무분별한 사용은 지양해야 합니다.
그 이유는 메모리적 측면으로 시작과 끝까지 메모리가 살아있게 되므로
이는 메모리 회수가 불가능하며 사용되지 않는 상황을 고려할때, 메모리 측면으로 비효율적인 방식입니다.
또한 구조 문제로 전역적인 접근은 코드의 유연성을 낮출수 있고, 이 문제는 테스트, 유지보수, 확장성 측면의 불리함으로 이어질 수 있습니다.