Object-Oriented Programming

2. 자바 T메모리

Planted January 7, 2023

[스프링 입문을 위한 자바 객체 지향의 원리와 이해] chapter2를 바탕으로 쓰여졌다.


자바 프로그램의 개발과 구동

현실 세계에서 소프트웨어는 개발자가 여러 개발 도구를 이용해서 개발하고, OS를 통해 하드웨어 상에서 구동되는 것이다. 그럼, 자바의 경우로 생각해보자. JDK라는 개발 도구를 이용해 개발된 프로그램은 JRE에 의해 가상 컴퓨터인 JVM 상에서 구동된다.

  • JDK : 자바 개발 도구 (Java Development Kit) → 개발도구
  • JRE : 자바 실행 환경 (Java Runtime Environmont) → OS
  • JVM : 자바 가상 기계 (Java Virtual Machine) → 하드웨어

→ JVM이라는 컴퓨터를 제어하는 OS를 JRE로 이해할 수 있고, 개발도구가 JDK인 것이다. 다만, 편의를 위해 JVM ⊂ JRE ⊂ JDK 이 상태로 배포된다.

프로그램이 메모리를 사용할 때 객체 지향 프로그램이 메모리를 사용할 때
코드 실행영역 코드 실행 영역은 동일
데이터 저장 영역 스태틱 영역, 스택 영역, 힙 영역으로 데이터 저장 영역을 분할(T메모리구조)

T메모리 구조 : Static, Stack, Heap

스태틱 영역 스택 영역 힙 영역
클래스의 놀이터 메서드들의 놀이터 객체들의 놀이터
패키지 배치됨 ‘{’ : 스택 프레임 생성, ‘}’ : 스택 프레임 소멸
클래스 배치됨 메서드 인자를 저장할 변수 공간은 스택 프레임 맨 밑에 확보

자바에 있는 절차적/구조적 프로그래밍의 유산

객체 지향 프로그래밍은 절차적/구조적 프로그램을 딛고 탄생했기에, 이를 이해하는 게 중요하다.

  • 절차적 프로그래밍의 유산 : goto를 쓰지마라!
    • 자바에의 goto는 아무 기능하지 않지만 사용하지 못하도록 이를 예약어로 등록했다.
    • cf. const는 자바에서는 이대신 final을 쓰므로 예약어로 지정됨
  • 구조적 프로그래밍의 유산 : 함수와 지역변수

※ 함수 vs 메서드 : 이 둘은 다르지 않지만, 메서드는 함수와 달리 반드시 클래스 안에 존재해야 한다.

1. 스택 프레임

a. main()

main() 메서드는 프로그램의 시작점이면서 끝이다. main()이 끝나면 JRE는 JVM을 종료하고 JRE도 우리 OS상 메모리에서도 사라진다. 이러면 당연히 앞 서 봤던 T메모리도 수명을 다하게 된다. → JVM 종료, JRE가 사용한 시스템 자원 반납, T메모리 소멸

  • main() 메서드 실행 전, JVM에서 수행하는 전처리 작업
    • java.lang 패키지 스태틱 영역에 배치
    • import된 패키지 스태틱 영역에 배치
    • 클래스 스태틱 영역에 배치

b. 블록 스택 프레임

  • 스택 프레임 내 만들어진다. 메서드 내 제어문(if…)가 있으면 생성된다.

c. “외부 스택 프레임에서 내부 스택 프레임의 변수에 접근하는 것은 불가능하나, 역은 가능하다!”

  • 코드가 하나 씩 실행되면서 T 메모리 변화를 따라가다보면, 당연하다.
    • 특정 메소드의 스택 프레임이 사라지면, 내부의 변수도 사라지는 법이다.
  • 예를 들어, main() 내에 if의 블록 스택 프레임이 생성되었다. (main의 변수m, if문의 변수k)
    • if가 생기기 전에는 당연히 메모리에 k가 없으니 접근 불가하고, if 종료된 이후에도 if 스택 프레임이 사라지므로 여전히 k에 접근 불가능한 것이다. 그래서 외부 스택 프레임에서는 내부 스택 프레임의 변수에 접근할 수 없다.
    • 역이 가능한 이유는 if가 실행되면서 if에서 main()의 변수m은 여전히 메모리에 있으므로 m에 접근이 가능하기 때문이다.

d. “메서드 스택 프레임에서 다른 메서드 스택 프레임 내부 변수에 접근할 수 없다!”

그럼, main()의 스택 프레임이 사라지기 전에 외부 스택 프레임 myMethod()에서 main()의 m변수에 접근할 수 있을까? 아니 이는 금지되었다. 왜 그럴까? 이 책의 필자가 짐작하는 이유는 다음과 같다.

메서드 스택 프레임에서 다른 메서드 스택 프레임의 내부변수에 접근할 수 없는 이유
1. 메서드는 서로의 고유 공간이다.
2. 포인터 문제 : myMethod()에서 m에 접근하려면, m의 메모리 위치를 알아야 하고, 포인터(메모리 주소값)이 필요해지기 때문이다.
3. 어느 메서드 스택 프레임의 변수에 접근해야 하는지 문제 발생 : 메서드를 호출하면서 스택 구조는 계속 변하기 때문에, 여러 스택 프레임 중 하나의 스택 프레임의 변수에 접근하려면 포인터가 일요하다.

※ 자바가 환영받은 이유 중 하나가 C/C++의 포인터가 없어졌다는 것!

2. 변수

a. main() 함수 내에 int i;라고 선언했다고 하자!

  • 그러면 스택 영역에 생성된 main() 스택 프레임 내에 i를 위한 변수 공간이 마련될것이다.
  • 여기에는 해당 공간의 메모리에 저장된 쓰레기값이 저장되어 있다.

b. 지역변수

  • 지역 변수 : 스택 메모리 내 스택 프레임에 종속 ↔ 스택 프레임 사라지면 소멸
  • 클래스 멤버 변수(전역변수) : 스태틱 영역(스택 프레임에 독립) ↔ JVM 종료 시 소멸
    • 읽기 전용으로 값 공유하는 전역 상수는 사용하는 걸 적극 추천!!
  • 객체 멤버 변수 : 힙 영역 ↔ 가비지 컬렉터(힙 메모리 회수기)에 의해 소멸

c. 반환값

  • 스택 프레임 내 반환값은 메서드가 종료되면서 반환할 값을 가지고 있는 가상의 변수

d. Call By Value

  • 값에 의한 호출 : 변수 자체가 아닌, 변수가 저장한 값을 복제해서 전달한다.
  • 다른 스택 프레임의 같은 이름의 변수는 서로 별도의 변수 공간이 마련된다.
    • 예를 들어, main()의 변수 k와 myMethod()의 변수 k는 서로 영향 주지 못하는 다른 변수 공간이다.

3. 멀티 스레드, 멀티 프로세스

a. 멀티 스레드

  • 스태틱 & 힙 역역 공유
  • 스택 영역만 스레드만큼 분할해서 사용
  • 메모리 사용 少
  • 전역 변수를 쓰면, 쓰레드의 안전성이 떨어진다.
    • [예를 들어] 값을 할당하고 출력하려는데, 그 사이에 다른 스레드에 의해 다른 모르는 값으로 수정되어 예상치 못한 값이 출력되는 상황이 발생할 수 있다.
    • 이를 보완하기 위해 lock을 걸 순 있지만, 멀티 스레드의 장점을 버린 꼴

b. 멀티 프로세스

  • 다수의 데이터 저장영역(T메모리)를 갖는 구조
  • 메모리 안전구조
  • 메모리 사용 多

참고

→ 책 읽으면서 이해가 안된 개념 따로 찾아 정리

1. STS 디버그 퍼스펙티브 (STS Debug perspective)

  • STS(Spring Tool Suite) 디버그 모드에서 breakpoint를 이용하면서 T메모리 스택 영역의 변화를 살펴볼 수 있다.
  • 이클립스나 STS 등에서 여러 perspective를 제공하는데 그 중 디버거를 사용하는 것
  • IBM 문서에 따르면,
    • 디버그 퍼스펙티브는 message flows의 그래픽 표현을 테스트하고 디버스하는 걸 말한다.
    • debug, breakpoints, variables, message flow editor view와 같은 다양한 view를 제공한다.

2. 서블릿(Servelt)

  • 웹프로그래밍에서 CGI는 요청당 프로세스를, Servlet은 요청당 스레드를 생성한다.

a. CGI(common gateway interface)

  • 공용 게이트웨이 인터페이스로, 웹서버를 외부 프로그램(application)과 연결하는 방법이다.
  • 애플리케이션 ↔ CGI ↔ Web server

b. Servlet

  • 요청당 프로세스가 아닌, 스레드를 생성하기 때문에, 가볍고 메모리 부담이 덜하다.
  • 동적 페이지를 생성하는 서버 측 프로그램으로, 자바 클래스의 일종으로 이해할 수 있다.
  • 동작 방식
    • 클라이언트로부터 요청이 오면, 요청/응답에 대한 객체를 생성한다
    • 해당 요청이 어느 서블렛에 대한 요청인지 찾아 응답객체에 동적 페이지를 담아 보낸다.
    • 이 응답을 보내면 해당 요청/응답 객체는 소멸한다.

3. 책에 나온 다이어그램

  • 메서드를 만들 때는 순서도 또는 의사코드(Pseudocode)를 작성하는 것이 좋다.

  • UML 액티비티 다이어그램

    • 프로그램의 전반적인 흐름을 파악하기에 용이하다.
  • 나시 슈나이더만 다이어그램 (NS 다이어그램; Nassi-Shneilderman diagram)

    • 프로그램의 구조를 보여준다.
    • 이 책의 필자의 추천! 이 직관적인 게 좋다!

👉 chapter 3