C를 공부하다 보면 정적 변수와 동적 변수라는 말을 자주 접합니다. 그런데 자바에서는 "동적 변수"라는 공식 용어가 따로 없어서 헷갈리기 쉽습니다. 이 글에서는 자바에서 변수가 어떻게 나뉘는지, 정적 변수는 무엇이고 C의 동적 할당에 해당하는 개념은 무엇인지 정리합니다.
자바는 변수를 선언 위치와 static 키워드 유무로 구분합니다.
public class Example {
static int classVar = 0; // 정적 변수 (클래스 변수)
int instanceVar = 0; // 인스턴스 변수
void method() {
int localVar = 0; // 지역 변수
}
}
| 종류 | 선언 위치 | 생성 시점 | 소멸 시점 | 저장 영역 |
|---|---|---|---|---|
| 정적 변수 (static) | 클래스 안, static 있음 | 클래스 로드 시 | 프로그램 종료 시 | 메서드 영역 |
| 인스턴스 변수 | 클래스 안, static 없음 | new로 객체 생성 시 | 객체가 GC될 때 | 힙 |
| 지역 변수 | 메서드 안 | 메서드 호출 시 | 메서드 종료 시 | 스택 |
정적 변수의 정확한 저장 위치는 JVM 버전과 구현에 따라 조금씩 다릅니다. 여기서는 개념적으로 "클래스 단위로 관리되는 영역"이라고 이해하면 충분합니다.
정적 변수는 클래스에 하나만 존재하며, 모든 객체가 공유하는 변수입니다.
class Counter {
static int total = 0; // 모든 객체가 공유
int mine = 0; // 객체마다 따로 존재
Counter() {
total++;
mine++;
}
}
public class Main {
public static void main(String[] args) {
Counter a = new Counter();
Counter b = new Counter();
Counter c = new Counter();
System.out.println(Counter.total); // 3
System.out.println(a.mine); // 1
System.out.println(c.mine); // 1
}
}
total은 객체를 몇 개 만들든 하나뿐입니다.mine은 객체마다 별도로 생성됩니다.Counter.total)public static final double PI = 3.14159; // 상수
static int nextId = 1; // 객체 간 공유되는 카운터
static final)C에서 malloc으로 실행 중에 메모리를 할당하는 것과 가장 비슷한 것이 자바의 new로 객체를 생성하는 것입니다. 이렇게 만든 객체는 힙(Heap)에 생기고, 크기를 실행 중에 정할 수 있습니다.
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr = new int[n]; // 실행 중에 크기 결정
ArrayList<Integer> list = new ArrayList<>();
list.add(10);
list.add(20); // 크기가 자동으로 늘어남
C와 결정적으로 다른 점은 free가 없다는 것입니다. 더 이상 참조되지 않는 객체는 GC가 알아서 회수합니다.
void test() {
int[] arr = new int[1000000];
// free 필요 없음
// 메서드가 끝나 참조가 사라지면 GC가 정리
}
| C | 자바 | |
|---|---|---|
| 정적 변수 | static, 전역 변수 | static 필드 |
| 동적 할당 | malloc / free | new (해제는 GC가 자동) |
| 메모리 누수 | free 안 하면 발생 | 거의 없음 (참조를 계속 들고 있으면 발생 가능) |
| 포인터 | 직접 사용 | 없음 (참조 변수만 존재) |
class Example {
int x = 10;
static int y = 20;
static void staticMethod() {
System.out.println(y); // OK
// System.out.println(x); // 컴파일 오류
}
}
static 메서드는 객체 없이 호출되기 때문에, 특정 객체에 속한 인스턴스 변수가 존재하는지 보장할 수 없습니다.
static 변수는 모든 객체와 스레드가 공유합니다. 어디서든 값이 바뀔 수 있어 디버깅이 어렵고, 멀티스레드 환경에서는 동기화 문제가 생길 수 있습니다. 상수(static final)나 꼭 필요한 공유 값이 아니라면 남용하지 않는 것이 좋습니다.
프로그램이 끝날 때까지 메모리에 남기 때문에, static 컬렉션에 계속 데이터를 쌓으면 GC가 회수하지 못해 메모리 누수의 원인이 될 수 있습니다.
static)가 있으며, 클래스 단위로 하나만 존재하고 모든 객체가 공유합니다.new로 힙에 객체를 생성하는 것이 C의 동적 할당에 대응합니다. 해제는 GC가 담당합니다.