본문 바로가기

Java

[자바] 메모리 구조

 자바의 프로그램 실행 구조

1) JVM이란?
- Java Virtual Machine의 약자로 자바 가상 머신
- 자바와 운영체제 사이의 중개자 역할을 수행, 자바가 운영체제에 구애 받지 않고 프로그램을 수행할 수 있도록 도와준다.
- Garbage Collector를 사용한 메모리 관리도 자동으로 수행
- 스택 기반으로 동작

2) 자바 프로그램의 실행 구조
- 자바로 구현된 응용 프로그램은 JVM에 의해 실행되기 때문에 어떤 운영체제에서도 동일한 결과를 갖는다.

- JVM은 운영체제에 종속적
- 응용 프로그램이 실행되면, JVM은 시스템으로부터 프로그램을 수행하는데 필요한 메모리를 할당 받음

 

■ JVM의 메모리 구조

1) 자바 프로그램의 실행 

- Source Code 파일(.java)을 Java Compiler를 통해서 Byte code 파일(.Class) 파일로 변환
- Byte code로 변환된 파일을 JVM의 Class Loader로 보낸다.
- Class Loader는 클래스 파일을 불러와서 메모리에 저장하는 역할
- Class Loader를 통해 JVM내의 Runtime Data Area에 배치된 바이트 코드들은 Execution Engine에 의해 명령어 단위로 읽어 실행
- Garbage Collector(GC)는 사용하지 않거나 필요없는 객체들을 메모리에서 소멸
- Runtime Data Area는 JVM이 프로그램을 수행하기 위해 운영체제로부터 할당받는 메모리 공간, 자바 애플리케이션을 실행할 때 사용되는 데이터들이 적제

[참고]
Gabage Collector은 힙 메모리 영역에 생성된 객체들 중에서 참조되지 않은 객체들을 탐색 후 제거하는 역할

2) Runtime Data Area(Memory Area)
[Method Area : 메서드 영역]
- JVM이 실행되면서 생성되는 공간
- JVM은 해당 클래스의 클래스 파일(.class)을 읽어 분석하여 클래스에 대한 정보, 전역변수 정보, static 변수 정보가 이곳에 저장
- Runtime Constant Pool에는 상수 정보가 저장
- 모든 스레드에서 정보가 공유

[Heap : 힙]
- 프로그램 실행 중 생성되는 모든 인스턴스는 모두 이곳에서 생성
- 즉, new 연사자로 생성되는 객체, Array와 같은 동적으로 생성된 데이터가 저장되는 공간(Reference Type의 데이터가 저장되는 공간)
- Heap에 저장된 데이터는 Garbage Collector가 처리하지 않는 한 소멸 X

[Stack : 스택]
- 메서드의 작업에 필요한 메모리 공간을 제공
- 메서드가 호출되면 스택은 메서드를 위한 메모리 공간을 할당
- 메모리는 메서드가 작업을 수행하는 동안 지역변수/매개변수 데이터, 연산의 중간 결과 등을 저장하는데 사용
- Last In First out, 나중에 들어온 데이터가 먼저 나간다.
- 메서드가 작업을 마치면 할당되었던 메모리 공간은 반환, 제거된다.
- 스택의 제일 위에 있는 메서드가 현재 실행 중인 메서드
- 아래에 있는 메서드가 바로 위의 메서드를 호출한 메서드

 

CallStackTest

- CallStackTest.java 파일을 실행시키면 JVM에 의해서 main 메서드가 호출됨으로써 프로그램이 시작
- 스택에 main 메서드를 위한  메모리 공간이 할당되고 main 메서드의 코드가 수행
- ㅇmain 메서드에서 firstMethod()를 호출, main 메서드가 끝난 것이 아니므로 main 메서드는 대기, firstMethod() 수행 시작
- firstMethod()에선 secondMethod()를 호출, firstMethod()가 끝난 것이 아니므로 대기, secondMethod() 수행 시작
- secondMethod()에서 println()을 호출, println()에 의해 화면에 "CallStackTest.secondMethod"가 출력
- println() 메서드의 수행이 완료되어 스택에서 사라지고 자신을 호출한 secondMethod()로 되돌아간다. 대기중이던 secondMethod()는 println()을 호출한 이후부터 수행을 재개
- secondMethod()에 더 이상 수행할 코드가 없으므로 종료, 자신을 호출한 firstMethod()로 돌아간다.
- firstMethod()에 더 이상 수행할 코드가 없으므로 종료, 자신을 호출한 main 메서드로 돌아간다.
- main 메서드에서도 더 이상 수행할 코드가 없으므로 종료, 스택은 완전히 비워지게 되고 프로그램은 종료
public class CallStackTest {
    public static void main(String[] args) {
        firstMethod();
    }

    static void firstMethod(){
        secondMethod();
    }

    static void secondMethod(){
        System.out.println("CallStackTest.secondMethod");
    }
}

 

■ 클래스 메서드(static 메서드)와 인스턴스 메서드

메서드 앞에 static이 붙어 있으면 클래스 메서드이고, 붙어 있지 않으면 인스턴스 메서드
클래스 메서드는 객체를 생성하지 않아도 클래스이름.메서드이름(매개변수)와 같은 식으로 호출이 가능
인스턴스 메서드는 반드시 객체 생성해야만 호출이 가능

1) 클래스를 설계할 때, 멤버 변수 중 모든 인스턴스에 공통으로 사용하는 것에 static을 붙인다. 
모든 인스턴스에서 같은 값이 유지되어야 하는 변수는 static을 붙여서 클래스 변수로 정의

2) 클래스 변수(static 변수)는 인스턴스를 생성하지 않아도 사용 가능
클래스가 메모리에 올라갈 때 이미 자동적으로 생성되기 때문에 인스턴스를 생성하지 않아도 사용이 가능

3) 클래스 메서드는 인스턴스 변수 사용 X
클래스 메서드가 호출되었을 때, 인스턴스가 존재하지 안흥ㄹ 

[참고]
- 클래스 영역에 선언된 변수 : 멤버 변수 
- 멤버 변수 중 static이 붙은 것은 클래스 변수(static 변수), static이 붙지 않은 변수를 인스턴스 변수
- 멤버 변수는 static 변수와 인스턴스 변수를 모두 통칭

 

■ MyMath2.java

- 인스턴스 메서드인 add()는 인스턴스 변수인 a와 b로 작업을 수행
- 인스턴스 메서드를 사용하기 위해서는 MyMath2 클래스의 인스턴스를 생성해야한다.
- static 메서드는 매개변수를 통해 작업을 수행
- static 메서드는 객체 생서 없이 클래스.메서드이름 으로 호출해서 사용이 가능
public class MyMath2 {
    Long a, b;  //인스턴스 변수

    long add() {
        return a + b;
    }

    static long add(Long a, Long b) {
        return a + b;
    }
}

class MyMath2Test {

    public static void main(String[] args) {
        System.out.println("클래스 메서드 호출=" + MyMath2.add(100L, 200L));

        MyMath2 myMath2 = new MyMath2();
        myMath2.a = 200L;
        myMath2.b = 100L;
        System.out.println("인스턴스 메서드 호출=" + myMath2.add());
    }
}

 

■ 클래스 멤버와 인스턴스 멤버간의 참조와 호출

같은 클래스에 속한 멤버들 간에는 별도의 인스턴스를 생성하지 않고도 서로 참조 또는 호출이 가능
단, 클래스 멤버가 인스턴스 멤버를 참조 또는 호출하고자 하는 경우에는 인스턴스를 생성

인스턴스 멤버가 존재하는 시점에 클래스 멤버는 항상 존재, 클래스 멤버가 존재하는 시점에 인스턴스 멤버는 존재하지 않을 수도 있다.
 

 

■ MemberCall.java

1) 에러 1 : 인스턴스 변수는 static 변수에 값을 저장 X
2) 에러 2 : static 메서드에서는 인스턴스 변수를 사용 X
3) 에러 3 : static 메서드에서는 인스턴스 메서드 호출 X
public class MemberCall {

    int iv = 10;
    static int cv = 20;

    int iv2 = cv;
//    static int cv2 = iv;    // 에러 1.
    static int cv2 = new MemberCall().iv;   //이처럼 객체를 생성 후 인스턴스 변수에 접근

    /**
     * static method
     * 클래스 변수는 사용이 가능하지만,
     * 인스턴스 변수는 사용이 불가능
     */
    static void staticMethod1(){
        System.out.println(cv);
//        System.out.println(iv);   //에러 2.
        MemberCall memberCall = new MemberCall();
        System.out.println(memberCall.iv);  //객체를 생성한 후에 인스턴스 변수 참조 가능
    }

    /**
     * 인스턴스 메서드는 인스턴스 변수, 클래스 변수 모두 사용이 가능
     */
    void instanceMethod1(){
        System.out.println(cv);
        System.out.println(iv); 
    }
    
    static void staticMethod2(){
        staticMethod1();
//        instanceMethod1();    //에러 3.
        MemberCall memberCall = new MemberCall();
        memberCall.instanceMethod1();   //인스턴스를 생성 후 메서드 호출이 가능
    }

    /**
     *  인스턴스 메서드는 static 메서드, 인스턴스 메서드 호출이 모두 가능
     */
    void instanceMethod2(){
        staticMethod1();
        instanceMethod1();
    }
}

 

 

[출처]

- 자바의 정석 3판

- https://velog.io/@shin_stealer/자바의-메모리-구조

- https://steady-coding.tistory.com/305