_

Always be tactful

프로그래밍/배웠어, 자바

[Java] 자바의 메모리 구조

funczun 2025. 1. 20. 01:20
멤버 변수 용어 정리
public class Data {
    public String name; // 인스턴스 변수
    public static int count; // 클래스 변수, 정적 변수, static 변수

    public Data(String name) {
        this.name = name;
        count++;
    }
}

 

 위 코드에서 name과 count 모두 멤버 변수이다. 그러나 static 유무에 따라 인스턴스 변수, 클래스 변수로 구분된다. 인스턴스 변수의 경우 인스턴스를 만들 때마다 새로 만들어지지만, 클래스 변수의 경우 일반적으로 자바 프로그램을 시작할 때 딱 1개가 만들어진다. 클래스 변수는 인스턴스 변수와는 달리 여러 곳에서 공유하는 목적으로 주로 사용된다.


변수와 생명주기

 

 지역 변수 (파라미터 포함)

ㄴ 스택 영역에 있는 스택 프레임 안에 보관

ㄴ 메서드 종료 시 스택 프레임도 제거되므로 해당 스택 프레임에 포함된 지역 변수도 함께 제거

 

 인스턴스 변수

ㄴ 힙 영역 사용

ㄴ 힙 영역은 GC가 발생하기 전까지 생존

 

 클래스 변수

ㄴ 메서드 영역의 static 영역에 보관

ㄴ 메서드 영역은 프로그램 전체에서 사용하는 공용 공간이므로 JVM에 로딩되는 순간 생성

ㄴ 따라서 JVM 종료될 때까지 생존


클래스 변수에 접근하는 두 가지 방법

 

 1. 인스턴스를 통한 접근

 2. 클래스를 통한 접근

Data data1 = new Data("EX");

System.out.println(data1.count); // 인스턴스를 통한 접근
System.out.println(Data.count); // 클래스를 통한 접근

 

 접근할 수 있는 방법은 많지만 클래스 변수는 클래스를 통한 접근을 해주는 것이 무조건 낫다. 왜냐하면 클래스 변수를 인스턴스 변수로 오해할 수 있기 때문이다. 아래에 조금 더 이해하기 쉽게 예시를 들어보겠다.

Data data1 = new Data("A");
System.out.println(data1.count);

Data data2 = new Data("B");
System.out.println(data2.count);

Data data3 = new Data("C");
System.out.println(data3.count);

 

 어떠한가? data1.count, data2.count, data3.count는 실제로 같은 메모리에 할당되어 있음에도, 각각 따로 할당되어 있는 듯 보인다. 이제 느낌 비교를 위해, 클래스를 통해 접근한 경우도 보자.

Data data1 = new Data("A");
System.out.println(Data.count);

Data data2 = new Data("B");
System.out.println(Data.count);

Data data3 = new Data("C");
System.out.println(Data.count);

 

 클래스를 통해 접근하니, count는 누가 봐도 static 변수임을 쉽게 알 수 있다. (내가 짠 코드지만 나만 쓰는 것이 아닐 수 있기 때문에 항상 가독성을 고려해야 한다!)


멤버 메서드 용어 정리
// 인스턴스 메서드
public class DecoStr1 {
    public String deco(String str) {
        return "* " + str + " *";
    }
}

// 정적 메서드 (또는 static 메서드)
public class DecoStr2 {
    public static String deco(String str) {
        return "* " + str + " *";
    }
}

 

 멤버 메서드가 어떠한 인스턴스 변수조차 포함하지 않는 경우, 객체를 굳이 생성해야 하나 싶은 생각이 든다. 그럴 때 정적 메서드를 사용하면 객체 생성 없이 DecoStr2.deco("funczun") 이런 식으로 사용할 수 있어 편리하다.


정적 메서드의 한계점
public class Deco {
    
    private int instanceValue;
    private static int staticValue;
    
    public static void staticCall() {
        instanceValue++; // 인스턴스 변수 접근 (불가능: Compile Error)
        instanceMethod(); // 인스턴스 메서드 접근 (불가능: Compile Error)
        
        staticValue++; // 정적 변수 접근 (가능)
        staticMethod(); // 정적 메서드 접근 (가능)
    }
    
    private void instanceMethod() {
        System.out.println("instanceValue: " + instanceValue);
    }
    
    private static void staticMethod() {
        System.out.println("staticValue: " + staticValue);
    }
}

 

 static 메서드가 만능은 아니다. static 메서드는 클래스 자체에 속해, 인스턴스 생성 없이 호출할 수 있는 메서드인데, 인스턴스 변수는 인스턴스를 생성해야 생긴다. 인스턴스를 생성하지 않았는데 무슨 인스턴스 변수에 접근할 수 있겠는가? (즉, static 메서드는 this를 알 수 없다.) 당연히 인스턴스 메서드 또한 접근할 수 없다.

728x90