[JAVA] 김영한의 실전 자바; 자바 메모리 구조와 static

선뀰·2023년 12월 29일
0

JAVA

목록 보기
15/25

자바 메모리 구조 (비유)

- 메서드 영역 :

클래스 정보를 보관한다. 이 클래스 정보가 붕어빵 틀이다.

- 스택 영역 :

실제 프로그램이 실행되는 영역이다. 메서드를 실행할 때 마다 하나씩 쌓인다.

- 힙 영역 :

객체(인스턴스)가 생성되는 영역이다. new 명령어를 사용하면 이 영역을 사용한다. 붕어빵 틀로부터 생성된 붕어빵이 존재하는 공간이다.

자바 메모리 구조 (실제)

메서드 영역(Method Area)

프로그램을 실행하는데 필요한 공통 데이터를 관리. 프로그램의 모든 영역에서 공유한다.

  • 클래스 정보 : 클래스의 실행 코드, 필드, 메서드와 생성자 코드 등 모든 실행 코드가 존재한다.
  • static 영역 : static 변수들을 보관한다.
  • 런타임 상수 풀 : 프로그램을 실행하는데 필요한 공통 리터럴 상수를 보관한다. 이런 문자를 공통으로 묶어서 관리한다. 프로그램을 효율적으로 관리하기 위한 상수들을 관리한다. ex. "hello", 123

스택 영역

자바 실행 시, 하나의 실행 스택이 생성된다. 각 스택 프레임은 지역 변수, 중간 연산 결과, 메서드 호출 정보 등을 포함한다.

  • 스택 프레임 : 스택에 쌓이는 영역 하나이다. 메서드를 호출할 때 마다 하나의 스택 프레임이 쌓이고, 메서드가 종료되면 해당 스택 프레임이 제거된다.

  • 힙 영역 : 객체(인스턴스)와 배열이 생성되는 영역이다. 가비지 컬렉션(GC)이 이루어지는 주요 영역이다.

  • 스택영역은 각 쓰레드 별로 하나의 실행 스택이 생성된다. 따라서 쓰레드 수 만큼 스택 영역이 생성된다. 쓰레드를 1개만 사용하므로 스택 영역도 하나이다.

메서드는 공통된 코드를 공유한다. 인스턴스 변수에는 메모리가 할당되지만, 메서드에 대한 새로운 메모리 할당은 없다.

후입선출 LIFO : 스택
선입선출 FIFO : 큐

static 제어자

static 클래스의 멤버(필드, 메서드, 이너 클래스)에 사용하는 제어자이다.

특정 클래스에서 공용으로 사용할 수 있는 변수를 만들 수 있다면 편리할 것이다.
static 키워드를 사용하면 공용으로 함께 사용할 수 있는 변수를 만들 수 있다.
static이 붙어 있는 멤버는 '정적 멤버'라고 한다.
객체 안에 있을 때 사용할 수 있는 상태가 되는 멤버 '인스턴스 멤버'

public class Data3 {
	public String name;
    public static int count; // static
    
    public Data3(String name) {
    	this.name = name;
        count++;
    }
}

인스턴스 필드와 정적 필드

class A {
int m=3; //객체를 생성한 후 사용 가능
static int n=5; //객체 생성 없이 사용 가능
}
A a=new A();
System.out.println(a.m); // 객체를 생성한 후에 사용 가능 = 권장하지 않는다.
System.out.println(A.n); // 객체를 생성하지 않고 바로 사용

static int count부분을 보면 변수 타입 int 앞에 static 키워드가 있다.
멤버 변수에 static을 붙이게 되면 static 변수, 정적 변수 또는 클래스 변수라고 한다.
객체가 생성되면 생성자에서 정적 변수 count의 값을 하나 증가시킨다.

count 정적 변수에 접근하는 방법이 특이한데, Data3.count와 같이 클래스에 .(dot)을 사용한다. 클래스에 직접 접근하는 것 처럼 느껴진다.
static이 붙은 멤버 변수 count는 인스턴스 영역에 생성되지 않는다. 메서드 영역에서 이 변수를 관리한다. Data3의 생성자와 같이 자신의 클래스에 있는 정적 변수라면 클래스명을 생략할 수 있다.

메서드 영역에서 static 영역도 하나만 존재한다.

멤버 변수(필드)의 종류

  • 인스턴스 변수 : static이 붙지 않은 변수
    인스턴스에 소속되어 있다. 인스턴스를 만들 때 마다 새로 만들어진다.

  • 클래스 변수 : static이 붙은 멤버 변수, ex. count
    클래스 변수, 정적 변수, static 변수 등으로 부른다. 인스턴스와 무관하게 클래스에 바로 접근해서 사용할 수 있고, 클래스 자체에 소속되어 있다.

public class Data3 {
	public String name;
    public static int count; // static
}


Data3 data1 = new Data3("A"); // 걍하기가 싫네..
        System.out.println("A count = " + Data3.count);
        // data1.name -> 인스턴스를 통해 접근하고
        Data3 data2 = new Data3("B"); // 걍하기가 싫네..
        System.out.println("B count = " + Data3.count);
        // Data3.count는 클래스를 통해 접근한다.
     

변수와 생명주기

  • 지역 변수(매개변수 포함)
    스택 영역에 있는 스택 프레임 안에 보관된다. 메서드가 종료되면 스택 프레임도 제거 되는데 해당 스택 프레임에 포함된 지역 변수도 함께 제거된다. 생존 주기가 짧다.

  • 인스턴스 변수
    인스턴스 변수는 힙 영역을 사용한다. 힙 영역 GC(가비지 컬렉션)이 발생하기 전까지 생존한다. 지역 변수보다 생존 주기가 길다.

  • 클래스 변수
    메서드 영역의 static영역에 보관되는 변수이다. 프로그램 전체에서 사용하는 공용공간. 클래스 변수는 해당 클래스가 JVM에 로딩 되는 순간 생성된다. JVM이 종료될 때 까지 생명주기가 이어진다.

정적 변수 접근 법

static 변수는 클래스를 통해 바로 접근할 수도 있고, 인스턴스를 통해서도 접근 가능하다.

 Data3 data4 = new Data3("D");
        System.out.println(data4.count); // 권장하지 않는다. count가 인스턴스 변수인가 라는 생각이 든다.
        // 클래스를 통한 접근
        System.out.println(Data3.count); // static 변수라는 생각이 든다. 
  • 인스턴스를 통한 접근 data4.count
    코드를 읽을 때 인스턴스 변수에 접근하는 것 처럼 오해할 수 있다.

  • 클래스를 통한 접근 Data3.count
    클래스에서 공용으로 관리하기 때문에 클래스를 통해서 접근하는 것이 명확하다.

static 메서드

특정 문자열을 꾸며주는 기능을 만들어준다.

public class DecoUtil2 {
public static String deco(String str) {
String result = "" + str + "";
return result;
}
}

메서드 앞에 static을 붙여주면 정적 메서드를 만들 수 있다. 정적 메서드는 정적 변수처럼 인스턴스 생성 없이 클래스 명을 통해서 바로 호출 가능하다.

static이 붙은 메서드는 객체 생성 없이 클래스명 + . + 메서드 명으로 바로 호출이 가능하다.

  public static void main(String[] args) {
       String s = "hello java";
       String deco = DecoUtil2.deco(s); // 클래스를 통해 바로 불러올 수 있다. 붕어빵 틀을 바로 불러오는 것이다.

인스턴스 메서드

static이 붙지 않은 메서드는 인스턴스를 생성해야 호출할 수 있다.
모든 메서드에 다 접근할 수 있다.

DecoData data1 = new DecoData();
data1.instanceCall();

정적 메서드 = 클래스 메서드

static메서드는 static만 사용할 수 있다.
객체 생성없이 클래스에 있는 메서드를 바로 호출할 수 있다는 장점이 있다.
정적 메서드는 static이 붙은 정적 메서드나 정적 변수만 사용할 수 있다.
정적 메서드는 인스턴스 변수, 인스턴스 메서드를 사용할 수 없다.
= 반대로 모든 곳에서 static을 호출할 수 있다.

접근 제어자만 허락한다면 모든 곳에서 static을 호출할 수 있다.

static은 static만 접근이 가능하다.
staticCall() 정적 메서드이다. 따라서 static만 사용할 수 있다. 정적 변수, 정적 메서드에는 접근할 수 있지만, static이 없는 인스턴스 변수나 인스턴스 메서드에 접근하면 컴파일 오류가 발생한다.

DecoData.staticCall(); 
  • 정적 메서드가 인스턴스의 기능을 사용할 수 없는 이유
    정적 메서드는 클래스의 이름을 통해 바로 호출할 수 있다. 인스턴스처럼 참조값의 개념이 없다.
    특정 인스턴스의 기능을 사용하려면 참조값을 알아야 하는데, 정적 메서드는 참조값 없이 호출한다.
    정적 메서드 내부에서 인스턴스 변수나 인스턴스 메서드를 사용할 수 없다.

객체의 참조값을 직접 매개변수로 전달하면 정적 메서드도 인스턴스의 변수나 메서드를 호출할 수 있다.

public static void staticCall(DecoData data) {
	data.instanceValue++;
    data.instanceMethod();
}

인스턴스 메서드와 정적 메서드

인스턴스 필드는 실제 데이터값을 저장하고
정적 메서드는 데이터값의 위치를 저장한다.

class A {
	void a() {
    	System.out.println("instance 메서드"); // 객체를 생성한 후에 사용이 가능하다.
    }
    static void b() {
    	System.out.println("static 메서드"); // 객체 생성 없이 사용 가능
    }
}  

A a1=new A();
a1.a(); //인스턴스 메서드 

A.b(); // 정적 메서드는 클래스명으로 바로 접근하여 사용이 가능하다.

용어 정리

  • 멤버 메서드의 종류
    1) 인스턴스 메서드 : static이 붙지 않은 멤버 메서드
    2) 클래스 메서드 : static이 붙은 멤버 메서드
    클래스 메서드, 정적 메서드, static 메서드 등으로 부른다.

정적 메서드 활용

객체 생성이 필요 없이 메서드의 호출만으로 필요한 기능을 수행할 때 주로 사용한다. 간단한 메서드 하나로 끝나는 유틸리티성 메서드에 자주 사용

  • 인스턴스를 통한 접근
    data3.staticCall()
    정적 메서드의 경우 인스턴스를 통한 접근은 추천하지 않는다. 인스턴스 메서드에 접근하는 것 처럼 오해할 수 있다.

  • 클래스를 통한 접근
    DecoData.staticCall()
    정적 메서드는 클래스에서 공용으로 관리하기 때문에 클래스를 통해서 접근하는 것이 더 명확하다. 정적 메서드에 접근할 때는 클래스를 통해서 접근하는것이 좋다.
    alt + enter를 누르면 import static이 자동으로 되고, DecoData.가 생략 가능하다.

특정 클래스의 모든 정적 메서드에 적용하려면 을 사용한다.
import static static2.Decodata.
;

main() 메서드는 정적 메서드

인스턴스 생성 없이 실행하는 대표적인 메서드가 main() 메서드이다.
main() 메서드가 static이기 때문에 바로 호출이 가능하다.

profile
공부 기록

0개의 댓글