나는 내 기억력을 믿지 않는다

[기술] 힙과 스택, 박싱과 언박싱, SOLID원칙

박도치 2024. 2. 21. 23:35

힙과 스택

 

스택 - 스택은 값 형식이 저장되며 프로그램을 실행하는데 필요한 메모리 공간으로 메소드가 호출되는데 필요한 메모리가 스택에 저장된다. 여기서 필요한 메모리는 메소드가 사용하는 코드들을 말한다. 컴퓨터의 메모리에는 한계가 있기 때문에 코드를 마음대로 메모리에 저장하게 되면 메모리의 한계인 Stack Overflow에 부딪혀 더 이상 정보를 저장할 수 없게된다. 그래서 메모리를 효율적으로 사용하기 위해 함수의 호출이 종료되면 그에 따른 스택 메모리도 자동으로 해체되며 LIFO 방식으로 가장 나중에 저장된 값을 반환하게 된다.

 

- 힙은 참조 형식이 저장되며 new키워드를 이용해서 생성한 메모리가 저장된다. 그래서 메소드가 아닌 전역변수도 힙에 저장된다. 배열, 컬렉션 등등 new로 생성하기 때문에 마찬가지로 힙에 저장된다. 힙은 메모리 제한이 없기 때문에 클래스도 힙에 저장된다. 스택 메모리에서 메모리주소를 할당하고 그 주소는 힙 메모리를 가르키는 지정표가 되어 힙 메모리에서 주소에 맞는 값에 접한다. 스택에서는 LIFO 방식으로 메모리를 삭제하지만, 힙 에서는 힙의 값을 가리키는 주소가 스택에 저장되어있지 않으면 값은 존재하지만 접근이 불가능하여 GC가 삭제하게 되지만, GC가 언제 삭제하게 할지 모릅니다. 데이터의 복구가 가능한 이유 중 하나가 힙 메모리 때문이며 어떠한 정보를 삭제할 때 그 정보가 아직 힙에 저장되어 있다면 컴퓨터는 스택 메모리 안의 해당 주소값만 삭제하게 된다. 그래서 주소가 없어서 접근은 불가능 하지만 정보는 힙에 살아있게 된다. 

 

요약하자면 아래와 같다.

 

스택은 "접근 속도"가 빠르고, "메모리 크기"에 제한이 있다, "삭제"는 LIFO방식으로 삭제되며, "변수 접근"이 해당 메소드 내에서만 접근이 가능하다. 힙은 "접근 속도"가 상대적으로 느리다. "메모리 크기"에는 제한이 없고, "삭제"는 GC가 알아서 삭제한다. "변수 접근"이 전역적으로 접근이 가능하다.

 

 

박싱과 언박싱

 

값 형식의 데이터를 참조 형식으로 변환하는 것을 박싱이라고 하며 그 반대를 언박싱이라고 한다.

 

값 형식은 스택 메모리에, 참조 형식은 힙 메모리에 저장된다.

 

박싱은 스택에 있는 데이터를 힙으로 복사하는 작업이며 언박싱은 힙에 있던 데이터를 스택으로 복사하는 과정이다. 다른 타입이나 타입보다 작은 범위로 언박싱하게되면 오류가 발생하기 때문에 is연산자를 통해 타입을 체크해주는 것이 중요하다.

 

박싱의 순서는

1. 힙 영역에 새로운 메모리를 할당하고,

2. 스택의 값을 힙 메모리로 복사한 뒤,

3. 힙 메모리의 주소 값을 갖는 새로운 스택 메모리를 할당한다.

 

박싱은 하나의 값을 옮기는데도 메모리 참조가 많이 일어나서 시간적 오버헤드가 발생한다.

 

언박싱도 비슷하지만 추가로 가비지를 생성하여 가비지컬렉션을 동작시키는 잠재적 오버헤드까지 가지고있다. 그래서 박싱과 언박싱은 지양하는 것이 좋다.

 

 

SOLID원칙

 

SOLID 원칙은

 

  • 단일 책임 원칙(SRP : Single Responsibility Principle),
  • 개방 폐쇄 원칙(OCP : Open/Closed Principle),
  • 리스코프 치환 원칙(LSP : Liskov’s Substitution Principle),
  • 인터페이스 분리 원칙(ISP : Interface Segregation Principle),
  • 의존관계 역전 원칙(DIP : Dependency Inversion Principle)의 앞 글자를 딴 객체 지향 프로그래밍의 5가지 원칙이다

 

단일 책임 원칙

 

단일 책임 원칙은 하나의 클래스는 하나의 책임만 가져야 한다는 것이다. 만일 하나의 클래스에 기능(책임)이 여러개 있다면 기능 변경(수정)이 일어났을 때 수정해야 할 코드가 많아지므로, 단일 책임 원칙의 목적은 프로그램의 유지보수성을 높이기 위함이다.

 

개방 폐쇄 원칙

 

개방 폐쇄 원칙은 확장에는 열려 있으나, 변경에는 닫혀있어야 한다는 원칙이다. 기능 추가가 필요하면 클래스 확장을 통해 손쉽게 구현하면서, 확장에 따른 클래스 수정은 최소화하도록 프로그램을 작성해야 하는 설계 기법이다.

 

리스코프 치환 원칙

 

리스코프 치환 원칙은 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다는 원칙이다. 다형성 이용을 위해 부모 타입으로 메서드를 실행해도 의도대로 실행되도록 구성해줘야 하는 원칙이다.

 

인터페이스 분리 원칙

 

인터페이스 분리 원칙은 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다는 원칙이다. 단일 책임 원칙이 클래스의 단일 책임을 강조한다면, 인터페이스 분리 원칙은 인터페이스의 단일 책임을 강조하는 것으로 보면 된다.

 

의존 관계 역전 원칙

 

마지막으로 의존 관계 역전 원칙은 구체화에 의존하면 안 되고, 추상화에 의존해야 한다는 것이다. 의존 관계 역전 원칙의 지향점은 각 클래스 간의 결합도를 낮추는 것이다. 쉽게 말해 어떤 클래스를 참조해서 사용해야 하는 상황이 생긴다면, 그 클래스를 직접 참조하는 것이 아니라 그 대상의 상위 요소(추상 클래스나 인터페이스)로 참조하라는 원칙이다.