Static(2) 이란?

2022. 11. 27. 20:25카테고리 없음

Static을 사용한다는 것은 메모리에 한번 할당되고 프로그램이 종료될 때 해제된다는 것을 의미합니다. 메모리 영역과 관련지어서 설명을 해보겠습니다.

 

사진출처: 망나니개발자님 블로그

 

먼저 JVM구조에서 메모리 영역을 보면 Static영역과 Heap영역, Stack영역이 있습니다. 여기서 Static영역과 Heap영역을 따로 빼서 아래 사진으로 살펴보겠습니다.

 

우리가 만드는 Class들은 Static영역에 생성됩니다. 이 클래스들을 new 연산을 통해 객체를 만들어서 사용하는데 생성된 객체는 Heap영역으로 들어가죠. Heap영역의 메모리는 Garbage Collector(GC)에 의해서 정리를 받습니다. 하지만 Static 키워드를 통해서 Static영역에 할당된 메모리는 모든 객체들의 공유할 수 있다는 장점을 갖지만, GC의 관리를 받지 못하고, 정리 또한 받지 못하게 되죠. 때문에 Static키워드를 많이 사용하면 프로그램이 종료될 때까지 메모리가 할당된 채로 존재하게 되므로 비효율적이라고 할 수 있습니다.

 

1. Static 변수 (정적 변수) 란?

 

말 그대로 프로그램이 실행될 때 메모리에 고정적으로 할당되어, 프로그램이 종료될 때 해제되는 변수입니다. Static을 사용하는 이유로는 크게 두 가지가 있는데요. 메모리 효율성과 공유 개념을 들 수 있습니다.

 

값이 변하지 않고 고정되는 경우에 Static을 사용하면 메모리를 효율적으로 사용할 수 있습니다. 

 

Static으로 설정하면 같은 곳의 메모리 주소만을 바라보기 때문에 Static 변수의 값을 공유할 수 있게 됩니다

 

class Counter  {
    int count = 0;
    Counter() {
        this.count++;
        System.out.println(this.count);
    }
}

public class Sample {
    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();
    }
}

결과: 서로 다른 객체 ,서로 다른 메모리를 가리키고 있다.

1
1
class Counter  {
    static int count = 0;
    Counter() {
        count++;  // count는 더이상 객체변수가 아니므로 this를 제거
        System.out.println(count);
    }
}

public class Sample {
    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();
    }
}

결과: 같은 메모리 주소 공유

1
2

 

 

 

Static 변수 부작용

메모리 누수(memory leak)란?

컴퓨터 프로그램이 필요하지 않은  메모리를 계속 점유하고 있는 현상이다.

 

할당된 메모리를 사용한 다음 반환하지 않는 것이 누적되면 메모리가 낭비된다. 즉, 더 이상 불필요한 메모리가 해제되지 않으면서 메모리 할당을 잘못 관리할 때 발생한다.

 

일부 서적에서 메모리 손실이라는 용어로 뜻을 옮기기도 하지만 leak라는 표현은 단순히 잃는 것 이상의 개념이므로 누수라는 표현이 더 정확하다.

 

자바에서 메모리 누수(memory leak)

더 이상 사용되지 않는 객체들이 가비지 컬렉터에 의해 회수되지 않고 계속 누적이 되는 현상을 말한다.

Old 영역에 계속 누적된 객체로 인해 Major GC가 빈번하게 발생하게 되면서, 프로그램 응답속도가 늦어지면서 성능 저하를 불러온다. 이는 결국 OutOfMemory Error로 프로그램이 종료되게 된다.

 

가비지 컬렉션을 통해 소멸 대상이 되는 객체가 되기 위해서는 어떠한 reference 변수에서 가르키지 않아야 한다.
다 쓴 객체에 대한 참조를 해제하지 않으면 가비지 컬렉션의 대상이 되지 않아 계속 메모리가 할당 되는 메모리 누수 현상이 발생 된다.

 

 

GC가 되지 않는 루트 참조 객체는 크게 3가지다.

 

1. Static 변수에 의한 객체 참조

  • static는 GC에 대상이 되지 않는다. Static 변수는 클래스가 생성될 때 메모리를 할당 받고 프로그램 종료 시점에 반환되므로 사용하지 않고 있어도 메모리가 할당되어 있다.  잘 활용하면 성능을 향상시킬 수 있지만, 사용하지 않는데 계속 할당 되기만 한다면 GC가 되지 않아 메모리 릭으로 이어져 시스템 자체가 돌아갈 수 없는 상태에 이를 수 있다.

2. 모든 현재 자바 스레드 스택내의 지역 변수, 매개 변수에 의한 객체 참조

  • 자바에서 현재 실행중인 (각 스레드별로) 모든 메소드내에 선언된 지역 변수와 매개변수에 의해 참조되는 객체와  그 객체로부터 직간접적으로 참조되는 모든 객체는 참조되어 사용될 가능성이 있으며, 이 뿐만 아니라 caller 메소드로 return된 후에는 caller 메소드에서 참조하고 있는 지역변수, 매개변수에 의해 참조되는 객체와 객체로부터 직간접적으로 참조되는 모든 객체 또한, GC되지 않고 참조되어 사용될 가능성이 있다.
  • 따라서, 각 자바 스레드의 스택 프레임내에 있는 모든 지역변수와 매개 변수에 의해 참조되는 객체와 그 객체로부터 직간접적으로 참조되는 모든 객체들이 참조되어 사용될 가능성이 있다는 것이다.

3. JNI 프로그램에 의해 동적으로 만들어지고 제거되는 JNI global 객체 참조

 

그 외 또 여러가지 방법으로 메모리 누수(memory leak)가 발생하는 패턴들이 있다.

package main;


import java.util.ArrayList;
import java.util.List;

public class  GabageTestStatic {
	
	
	static final List<String>nums = new ArrayList<String>() ;
	
		public static void main(String[] args) {
			
			while (true) {
			
				nums.add("a");
				
				System.out.println("Total Memory : "+Runtime.getRuntime().totalMemory());

				System.out.println("Free Memory : "+Runtime.getRuntime().freeMemory());

				System.out.println("Max Memory : "+Runtime.getRuntime().maxMemory());
				
			}
		
	
		
		}
		
		
	}

OOME 발생

 

Static으로 ArrayList 선언 

ArrayList에  "a" String   무한 루프로 연속 할당.

 

결과: 

메모리 점유율이 높아지고, static 변수가 프로그램 종료시 까지 계속 메모리를 가지고 있다보니 메모리가 부족해지는 상황이 발생