ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java - static
    Language/Java 2022. 3. 4. 13:24

    static

    static이란?

     

    static은 고정된이라는 의미를 가지고 있으며, static 키워드를 통해 정적 필드와 정적 메서드를 만들 수 있다.

    정적 필드와 정적 메서드는 객체(인스턴스)에 소속된 멤버가 아니라 클래스에 고정된 멤버이기 때문에 클래스 로더가 클래스를 로딩해서 메서드 메모리 영역에 적재할 때 클래스별로 관리된다. 따라서 클래스의 로딩이 끝나는 즉시 바로 사용이 가능하며, 프로그램이 종료되기 전까지 사용할 수 있다.

    static 키워드를 통해 생성된 정적 멤버들은 heap 영역이 아닌, static 영역에 할당된다. static 영역에 할당된 메모리는 Garbage Collection의 관리 영역 밖에 존재하기 때문에 프로그램이 종료될 때까지 메모리에 값이 유지된 채로 존재하게 된다. 추가적으로, 정적 객체는 Garbage Collection에 의해 수집될 수 있다.

     

    Permanent vs Metaspace

    static이 저장되는 위치를 알기 전에 Permanent 영역과 Metaspace 영역을 이해해야 한다.

     

    Permanent 영역

     - Java 8 이전까지 존재했던 Method 영역이며, 클래스 내부의 메타 데이터를 저장하는 영역이다.

     - Heap 영역에 속하며 클래스, 메서드 메타 데이터, 정적 객체, 정적 변수, 상수 풀 등을 관리한다.

     - Java 8 이후부터는 Metaspace 영역으로 대체되었다.

     

    Metaspace 영역

     - Java 8부터 생긴 영역으로 Permanent 영역이 관리하던 일부 정보를 저장하는 영역이다.

     - Method 영역에 속하는 동시에 Native Memory 영역에 속한다.

     - Heap 영역이 아닌, Native Memory 영역에 속하기 때문에 JVM이 아닌 OS에서 관리하는 영역이다.

     

    Java 8 이전

     

    Java 8 이전의 Heap 영역을 살펴보면, Permanent 영역이 존재하고 이 안에 클래스 메타 데이터, 정적 변수 등이 저장된다. 이때, Permanent 영역은 Method 영역에 해당하므로, Java 8 이전의 static 변수들은 Method 영역에 저장되는 것이다.

     

    Java 8 이후

     

    Java 8 이후의 Heap 영역을 보면, Permanent 영역이 사라진 것을 확인할 수 있다. 대신 Heap 영역 외부에서 Metasapce라고 하는 Native Memory에서 Permanent 영역이 저장하고 있던 정보들을 들고 있도록 바뀌었다. 이때 주의할 점은 Permanent가 저장하던 정보를 전부 Metaspace에서 관리하는 것은 아니다.

     

     

    JDK 8의 공식문서를 확인해 보면, Permanent 영역에서 관리하던 클래스 메타 데이터는 Metaspace로 옮겨지고 interned String과 클래스 정적 변수는 Heap 영역에 옮겨진다는 사실을 알 수 있다. 즉, Java 8 이후부터 static은 Heap 영역에서 관리하게 된다.

     

    Permanent 영역이 사라진 이유

    각종 클래스 메타 데이터를 Heap 영역에서 관리하다 보니, Heap 영역에 들어간 데이터의 사이즈를 계산하기 어려워서 Out Of Memory가 자주 발생하곤 했다. 그래서 클래스 메타 데이터는 Native Memory 영역에서 관리하고, 정적 변수만 Heap 영역에서 관리하는 것이다.

     

    Garbage Collection

    Java 8 이전이든 이후든 정적 객체는 Garbage Collection의 수집 대상이 된다. 그래서 프로그램 전체 동안 정적 변수가 사라지지 않는다는 것은 일부만 맞는 말이고, 참조를 잃은 정적 객체는 언제나 Garbage Collection의 수집 대상이 될 수 있다. 당연한 이야기지만 Primitive Type의 정적 변수는 프로그램 종료 전까지 살아남게 된다.


    static의 사용

    static 변수
    public class Main {
    
        public static void main(String[] args) {
            LikeCount likeCount01 = new LikeCount();
            LikeCount likeCount02 = new LikeCount();
    
        }
    }
    
    class LikeCount {
        int count;
    
        public LikeCount() {
            this.count++;
            System.out.println("좋아요 개수 : " + count);
        }
    }

    위 코드는 게시물의 좋아요를 누를 때마다 좋아요 값을 증가시키는 코드의 예제이다.

     

    좋아요 개수 : 1
    좋아요 개수 : 1

    likeCount01, likeCount02 객체가 생성될 때, 서로 다른 메모리를 할당받기 때문에, 동일한 좋아요 개수를 출력하게 된다.

     

    public class Main {
    
        public static void main(String[] args) {
            LikeCount likeCount01 = new LikeCount();
            LikeCount likeCount02 = new LikeCount();
    
        }
    }
    
    class LikeCount {
        static int count;
    
        public LikeCount() {
            this.count++;
            System.out.println("좋아요 개수 : " + count);
        }
    }
    좋아요 개수 : 1
    좋아요 개수 : 2

    그러나 위 코드와 같이 static 키워드를 통해 count 변수를 선언하게 되면 likeCount01, likeCount02 객체가 하나의 메모리를 공유하기 때문에 서로 다른 좋아요 개수를 출력하게 된다.

     

    static 메서드

     

    static 메서드는 객체의 생성 없이 호출이 가능하고, 해당 객체 내에서는 호출이 불가능하다. static 메서드 안에서는 인스턴스 변수 접근이 불가능하다.

    또한, 위 코드에서 알 수 있듯이 static 메서드 안에서는 인스턴스 변수에 대한 접근이 불가능하다.

     

     

    static 메서드 안에서는 static 변수만 접근이 가능하다.


    정리

    static의 사용 정리

    1. 인스턴스에 공통적으로 사용해야 하는 것에 static을 붙인다.

     - 인스턴스를 생성하면, 각 인스턴스들은 서로 다른 독립적인 메모리를 할당받기 때문에 서로 다른 값을 유지한다.

     - 경우에 따라 인스턴스들이 공통적인 값이 유지되어야 하는 경우에는 static을 사용한다.

     

    2. static이 붙은 멤버 변수는 인스턴스를 생성하지 않아도 사용할 수 있다.

     - static이 붙은 멤버 변수는 클래스가 메모리에 올라갈 때 이미 자동적으로 생성이 되기 때문이다.

     

    3. static이 붙은 메서드에서는 인스턴스 변수를 사용할 수 없다.

     - static 메서드는 인스턴스 생성 없이 호출 가능한 반면, 인스턴스 변수는 인스턴스를 생성해야만 존재하기 때문에 static이 붙은 메서드에서는 인스턴스 변수의 사용을 허용하지 않는다.

     - 반대로 인스턴스 변수가 존재한다는 것은 static 멤버들은 이미 메모리에 존재한다는 것을 의미하기 때문에 인스턴스 변수 및 인스턴스 메서드에서는 static이 붙은 멤버들을 사용할 수 있다.

     

    4. 메서드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는 것을 고려한다.

     - 메서드 호출시간이 짧아지기 때문에 효율이 높아진다.

     

    5. 클래스 설계 시 static의 사용지침

     - 클래스의 멤버 변수 중 모든 인스턴스에 공통된 값을 유지해야 하는 것이 있다면 static을 붙인다.

     - 작성한 메서드 중 인스턴스 변수를 사용하지 않는 메서드에 대해서 static을 붙일 것을 고려한다.

     

    static을 지양해야 하는 이유

    1. 메모리 문제

     - static은 프로그램 실행 시점에 메모리에 할당을 하며, 웬만하면 프로그램 종료 시점까지 메모리에서 해제되지 않는다.

     

    2. 동시성 이슈 문제

     - static은 전역에서 접근이 가능하므로 별도의 동기화 전략을 수립해야 한다.

     

    3. 런타임 다형성 불가

     - static으로만 이루어진 메서드를 사용하는 객체의 경우, 해당 객체를 메모리로 할당하여 사용하는 것이 아니고 객체, 메서드로 바로 접근하여 호출한다.

     

    4. 객체의 상태를 이용할 수 없다.

     - 정적 메서드를 사용하기 위해서는 필요로 하는 인자를 모두 외부에서 주입해야 한다. 정적 메서드 안에는 클래스의 인스턴스 필드를 사용할 수 없기 때문이다.

     - 일반 메서드라면 객체 내에 있는 상태를 통해 해당 메서드를 구현해 줄 수 있으므로 변화하는 상태에 따라 다채로운 기능 구현이 가능하다.

     - 따라서, 객체 내의 정적 메서드가 많아지면 외부 값에 의존하는 수동적인 객체가 되어 버린다.

     

    5. 테스트하기 어려움

     - 정적 필드는 전역적으로 관리되기 때문에 프로그램 전체에서 이 필드에 접근하고 수정할 수 있다. 따라서 해당 필드를 추론하기 어려워 테스트하기 까다롭다.

     

    static은 언제 사용할까?

    상수 정의

    private static final double PI = 3.14;

     - 절대 변하지 않는 변수를 상수라고 하는데, 해당 상수는 객체 내에서 매번 일반 변수로 정의하기보다는 한 번 정적 변수로 정의하면 메모리를 아낄 수 있다.

     

    유틸리티 클래스 정의

     - 유틸리티 클래스는 인스턴스 메서드와 인스턴스 변수를 제공하지 않고, 데이터 처리를 위한 정적 메서드만 존재하는 클래스를 의미한다.

     - Java에서는 Math 클래스를 보면, 상수 외에 인스턴스 변수가 하나도 없고 오로지 계산을 위한 정적 메서드만을 제공한다.

     - 이렇게 애초부터 객체의 상태를 이용할 생각이 없고, 여러 객체들의 필요에 의해 데이터를 처리하는 공통 로직이 필요할 때는 static을 사용하여 설계한다.


    출처

    [JAVA] Java8부터는 static이 heap영역에 저장된다? (tistory.com)

    [Java] static이란? (tistory.com)

    https://dev-coco.tistory.com/23?category=962739 

     

    728x90

    댓글

Designed by Tistory.