재귀 호출과 jvm의 구조

최준호·2021년 7월 30일
0

java

목록 보기
11/25

재귀 호출(recursive call)이란

재귀 호출이란 method를 설계할 때 자기 자신을 다시 불러서 사용한다라는 개념인데 코드로 보자면

class Test{
    public static void main(String[] args){
    	num(5);
    }
    
    public static void num(int n){
    	if(n==0) return;
        System.out.println(n);
        num(--n);
    }
}

코드를 실행하면 5부터 1까지 프린트되는 걸 확인할 수 있다.
num이란 메서드 안에서 num을 또 호출하는 것인데 이는 조건문을 통해 종료시켜주는게 무조건 필요하다.

jvm과 재귀 호출 구조 이해

jvm의 자세한 구조는 인터넷을 검색해서 보는게 더 빠를 것이다. 나는 재귀 호출과 관련하여 jvm에 구조에 대해 왜 이해해야 하는지를 설명하려고 한다.

jvm은 우리가 프로그램을 실행 했을때 제일 먼저 하는 일로 class에 main을 찾는 것이다. java를 가장 배울 때 가장 먼저 만나게 되는 코드는

publc void main(String[] args){
}

이 코드일 것이다. 여기 안에서 우리는 메서드를 호출도 하고 객체를 만들기도 한다.

그럼 jvm은 main을 가장 먼저 찾아서 무엇을 하려는걸까?

jvm은 java를 실행하기 위한 각 영역을 지정하여 사용하고 있는데. 크게 5가지로 분류되어진다. (Method, Heap, call stack, pc register, native method stack)

JVM 이란

자세한 정보는 위 링크에서 확인하고 넘어가자.

재귀 호출을 이해하기 위한 부분은 call stack 영역을 확인하면 되는데. 이 call stack이란 우리가 프로그램을 작성하여 실행하였을 때 사용되는 메서드와 지역 변수, 연산 등 한번 처리하고 버려지는 데이터들의 메모리 영역이라고 생각하면 된다.

그렇다면 main과 call stack이 무슨 관련이 있을까?

위에서 말한 것처럼 jvm은 java의 main을 먼저 찾았다. 그런 다음 main을 stack 영역에 다음과 같이 저장한다.

또한 여기서 stack이란 개념에 대해 이해하는 것이 필요한데. stack이란 LIFO(Last in First out)으로 가장 나중에 들어온 값이 처음으로 나온다는 말이다. stack이란 자료구조에서는 또한 데이터를 가지고 왔을때 해당 데이터도 삭제되어진다는 점을 기억하자.

그렇다면 이렇게 stack에 main을 넣었을때 어떤 작업이 진행되어질까?

우선은 당연하게 main의 코드가 실행되어진다. 우리가 가장 먼저 본 재귀 코드를 예시로 봐보자.

class Test{
    public static void main(String[] args){
    	num(5);
    }
    
    public static void num(int n){
        if(n==0) return;
        System.out.println(n);
        num(n-1);
        System.out.println((n)+" 메서드 실행 완료");
    }
}

이 코드를 실행했을때 jvm은 main을 찾아서 stack에 main을 넣었다. 그리고 실행되어진다. 그런데 main 안에 또 num이라는 메서드를 발견했다.

그럼 다음과 같이 stack에 main 위에 num을 쌓는다. 그러면 이때 stack의 순서에 따라 num이 우선적으로 실행되기 위해 main은 stack에서 값을 반환 받을 때까지 멈춰 있게 된다. 그러면 이제 num의 코드를 보면

public static void num(int n){
        if(n==0) return;
        System.out.println(n);
        num(n-1);
        System.out.println((n)+" 메서드 실행 완료");
}

n이 0일때 멈추며 0이 아닐때는 n의 값을 출력하며 다시 num() method를 n의 값을 -1씩 하며 재귀 호출하고 있다. 그렇다면 num(5)를 호출한 main을 가지고 있는 java class를 실행했을 경우의 jvm의 stack을 그려보자

다음과 같이 stack에 쌓여 있을 것이다. num(5)가 실행되어 5를 프린트한 뒤 num(4)를 호출하기 때문에 잠시 멈춘다. 그 후 num(4)의 4를 프린트한 뒤 num(3) ... num(0)까지 실행한다. num(0)에서 n이 0이기 때문에 재귀 호출을 멈출 있었고 마지막 main을 실행하여 stack을 모두 비운뒤 프로그램은 종료되어진다.

여기서 가장 중요한 점은 num(5)가 메서드 중 가장 먼저 실행되지만 num(4)의 return이 될 때까지 대기하고 있으며 num(4)는 num(3)의 return을 대기하고 결국 num(0)의 return 값을 받은 num(1)이 종료되어야 0~5까지 모두 리턴되어 로직이 종료되는 것을 이해해야한다.

그래서 jvm이랑 재귀 호출이 무슨 관계?

재귀 호출에 대해서 처음 공부하거나 이미 jvm의 구조를 이해하고 있어서 재귀 호출이란 개념을 stack의 메모리 구조를 생각하며 이해하신 분이라면 이 글은 아무런 도움이 되지 않을 수도 있다. 하지만 혹시 나와 같이 java가 동기 방식임을 알지만 그걸 jvm의 stack이란 개념을 바로 떠올리지 못하며 재귀 호출을 머리가 아닌 가슴으로 이해하려다가 머리가 깨진 분들이라면 도움이 될 수 있어서 적어본다!

재귀 호출을 stack에 개념에서 생각하면 조금 더 쉽게 이해할 수 있을지도 모른다!

profile
코딩을 깔끔하게 하고 싶어하는 초보 개발자 (편하게 글을 쓰기위해 반말체를 사용하고 있습니다! 양해 부탁드려요!) 현재 KakaoVX 근무중입니다!

0개의 댓글