Java static & JVM 메모리 모델

강서진·2023년 11월 13일
0

Java

목록 보기
10/35
post-custom-banner

강좌 Course 1. Part 3. ch6 요약

static과 메모리

많은 클래스 중에서도 실제로 그 내용이 실행되는 클래스는 main 클래스다. main 클래스는 객체 생성 없이도 실행될 수 있는데, 그 이유는 실행방식에 있다.

Java 프로그램이 실행되면,
1. JVM이 가장 먼저 시작 클래스를 찾는다.
2. 이때, static 키워드가 붙은 멤버들은 클래스를 사용하는 시점에서 정해진 메모리 공간 (=static zone)에 자동으로 한 번만 로딩된다.
3. JVM이 static zone에서 main() 메서드를 호출한다.
4. 호출된 메서드를 Call Stack Frame Area(=Stack Area, last-in-first-out)에 push하여 동작을 시작한다. 다시 말하면, 호출된 메서드들을 Stack Area에 기계어코드로 바꿔 넣는다는 뜻이다.
실행이 끝난 메서드는 Call Stack Frame Area에서 소멸한다.

public class StaticTest {
    public static void main(String[] args) {
        int a=10;
        int b=20;

        hap(a,b) // 에러, main은 static에 있으나 hap 메서드는 static이 아니라 불러올 수 없음

    }

    public int hap(int a, int b){
        int v = a+b;
        return v;
    }
}

static과 non-static 멤버 접근 방법

static이 붙은 클래스는 JVM이 자동으로 메모리에 로딩하여 실행된다. 반대로 non-static 멤버는 자동으로 로드되지 않기 때문에 객체를 생성하여 메모리에 로딩해야 한다.

public class NonStaticTest {
    public static void main(String[] args) {
        int a=10;
        int b=20;

		// 객체 생성
        NonStaticTest st = new NonStaticTest();
        int n = st.hap(a,b);
        System.out.println(n);
    }

    public int hap(int a, int b){
        int v = a+b;
        return v;
    }
}

현재 실행하고 있는 클래스 내에서 또 해당 객체를 생성한다는 것이 조금 혼란스러울 수 있는데, 실제로는 메모리에 main이 올라가는 것이라고 생각하면 덜 혼란스럽다. 메모리에 딱 한번 로딩되기 때문에 객체를 생성한다고 해서 main이 한번 더 메모리에 올라가거나 하는 것이 아니다.
non-static 멤버가 객체 생성을 통해 메모리에 올라가면, non-static zone에 로딩된다. 이 때 로딩된 것은 객체가 생성되는 메모리 공간인 Heap Area에 자신이 저장된 위치의 포인터이다.
non-static 메서드는 객체를 생성한 후 호출을 해야 한다는 점에서 인스턴스 메서드라고도 한다.

static 메서드 접근법(2개의 클래스)

static 멤버는 클래스를 사용하는 시점에서 자동으로 static-zone에 로딩된다. 따라서 new 생성자 메서드를 사용해 객체를 생성하지 않고 바로 메서드를 호출할 수 있다.
클래스 이름에 바로 호출할 메서드를 붙여 사용할 수 있기 때문에 클래스 메서드라고도 한다.

// 예시: static 클래스
public class MyUtil {
    public static int sum(int a, int b){
        int ans = a+b;
        return ans;
    }
}

// 예시: 실행할 main 클래스
import fc.java.model.MyUtil;
public class StaticAccess {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int sum = MyUtil.sum(a,b); // 생성자메서드를 사용하지 않아도 된다. 
        System.out.println(sum);
    }
}

non-static 접근 방법(2개의 클래스)

// 예시: non-static 클래스
public class MyNSUtil {
    public int sum(int a, int b){
        int ans = a+b;
        return ans;
    }
}

// 예시: 실행할 main 클래스
import fc.java.model.MyNSUtil;
public class NonStaticAccess {
    public static void main(String[] args) {
        int a = 10;
        int b =20;
        MyNSUtil util = new MyNSUtil();
        System.out.println(util.sum(a,b));
    }
}

생성자 메서드를 사용해 새로 객체를 생성하여 접근한다.

JVM이 사용하는 메모리 영역


* GC는 주기적으로 Heap Area를 체크하고, 아직 포인터가 남아있는 객체들에 세대를 매긴다. 포인터가 사라진 객체들은 메모리에서 지우고, 남아있는 객체들에는 세대를 추가하는 식으로 진행되며, GC의 우선순위는 세대가 높은 객체가 된다.
(하단의 포인터가~체크는 GC 내용이다. 왜 저기다가 필기했는지 나도 모르겠다...)
* Runtime Constant Pool은 나중에 API 관련 수강할 때.

객체 생성과 static의 관계

어떤 클래스의 모든 멤버가 static 멤버라면, 굳이 객체생성을 할 필요가 없다(물론 그렇게 해도 실행은 문제없이 되긴 한다). 이러한 상황에서 객체생성을 막으면 클래스 이름으로 메서드를 호출하는 방식을 사용하도록 public인 생성자 메서드를 private으로 바꿈으로써 유도할 수 있다.
(따라서 생성자가 반드시 public이라는 말은 잘못된 말이다. System, Math같은 클래스들은 private 생성자를 가지고 있다.)

public class AllStaticTest{
	private AllStaticTest(){}
	
	public static int hap(int a, int b){
    	int v = a+b;
        return v;
        }
    public static int max(int a, int b){
    	return a>b ? a : b;
    	}
    public static int min(int a, int b){
    	return a<b ? b : a;
      	}
    

class, object, instance의 상호관계

Class(클래스): 객체를 모델링하는 도구(설계도)

Object(객체): 클래스를 통해서 선언되는 변수

Instance(인스턴스, 실체): 객체생성에 의해 메모리(Heap Memory)에 만들어진 객체

서로 약간씩 다르지만 비슷한 개념으로, 전부 객체를 나타낸다.

post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 11월 13일

잘 봤습니다. 좋은 글 감사합니다.

답글 달기