알고리즘을 풀이하며 정적 변수, static 을 많이 사용하곤 한다. 이에 대한 정확한 의미에 대해 알아보고 분석해 보고자 글을 작성한다.
🟢 정적(static)
자바에서 static 키워드는 클래스 레벨의 변수나 메소드, 블록을 정의할 때 사용된다. 이는 인스턴스 생성 없이도 접근 가능하며, 모든 인스턴스에서 공유된다. static 변수는 프로그램이 시작할 때 메모리에 할당되고 프로그램이 종료될 때까지 유지된다.
왜냐하면 static 멤버는 클래스가 로드될 때 메모리의 메소드 영역에 할당되기 때문이다.이는 모든 인스턴스가 공유하는 특성 때문에 유용하게 사용될 수 있다. 예를 들어, 어떤 클래스의 인스턴스들이 공통적으로 사용해야 하는 값을 static 변수로 선언할 수 있다.
하지만, static 키워드의 남용은 객체지향 프로그래밍의 원칙과 상반되며, 메모리 사용량 증가로 이어질 수 있습니다. 따라서 static 멤버의 사용은 신중하게 결정해야 한다.
왜냐하면 static 멤버는 프로그램 종료 시까지 메모리에 남아 있어, 과도한 static 사용은 메모리 누수의 원인이 될 수 있기 때문이다. 이는 특히 대규모 애플리케이션에서 성능 저하의 원인이 될 수 있다.
static은 일반 인스턴스와는 다르게 static 영역에 메모리를 할당 하기 때문에 인스턴스 static 메서드에서는 아직 메모리가 할당 되지 않은 인스턴스 메서드를 사용 할 수 없다.
🔽 정적(Static) 멤버 생성
Static 키워드를 통해 생성된 정적 멤버들은 Heap 영역이 아닌 Static 영역에 할당된다. Static 영역에 할당된 메모리는 모든 객체가 공유하여 하나의 멤버를 어디서든지 참조할 수 있는 장점을 가지지만 Garbage Collector 의 관리 영역 밖에 존재하기에 Static 영역에 있는 멤버들은 프로그램의 종료시까지 메모리가 할당된 채로 존재하게 된다. 그렇기에 Static 을 너무 남발하게 되면 만들고자 하는 시스템 성능에 악영향을 줄 수 있다.
💡Static 영역과 Heap 영역
🔻Static 영역
Static 영역, 또는 Method 영역이라고 불리는 이 영역은 JVM 이 시작될 때 생성되고, 프로그램이 종료될 때 해제된다. 이 영역에는 다음과 같은 정보들이 저장된다.
클래스 정보
- 클래스의 메타데이터, 클래스에 대한 정보 (클래스 이름, 부모 클래스 등) 가 저장된다.
정적 변수
- 클래스에 선언된 `static` 키워드를 가진 변수들이 저장된다. 이 변수들은 클래스의 모든 인스턴스에서 공유되며, 클래스 로드 시 할당되고, 프로그램 종료 시 메모리에서 해제된다.
상수
- `final` 키워드와 함께 선언된 상수도 이 영역에 저장된다.
메서드 코드
- 클래스의 메서드 구현 코드가 저장되며, 이 코드는 모든 객체가 공유한다.
class Company {
static String companyName = "OpenAI";
static String getCompanyName() {
return companyName;
}
}
예를 들어, 다음과 같은 클래스에서 `companyName`과 `getCompanyName()` 메서드 구현이 Static 영역에 저장된다.
🔻Heap 영역
Heap 영역은 런타임에 동적으로 할당되는 데이터를 저장하는 영역으로, 객체와 배열이 저장된다. 모든 객체의 인스턴스와 배열은 힙에 생성된다. 이 영역은 가비지 컬렉터에 의해 관리되며, 더 이상 참조되지 않는 객체들은 가비지 컬렉터가 정리한다.
객체의 인스턴스 데이터
- 객체 각각의 상태 정보(인스턴스 변수)가 저장된다.
참조 변수
- 객체를 가리키는 참조 변수들 역시 이 영역에 저장된다.
class Employee {
String name;
int age;
Employee(String name, int age) {
this.name = name;
this.age = age;
}
}
Employee emp = new Employee("John Doe", 30);
이 예에서 `Employee` 객체 `emp` 는 힙 영역에 할당되며, `name`과 `age` 인스턴스 변수도 같은 영역에 저장된다.
Static 영역은 클래스 관련 데이터와 정적 변수를 저장하고, 이는 모든 인스턴스 간에 공유된다. Heap 영역은 각 객체의 인스턴스 데이터를 동적으로 저장하며, 객체가 더 이상 필요하지 않을 때 가비지 컬렉터에 의해 정리된다.
🔽 정적(Static) 멤버 선언
필드나 메소드를 생성 시 인스턴스로 생성할것인지 정적으로 생성할것인지에 대한 판단 기준은 공용으로 사용하느냐 아니냐로 내리면 된다. 그냥 생성한다면 자동으로 인스턴스로 생성되며 정적으로 생성하려면 필드와 메소드 선언 시 static이라는 키워들를 추가적으로 붙이면 된다.
static int num = 0; //타입 필드 = 초기값
public static void static_method(){} //static 리턴 타입 메소드 {}
🔽 정적(Static) 필드 사용 예시
class Number{
static int num = 0; //클래스 필드
int num2 = 0; //인스턴스 필드
}
public class Static_ex {
public static void main(String[] args) {
Number number1 = new Number(); //첫번째 number
Number number2 = new Number(); //두번쨰 number
number1.num++; //클래스 필드 num을 1증가시킴
number1.num2++; //인스턴스 필드 num을 1증가시킴
System.out.println(number2.num); //두번째 number의 클래스 필드 출력
System.out.println(number2.num2); //두번째 number의 인스턴스 필드 출력
}
}
===============================================================================
1
0
Number이라는 클래스안에 클래스 변수 num과 인스턴스 변수 num2를 생성하였고 두개의 Number인스턴스 number1과 number2를 생성했을때 number1에서 num1과 num2를 각각 1씩 증가시키고 number2에서 num1와 num2를 각각 출력시켰을때는 num1은 1, num2는 0이 출력되었다.
왜 이런 현상이 나타났느냐면 인스턴스 변수는 인스턴스가 생성될 때마다 생성되므로 인스턴스마다 각기 다른 값을 가지지만 정적 변수는 모든 인스턴스가 하나의 저장공간을 공유하기에 항상 같은 값을 가지기에 나타난 현상이다.
🔽 정적(Static) 메서드 사용 예시
class Name{
static void print() { //클래스 메소드
System.out.println("내 이름은 홍길동입니다.");
}
void print2() { //인스턴스 메소드
System.out.println("내 이름은 이순신입니다.");
}
}
public class Static_ex {
public static void main(String[] args) {
Name.print(); //인스턴스를 생성하지 않아도 호출이 가능
Name name = new Name(); //인스턴스 생성
name.print2(); //인스턴스를 생성하여야만 호출이 가능
}
}
정적 메소드는 클래스가 메모리에 올라갈 때 정적 메소드가 자동적으로 생성된다. 그렇기에 정적 메소드는 인스턴스를 생성하지 않아도 호출을 할 수 있다. 정적 메소드는 유틸리티 함수를 만드는데 유용하게 사용된다.