Java & Spring

[Java] static과 final은 언제 사용해야할까

ju_young 2024. 1. 20. 19:23
728x90

먼저 JVM 메모리는 다음과 같이 이루어져있다.

여기서 Method Area에 Static 변수가 저장되며 JVM 시작시 생성된다. 그리고 GC(Garbage Collection) 대상이 아니지만 명시적으로 null 선언시 GC 대상이 된다.

static은 언제 사용할까

보통 여러 인스턴스 간에 데이터를 공유해야할 때 다음과 같이 클래스 변수를 사용하거나 인스턴스에 종속되지 않는 메소드를 정의할 때 사용한다.

public class Example {
    public static int count;

    public static void incrementCount() {
        count++;
    }
}

 

그리고 내부 클래스를 정의할 때에도 static을 선언해주어야한다. 만약 아래처럼 static을 선언하지 않으면 내부 클래스에 대한 외부 참조가 생성된다.

public class OuterClass {
   int n = 10;

   class InnerClass {
       int inN = 20;
   }
}

 

컴파일된 OuterClass$InnerClass.class를 열어보면 다음과 같이 InnerClass가 OuterClass에 의존하는 것을 확인할 수 있다.

class OuterClass$InnerClass {
    int inN;

    OuterClass$InnerClass(final OuterClass this$0) {
        this.this$0 = this$0;
        this.inN = 20;
    }
}

 

이렇게 InnerClass가 OuterClass를 외부 참조하고나서 OuterClass가 필요없어진다면 InnerClass만 남게될 것이다. 그러면 OuterClass는 GC 대상이되야하지만 InnerClass는 OuterClass를 참조하고 있기 때문에 메모리에서 제거되지 않고 남게되는 것이다. 이로인해 메모리 누수(Memory Leak)가 발생한다.

 

static을 선언하고 난 후 OuterClass$InnerClass.class를 열여보면 다음과 같다.

class OuterClass$InnerClass {
    int inN = 20;

    OuterClass$InnerClass() {
    }
}

final은 언제 사용할까

final은 보통 값을 변경할 수 없는 상수를 선언하거나 메소드를 override할 수 없도록 하는 등의 목적으로 사용된다.

public class Example {
    public static final double PI = 3.14;  // 상수

    public final void doSomething() {
        ...
    }
}

 

이외에도 local variable, instance variable에 final 선언을 해주는데 local variable에 final을 선언해주는 것은 GC의 성능과 관련이 없다. 하지만 instance variable에 final을 선언하면 GC의 작업량을 줄일 수 있다고한다.

public class MutableHolder {
  private Object value;
  public Object getValue() { return value; }
  public void setValue(Object o) { value = o; }
}

public class ImmutableHolder {
  private final Object value;
  public ImmutableHolder(Object o) { value = o; }
  public Object getValue() { return value; }
}

우선 위 코드에 MutableHolder는 setValue 메소드를 사용하여 value를 변경할 수 있다. 이 때 MutableHolder을 생성하고 value를 update하기 때문에 Older object가 Younger object를 참조한다고 할 수 있다. 반면에 ImmutableHolder는 생성자에 value를 주입받아 생성하기 때문에 Younger object가 Older object를 참조한다고 할 수 있다. (주입할 value가 먼저 생성되어야 생성자에 넣어줄 수 있기 때문)

 

여기서 MutableHolder는 value가 변경되면 minor GC를 수행하면서 참조 object를 scan해야한다. 즉, 변경가능한(mutable) variable을 사용하면 GC에 의해 참조 object를 찾기 위한 작업이 늘어나게 된다. 따라서 불변의(immutable) variable을 사용함으로써 GC의 작업량을 줄일 수 있다는 것이다.


[reference]
https://inpa.tistory.com/entry/JAVA-%E2%98%95-JVM-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD-%EC%8B%AC%ED%99%94%ED%8E%B8
https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%9E%90%EB%B0%94%EC%9D%98-%EB%82%B4%EB%B6%80-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%8A%94-static-%EC%9C%BC%EB%A1%9C-%EC%84%A0%EC%96%B8%ED%95%98%EC%9E%90
https://mangkyu.tistory.com/120
https://stackoverflow.com/questions/306862/does-using-final-for-variables-in-java-improve-garbage-collection
https://stackoverflow.com/questions/35384393/how-do-immutable-objects-help-decrease-overhead-due-to-garbage-collection

728x90