[Java]자바 메모리 구조와 static

자바 메모리 구조

image-20241031213227945

자바의 메모리 구조는 크게 메서드 영역, 스택 영역, 힙 영역 3개로 나눌 수 있다.

  • 메서드 영역 : 클래스 정보를 보관한다. (ex. 붕어빵 틀)
  • 스택 영역 : 실제 프로그램이 실행되는 영역이다. 메서드를 실행할 때 마다 하나씩 쌓인다.
  • 힙 영역 : 객체(인스턴스)개 생성되는 영역이다. new 명령어를 사용하면 이 영역을 사용한다. (ex. 붕어빵 틀에서 만들어진 붕어빵이 쌓이는 영역)

image-20241031213452110

메서드 영역 (Method Area)

프로그램을 실행하는데 필요한 공통 데이터를 관리한다. 이 영역은 프로그램의 모든 영역에서 공유한다.

  • 클래스 정보 : 클래스의 실행 코드(바이트 코드), 필드, 메서드, 새성자 코드 등 모든 실행 코드가 존재한다.
  • static 영역 : static변수들을 보관한다.
  • 런타임 상수 풀 : 프로그램을 실행하는데 필요한 공통 리터럴 상수를 보관한다. 이 외에도 프로그램을 효율적으로 관리하기 위한 상수들을 관리한다.

    📌 메서드 코드는 메서드 영역에

    image-20241031213901203

    자바에서 특정 클래스로 100개의 인스턴스를 생성하면, 힙 메모리에 100개의 인스턴스가 생긴다. 각각의 인스턴스는 내부에 변수와 메서드를 가진다. 같은 클래스로부터 생성된 객체라도 인스턴스 내부의 변수 값은 서로 다를 수 있지만, 메서드는 공통된 코드를 공유한다.
    따라서 객체가 생성될 때, 인스턴스 변수에는 메모리가 할당되지만, 메서드에 대한 새로운 메모리 할당은 없다. 메서드는 메서드 영역에서 공통으로 관리되고 실행된다.
    즉, 인스턴스의 메서드를 호출하면 실제로는 메서드 영역에 있는 코드를 불러서 수행한다.

스택 영역 (Stack Area)

자바 실행 시, 하나의 실행 스택이 생성된다. 각 스택 프레임은 지역 변수, 중간 연산 결과, 메서드 호출 정보 등을 포함한다.
참고 : 스택 영역은 각 스레드별로 하나의 실행 스택이 생성된다. 따라서 스레드 수 만큼 스택 영역이 생성된다.

📌 스택

🔗 자료구조-스택/큐

자바는 스택 영역을 사용해서 메서드 호출과 지역 변수(매개변수 포함)을 관리한다.
메서드를 계속 호출하면 스택 프레임이 계속 쌓인다.
지역 변수는 스택 영역에서 관리한다.
스택 프레임이 종료되면 지역 변수도 함께 제거된다.
스택 프레임이 모두 제거되면 프로그램도 종료된다.

  • 스택 프레임 : 스택 영역에 쌓이는 네모 박스가 하나의 스택 프레임이다. 메서드를 호출할 때 마다 하나의 스택 프레임이 쌓이고, 메서드가 종료되면 해당 스택 프레임이 제거된다.

힙 영역 (Heap Area)

객체(인스턴스)와 배열이 생성되는 영역이다. 가비지 컬렉션(GC)이 이루어지는 주요 영역이며, 더 이상 참조되지 않는 객체는 GC에 의해서 제거된다.

지역변수는 스택 영역에, 객체(인스턴스)는 힙 영역에 관리된다.

메서드 영역에서 관리하는 변수는 없을까? 정답은 있다. 하지만 이를 이해하기 위해서는 먼저 static의 개념을 알아야한다.

static 변수

static키워드는 주로 멤버 변수와 메서드에 사용된다.
static변수는 왜 필요한가.

static 변수의 사용

특정 클래스에서 공용으로 함께 사용할 수 있는 변수를 만들 수 있으면 편리할 것이다. 이 때 바로 static키워드를 사용할 수 있다.

public class Data3 {
  public String name;
  public static int count; //static 변수, 정적 변수, 클래스 변수
  
  public Data3(String name) {
    this.name = name;
    count++;
  }
}
public class DataCountMain3 {
  public static void main(String[] args) {
    Data3 data1 = new Data3("A");
    System.out.println("A count=" + Data3.count); //1
    
    Data3 data2 = new Data3("B");
    System.out.println("B count=" + Data3.count); //2
    
    Data3 data3 = new Data3("C");
    System.out.println("C count=" + Data3.count); //3
  }
}

image-20241031221414206

static 변수 정리

용어정리

public class Data{
  public String name;
  public static int count; 
}

namecount는 둘 다 멤버변수이다. 멤버 변수(필드)는 static여부에 따라 다음과 같이 분류할 수 있다.

  • 인스턴스 변수

    static이 붙지 않은 멤버 변수는 인스턴스를 생성해야 사용할 수 있고, 인스턴스에 소속되어 있다. 따라서 인스턴스 변수라 일컫는다.

  • 클래스 변수

    클래스 변수, 정적 변수, static변수 등으로 부른다.
    static이 붙은 멤버 변수는 인스턴스와 무관하게 클래스에 바로 접근해서 사용할 수 있고, 클래스 자체에 소속되어 있으므로 클래스 변수라 한다.
    클래스 변수는 자바 프로그램을 시작할 때 딱 1개가 만들어진다. 인스턴스와 달리 보통 여러곳에서 공유하기 위한 목적으로 만들어진다.

변수와 생명주기

  • 지역 변수

    지역 변수는 스택 영역에 있는 스택 프레임 안에 보관된다. 메서드가 종료되면 스택 프레임도 제거되는데 이 때 해당 스택 프레임에 포함된 지역변수도 함께 제거 된다. 따라서 지역 변수는 생존 주기가 짧다.

  • 인스턴수 변수

    인스턴스에 있는 멤버 변수를 인스턴스 변수라 한다. 인스턴스 변수는 힙 영역을 사용한다. 힙 영역은 GC(가비지 컬렉션)이 발생하기 전까지는 생존하기 때문에 보통 지역 변수보다는 생존 주기가 길다.

  • 클래스 변수

    클래스 변수는 메서드 영역의 static 영역에 보관되는 변수이다. 메서드 영역은 프로그램 전체에서 사용하는 공용 공간이다. 클래스 변수는 해당 클래스가 JVM에 로딩되는 순간 생성된다. 그리고 JVM이 종료될 때까지 생명주기가 이어진다. 따라서 가장 긴 생명주기를 가진다.

    📍static이 정적변수인 이유가 여기에 있다. 힙 영역에 생성되는 인스턴스 변수는 동적으로 생성되고 제거된다. 반면 정적 변수는 거의 프로그램 실행 시점에 생성되고 프로그램 종료 시점에 제거 된다. 이름 그대로 정적이다.

static 메서드

정적 메서드는 정적 변수처럼 인스턴스의 생성없이 바로 호출할 수 있다.

하지만 정적 메서드는 언제나 사용할 수 있는 것은 아니다.

  • static메서드는 static만 사용할 수 있다.
    • 클래스 내부의 기능을 사용할 때, 정적 메서드는 static이 붙은 정적 메서드나 정적 변수만 사용할 수 있다.
    • 클래스 내부의 기능을 사용할 때, 정적 메서드는 인스턴스 변수나, 인스턴스 메서드를 사용할 수 없다.
  • 반대로 모든 곳에서 static을 호출할 수 있다.
    • 정적 메서드는 공용기능이다. 접근 제한자의 제한이 없다면 어디서든 호출이 가능하다.

정적 메서드는 클래스의 이름을 통해 바로 호출 가능하다. 그래서 인스턴스 처럼 참조값의 개념이 없다. 특정 인스턴스의 기능을 사용하려면 참조값을 알아야 하는데, 정적 메서드는 참조값이 없이 호출한다. 따라서 정적 메서드 내부에서 인스턴스 변수나 인스턴스 메서드를 사용할 수 없는 것이다.

정적 메서드의 활용

정적 메서드는 객체 생성이 필요 없이 메서드의 호출만으로 필요한 기능을 수행할 때 주로 사용한다.
주로 간단한 메서드 하나로 끝나는 유틸리티성 메서드에 자주 사용된다. 수학의 여러가지 기능을 담은 클래스를 만들 수 있는데, 이 경우 인스턴스 변수 없이 입력한 값을 계산하고 반환하는 것이 대부분이다. 이럴 때 정적 메서드를 사용해서 유틸리티성 메서드를 만들면 좋다.

가장 대표적인 정적 메서드가 바로 main()메서드이다.
main()메서드는 프로그램을 시작하는 시작점이 되는데, 생각해보면 객체를 생성하지 않아도 main()메서드는 작동했다. 이는 main()메서드가 static 메서드이기 때문이다.

Leave a comment