군만두의 IT 공부 일지

[스터디6] 02. 자바와 절차적/구조적 프로그래밍 본문

프로그래밍/객체지향

[스터디6] 02. 자바와 절차적/구조적 프로그래밍

mandus 2025. 3. 7. 17:21

02. 자바와 절차적/구조적 프로그래밍

  • 자바: 객체지향 언어이면서 근본적으로는 프로그래밍 언어

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

자바 개발 환경을 이해하려면 JVM(Java Virtual Machine)의 존재와 역할을 아는 것이 필수다.

 

현실 세계 가상 세계(자바 월드)
소프트웨어 개발 도구 JDK - 자바 개발 도구 JVM용 소프트웨어 개발 도구
운영체제 JRE - 자바 실행 환경 JVM용 OS
하드웨어 - 물리적 컴퓨터 JVM - 자바 가상 기계 가상의 컴퓨터

▲ 현실 세계 vs. 가상 세계

  • JDK(Java Development Kit): 자바 소스 컴파일러인 javac.exe 포함
  • JRE(Java Runtime Environment): 자바 프로그램 실행기인 java.exe 포함
  • 자바에서 위와 같은 구조를 택한 이유는 기존 언어로 작성한 프로그램은 각 플랫폼(하드웨어와 OS의 조합)용으로 배포되는 설치 파일을 따로 준비하는 불편함을 없애기 위해서다.
  • 자바의 특성: Write Once Run Anywhere
코드 실행 영역 데이터 저장 영역

▲ 프로그램이 메모리를 사용하는 방식
 

코드 실행 영역 스태틱(Static) 영역
스택(Stack) 영역 힙(Heap) 영역

▲ 객체 지향 프로그램의 메모리 사용 방식

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

  • 절차적 프로그래밍을 한마디로 표현하면 goto를 쓰지 말라는 것이다.
  • 자바 공식 문서에서 goto와 const는 not used(사용 안 함)로 되어 있다.
    • goto를 사용하면 프로그램의 실행 순서가 인간이 이해하기에 너무 복잡해질 가능성이 있다.
    • 다른 언어의 const 역할을 자바에서는 final이 대신한다.
abstract continue for new switch
assert default goto package synchronized
boolean do if private this
break double implements protected throw
byte else import public throws
case enum instanceof return transient
catch extends int short try
char final interface static void
class finally long strictfp volatile
const float native super while

▲ 자바의 예약어

  • 구조적 프로그래밍함수를 쓰라는 것이다.
    • 중복 코드를 한 곳에 모아 관리할 수 있다.
    • 논리를 함수 단위로 분리해서 이해하기 쉬운 코드를 작성할 수 있다.
    • 공유 사용 시 문제가 발생하기 쉬운 전역 변수보다는 지역 변수를 써야 한다.

객체지향 언어에서 절차적/구조적 프로그래밍의 유산은 메서드 안에서 확인할 수 있다. 제어문이 존재할 수 있는 유일한 공간이 메서드 내부이기 때문이다.

 

Q. 함수(Function)와 메서드(Method)는 무엇이 다를까?
A. 함수는 클래스나 객체와 아무 관계가 없지만, 메서드는 반드시 클래스 정의 안에 존재해야 한다.

 

abstract continue for new switch
assert default goto package synchronized
boolean do if private this
break double implements protected throw
byte else import public throws
case enum instanceof return transient
catch extends int short try
char final interface static void
class finally long strictfp volatile
const float native super while

▲ 자바에 존재하는 절차적/구조적 프로그래밍의 유산

다시 보는 main() 메서드: 메서드 스택 프레임

  • main() 메서드: 프로그램이 실행되는 시작점
public class Start {
    public static void main(String[] args) {
        System.out.println("Hello OOP!!!");
    }
}
스태틱 영역 - 클래스들의 놀이터
스택 영역 - 메서드들의 놀이터 힙 영역 - 객체들의 놀이터

▲ T 메모리 구조

  • main() 메서드가 실행되기 전 JVM에서 수행하는 전처리 작업들
    1. java.lang 패키지를 T 메모리의 스태틱 영역에 배치한다.
    2. import된 패키지를 T 메모리의 스태틱 영역에 배치한다.
    3. 프로그램 상의 모든 클래스 T 메모리의 스태틱 영역에 배치한다.
  • 이제 System.out.println("Hello OOP!!!") 구문을 실행하게 될까?
    1. 스택 프레임(stack)이 스택 영역에 할당된다.
    2. 메서드의 인자 args를 저장할 변수 공간을 스택 프레임의 맨 밑에 할당한다.
    3. T 메모리를 구성하고 나면 main() 메서드 안의 첫 명령문을 실행하게 된다.
  • System.out.println("Hello OOP!!!") 구문이 실행되면
    • T 메모리에는 변화가 없다.
핵심 내용 정리
- T 메모리 구조
- java.lang 패키지
- import 패키지와 클래스들
- 메서드 스택 프레임
- JVM
- JRE

 
힙 영역은 중요한 영역이지만 2장에서는 다루지 않는다.

2. 변수와 메모리: 변수! 너 어디 있니?

public class Start2 {
    public static void main(String[] args) {
        int i;
        i = 10;
        
        double d = 20.0;
    }
}
  • main() 메서드 스택 안에 밑에서부터 변수 i를 위한 공간을 마련할 때, 현재 변수 i에 저장된 값은 알 수 없는 값이다.
  • 선언만 하고 10으로 초기화하지 않으면, i 변수를 사용하는 코드에서 자바 컴파일러(javac)는 오류를 발생시킨다.
  • 닫는 중괄호로 main() 메서드 스택 프레임이 스택 영역에서 사라진다. (프로그램 종료)

3. 블록 구문과 메모리: 블록 스택 프레임

public class Start3 {
    public static void main(String[] args) {
        int i = 10;
        int k = 20;
    
        if(i == 10) {
            int m = k + 5;
            k = m;
        } else {
            int p = k + 10;
            k = p;
        }
    
        //k = m + p;	// 컴파일 오류 발생
    }
}
  • if는 조건에 따라 분기를 일으킬 것이다.
  • 여는 중괄호를 만나면 스택 프레임이 시작된다고 했는데, 여기서 만들어지는 스택 프레임은 메서드의 스택 프레임이 아니라 if문, 참인 블록의 스택 프레임이다.
  • if 블록 중 참일 때의 블록을 종료하는 닫는 중괄호를 만나면 if 블록 스택 프레임은 스택 영역에서 사라진다.
  • main() 메서드 스택 프레임을 소멸시키는 블록 마침 기호인 닫는 중괄호를 만나면 T 메모리 소멸, JVM 기동 중지, JRE가 사용했던 시스템 자원을 운영체제에 반납하게 된다.

4. 지역 변수와 메모리: 스택 프레임에 갇혔어요!

Q. T 메모리는 세 개의 영역이 있는데 변수는 스태틱 영역, 스택 영역, 힙 영역 중 어디에 있는 걸까?
A. 세 군데에 모두
  • 지역 변수:
    • 스택 영역에서, 그것도 스택 프레임 안에서 일생을 보낸다.
    • 스택 프레임이 사라지면 함께 사라진다.
  • 클래스 멤버 변수:
    • 스태틱 영역에서 일생을 보낸다.
    • JVM이 종료될 때까지 고정된(static) 상태로 그 자리를 지킨다.
  • 객체 멤버 변수:
    • 에서 일생을 보낸다.
    • 객체와 함께 가비지 컬렉터라고 하는 힙 메모리 회수기에 의해 일생을 마친다.
외부 스택 프레임에서 내부 스택 프레임의 변수에 접근하는 것은 불가능하나 그 역은 가능하다. → if 블록 스택 프레임 예

5. 메서드 호출과 메모리: 메서드 스택 프레임 2

public class Start4 {
    public static void main(String[] args] {
        int k = 5;
        int m;
        
        m = square(k);
    }
    
    private static int square(int k) {
        int result;
        
        k = 25;
        
        result = k;
        
        return result;
    }
}
  1. square() 메서드를 호출하면 제어 흐림이 square() 메서드가 선언된 줄로 이동할 것이다.
  2. 메서드 호출이 일어나면 메서드의 스택 프레임이 T 메모리 스택 영역에 새로 생성된다.
  3. square() 메서드 스택 프레임에는 반환값을 저장할 변수 공간이 맨 아래, 인자를 저장할 변수 공간, 메서드의 지역 변수가 자리 잡는다.
  4. square() 메서드 안의 k 변수에 무슨 짓을 해도 main() 메서드 안의 k 변수는 영향이 없다.
    • main() 메서드가 가진 변수 k와 square() 메서드가 가진 변수 k는 이름만 같고 서로 별도의 변수 공간이다.
    • Call By Value(값에 의한 호출)
  5. result 변수에 25라는 값이 저장디고, 호출한 쪽에 돌려줄 값을 가져야 할 반환값 변수에 result 변수에 담긴 값이 복사될 것이다.
  6. 닫는 중괄호를 만나면 square() 메서드 스택 프레임은 스택에서 사라진다. 반환값이 있으니 그 값을 돌려주면서 사라진다.
  7. 닫는 중활호를 만나면 스택 프레임 하나가 또 사라지면서 main() 메서드 스택 프레임을 메모리에서 사라지게 하고, 모든 프로그램이 종료된다.
Q. main() 메서드의 어디에선가 square() 메서드 내의 지역 변수 result에 직접 접근할 수 있을까? 반대로 square() 메서드의 어디에선가 main() 메서드의 지역 변수 m에 직접 접근할 수 있을까?
A. 절대 접근할 수 없다.

6. 전역 변수와 메모리: 전역 변수 쓰지 말라니까요!

두 메서드 사이에 값을 전달하는 방법

  • 메서드를 호출할 때 메서드의 인자를 이용하는 방법
  • 메서드를 종료할 때 반환값을 넘겨주는 방법
  • 전역 변수를 사용하는 방법
public class Start5 {
    static int share;
    
    public static void main(String[] args) {
         share = 55;
         
         int k = fun(5, 7);
         
         System.out.println(share);
    }
    
    private static int fun(int m, int p) {
         share = m + p;
         
         return m - p;
    }
}
  • share 변수는 T 메모리의 스태틱 영역에 변수 공간이 할당된다.
  • main() 메서드 스택 프레임에 k 변수 공간이 만들어진다.
  • fun() 메서드 스택 프레임이 생성되고 인자값들과 반환값을 저장할 변수 공간도 생긴다.
  • 메서드 밖에서 선언된 변수 share는 메서드들 사이에서 공유해서 사용할 수 있는 전역 변수가 된다.

지역 변수와 전역 변수의 특징

  • 스택 프레임에 종속적인 지역 변수
  • 스택 프레임에 독립적인 전역 변수

전역 변수를 쓰지 말라고 하는 이유

  • 프로젝트 규모에 따라 코드가 커지면서 여러 메서드에서 전역 변수의 값을 변경하기 시작하면, T 메모리로 추적하지 않는 이상 전역 변수에 저장되어 있는 값을 파악하기 쉽지 않다.
  • 읽기 전용으로 값을 공유해서 전역 상수로 쓰는 것은 좋다.

7. 멀티 스레드 / 멀티 프로세스의 이해

  • 멀티 스레드(Multi Thread)의 T 메모리 모델: 스택 영역을 스레드 개수만큼 분할해서 쓰는 것
스태틱 영역
스택 영역 - 메서드들의 놀이터 힙 영역
스레드 스레드

▲ 멀티 스레드는 스택 영역을 스레드 개수만큼 분할해서 사용

  • 멀티 프로세스(Multi Process): 다수의 데이터 저장 영역, 즉 다수의 T 메모리를 갖는 구조
    • 장점: 하나의 프로세스가 다른 프로세스의 T 메모리 영역을 절대 침범할 수 없는 메모리 안전한 구조이다.
    • 단점: 메모리 사용량은 크다.
  • 멀티 스레드:
    • 하나의 T 메모리 안에서 스택 영역만 분할한 것이기 때문에, 하나의 스레드에서 다른 스레드의 스택 영역에는 접근할 수 없다.
    • 스태틱 영역과 힙 영역은 공유해서 사용하는 구조다.

 

이 글은 『스프링 입문을 위한 자바 객체 지향의 원리와 이해』 책을 학습한 내용을 정리한 것입니다.
Comments