논리 주소 vs 물리 주소
CPU가 주소를 생성하면 그건 진짜 주소가 아니라 "논리 주소" 이다. 반면 진짜 RAM에서의 위치를 "물리 주소" 라고 한다.
프로그램이 메모리를 사용하려 할 때, CPU는 논리 주소를 생성하고 이걸 실제 물리 주소로 바꾸는 작업이 필요한데, 이 작업을 해주는 게 MMU(Memory Management Unit)이라는 하드웨어다.
논리 주소와 물리 주소는 보통 다르고 매핑을 통해 연결된다. 예를 들어, CPU가 346번지를 접근하려 할 때 운영체제가 "너는 14000부터 써" 라고 했다면 실제 접근 위치는 14346이 된다. 이 때 14000이라는 값을 재배치 레지스터라고 한다.
동적 적재
지금까지의 설명은 모두 프로세스가 실행되려면 메모리에 전부 올라와 있어야 한다는 전제였다.
그런데 그렇게 하면 프로세스 크기가 메모리 크기보다 커진다면 실행이 불가능해진다.
이 때 필요한 게 Dynamic Loading, 동적 적재다. 동적 적재는 루틴(코드 조각?)이 필요할 때만 메모리에 올리는 방식이다.
처음부터 다 올리는 게 아니라, 필요하면 그 때 디스크에서 메모리로 복사한다.
예를 들어 main함수가 실행되다 다른 함수를 호출하면, 그 루틴이 메모리에 없으면 디스크에서 로드해서 메모리에 적재한다.
이 작업을 수행하는 걸 linking loader(연결 적재기)라고 하며, 함수 호출 시 마다 필요한 코드를 찾아서 메모리에 올려준다.
장점은, 라이브러리 함수가 많은 프로그램에 유용하다.
단점은 실행 도중 필요한 루틴을 찾고 옮겨야 하니까 성능에 살짝 손해가 있으며, OS의 자원이 필요하다는 점이 있다.
일반적으로 JAVA에서 지역 변수나 매개변수같은 일시적인 값들은 Stack에 저장되고,
new나 컬렉션으로 동적으로 생성한 객체들은 Heap에 올라가며, 이 힙 영역은 GC가 자동으로 관리한다.
반면, 전역 변수나 static 변수는 프로그램 시작 시 메모리에 할당되어 종료까지 유지되는 데이터 영역에 존재한다.
Spring 프레임워크에서는 @Bean, @Component 등으로 등록된 싱글톤 객체들이 힙 메모리에 생성되며, 실제 메서드 호출 시 해당 호출 흐름만 스택에 잠시 올라간다.
GC는 reference가 있는 객체는 수거하지 않아서, 스프링 컨테이너가 빈을 강하게 참조하고 있기 때문에 빈들을 수거하지 못한다.
여담인데 Lazy Loading과 비슷한 거 같다. 메모리 주소를 계획만 해놓고, 메모리 공간을 미리 확보해두진 않는 점에서 다르긴 하다.
동적 연결
프로그램이 실행되면서 시스템 라이브러리(DLL 등)를 나중에 연결하는 방식이다.
반대 개념은 정적 연결이 있는데, 이건 컴파일 할 때 라이브러리 코드를 아예 집어넣는 방식이다.
동적 연결결은 실행할 때 필요한 함수만 그 때 연결하고 + 로딩한다.
예를 들어 C 프로그램이 printf를 사용하면, 이건 실제로는 C 표준 라이브러리에 있다 -> 실행 중에 그 라이브러리에서 printf 함수만 꺼내어서 연결한다
장점으로는, 메모리가 절약된다. 모든 프로세스가 같은 DLL 하나만 공유 가능하다. 또한 그 덕에 업데이트가 편하다는 점이 있다.
단점으로는, OS가 버전 관리, 공유, 메모리 보호 모든 걸 다 해줘야 한다. DLL 교체 시 다른 프로그램이 깨질 수 있어서 버전 호환성에 신경써야 한다.
JAVA 하나 설치해놓고 필요한 거(collection같은거) 다 import해서 가져다 사용하는 느낌이다.
'독서기록장' 카테고리의 다른 글
8. 운영체제 9장 메모리 (3) 연속 메모리 할당 (0) | 2025.03.30 |
---|---|
6. 운영체제 9장 메모리 (1) with redis.. (0) | 2025.03.28 |
5. Real MySQL 8장 인덱스를 잘 걸자 (0) | 2025.03.20 |