새소식

반응형
Java/Spring

클래스 로더와 클래스, 멤버 변수, static 멤버변수, static Inner 클래스 등 메모리 로딩 및 초기화

  • -
반응형

JVM 이란?

Java Virtual Machine 으로 Java 와 OS 사이에서 가장 중요한 중간자 역할을 한다.

개발자가 작성한 .java 파일을 javac.exe 인 자바 컴파일러가 컴파일하면 (.class) 라는 바이트 코드가 된다.

컴파일된 바이트 코드(.class) 를 OS 에 맞게 기계어로 해석해주는 역할을 JVM 이 한다.

 

 

  1. 자바 소스코드(.java)를 자바 컴파일러가 바이트코드(.class) 로 변환한다.
  2. 변환된 바이트코드를 JVM 의 Class Loader(클래스로더) 로 전달한다.
  3. Class Loader(클래스 로더)에서 JVM 런타임 영역으로 로딩(할당)하여 메모리에 올린다.

 

JVM 클래스 로더(Class Loader)

클래스 로더는 컴파일 된 자바 파일(.class) 을 동적으로 로드하고 JVM 의 실행 가능한 메모리 영역인

Runtime Data Areas 에 적재하는 작업을 한다.

 

클래스 로더에서 .class 파일을 로딩하는 순서는 3단계로 구성된다.

1. Loading(로드) : 클래스 파일을 가져와서 JVM 메모리에 로드한다.

2. Linking(링크) : 클래스 파일을 사용하기 위해 검증한다.

3. Initialization(초기화) : 클래스 변수들을 적절한 값으로 초기화한다.

 

클래스 로더는 Loading(로드) 할때 클래스나 static 멤버 변수 등 모두 한번에 올리지 않고 어플리케이션에서 필요한것만

동적으로 메모리에 적재하게 된다.

클래스 내에 멤버 변수를 호출하게 되면 그때서야 클래스가 동적으로 메모리에 로드한다.

 

정리하자면 JVM 의 클래스 로더는 실행될 때 모든 클래스를 메모리에 올리지 않고 필요한 클래스만 메모리에 적재하고

호출받은 클래스들을 그때마다 메모리에 적재하면서 메모리를 효율적으로 관리한다.

 

 

클래스 로드 시점 확인

본인은 Spring Boot 를 사용했기 때문에 VM arguments 옵션에 -verbose:class 를 추가하여 확인했다.

 

 

1. Outer 클래스를 생성하고 Main 함수에서 호출 안했을 때

Outer 클래스를 생성하고 호출을 안했을 때는 클래스를 로드하지 않는다.

위에서 설명했듯이 필요한 클래스만 로드하여 메모리 효율성을 높인다.

package com.obo.model;

public class Outer {
	// static 변수
	public static String staticStr = "Outer 클래스의 static 변수입니다.";
	
	// static final 상수
	public static final String finalStaticStr = "Outer 클래스의 static final 변수입니다.";
	
	// Outer 생성자
	public Outer() {
		System.out.println("Outer 생성자 초기화");
	}
	
	// static 메서드
	public static void getMethod() {
		System.out.println("Outer 클래스의 static 메서드 호출");
	}
	
	public class Inner {
		Inner() { System.out.println("Outer 클래스 일반 이너 클래스입니다."); }
	}
	
	public static class staticInner {
		static String staticStr = "staticInner 이너 클래스의 static String 변수입니다.";
		static final String finalStaticStr = "staticInner 이너 클래스의 final Static String 변수입니다";
		
		staticInner() {
			System.out.println("stataicInner 클래스 생성자입니다.");
		}
	}
}

 

 

package com.obo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.obo.model.Outer;

@SpringBootApplication
public class OboTestProjectApplication {

	public static void main(String[] args) {
		SpringApplication.run(OboTestProjectApplication.class, args);
		System.out.println("*******************");
		//System.out.println("Outer : "+Outer.staticStr);
		System.out.println("*******************");
	}

}

 

결과

 

 

2. Outer 클래스 인스턴스 생성

위 결과를 보면 Outer 클래스의 인스턴스 생성 시 필요하다고 판단하여 클래스를 메모리에 올려 로드한다.

package com.obo.model;

public class Outer {
	// static 변수
	public static String staticStr = "Outer 클래스의 static 변수입니다.";
	
	// static final 상수
	public static final String finalStaticStr = "Outer 클래스의 static final 변수입니다.";
	
	// Outer 생성자
	public Outer() {
		System.out.println("Outer 생성자 초기화");
	}
	
	// static 메서드
	public static void getMethod() {
		System.out.println("Outer 클래스의 static 메서드 호출");
	}
	
	public class Inner {
		Inner() { System.out.println("Outer 클래스 일반 이너 클래스입니다."); }
	}
	
	public static class staticInner {
		static String staticStr = "staticInner 이너 클래스의 static String 변수입니다.";
		static final String finalStaticStr = "staticInner 이너 클래스의 final Static String 변수입니다";
		
		staticInner() {
			System.out.println("stataicInner 클래스 생성자입니다.");
		}
	}
}

 

package com.obo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.obo.model.Outer;

@SpringBootApplication
public class OboTestProjectApplication {

	public static void main(String[] args) {
		SpringApplication.run(OboTestProjectApplication.class, args);
		System.out.println("*******************");
		new Outer();   // Outer 클래스 인스턴스 생성
		System.out.println("*******************");
	}

}

 

결과

 

 

3. Outer 클래스의 static 변수 호출

일반 static 변수를 호출했을 때 Outer 클래스를 로드한다.

static 메서드 또한 static 변수와 동일하게 Outer 클래스를 로드한 후 호출한다.

package com.obo.model;

public class Outer {
	// static 변수
	public static String staticStr = "Outer 클래스의 static 변수입니다.";
	
	// static final 상수
	public static final String finalStaticStr = "Outer 클래스의 static final 변수입니다.";
	
	// Outer 생성자
	public Outer() {
		System.out.println("Outer 생성자 초기화");
	}
	
	// static 메서드
	public static void getMethod() {
		System.out.println("Outer 클래스의 static 메서드 호출");
	}
	
	public class Inner {
		Inner() { System.out.println("Outer 클래스 일반 이너 클래스입니다."); }
	}
	
	public static class staticInner {
		static String staticStr = "staticInner 이너 클래스의 static String 변수입니다.";
		static final String finalStaticStr = "staticInner 이너 클래스의 final Static String 변수입니다";
		
		staticInner() {
			System.out.println("stataicInner 클래스 생성자입니다.");
		}
	}
}

 

package com.obo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.obo.model.Outer;

@SpringBootApplication
public class OboTestProjectApplication {

	public static void main(String[] args) {
		SpringApplication.run(OboTestProjectApplication.class, args);
		System.out.println("*******************");
        // Outer 클래스 static 변수호출
		System.out.println("static 변수 호출 : "+Outer.staticStr);
		System.out.println("*******************");
	}

}

 

결과

 

 

4. Outer 클래스의 static final 상수 호출

static final 상수를 호출했을 때는 일반 static 변수와 다르게 Outer 클래스를 로드하지 않는다.

이유는 final 상수들은 저장되는 메모리 영역이 다르다.

package com.obo.model;

public class Outer {
	// static 변수
	public static String staticStr = "Outer 클래스의 static 변수입니다.";
	
	// static final 상수
	public static final String finalStaticStr = "Outer 클래스의 static final 변수입니다.";
	
	// Outer 생성자
	public Outer() {
		System.out.println("Outer 생성자 초기화");
	}
	
	// static 메서드
	public static void getMethod() {
		System.out.println("Outer 클래스의 static 메서드 호출");
	}
	
	public class Inner {
		Inner() { System.out.println("Outer 클래스 일반 이너 클래스입니다."); }
	}
	
	public static class staticInner {
		static String staticStr = "staticInner 이너 클래스의 static String 변수입니다.";
		static final String finalStaticStr = "staticInner 이너 클래스의 final Static String 변수입니다";
		
		staticInner() {
			System.out.println("stataicInner 클래스 생성자입니다.");
		}
	}
}

 

package com.obo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.obo.model.Outer;

@SpringBootApplication
public class OboTestProjectApplication {

	public static void main(String[] args) {
		SpringApplication.run(OboTestProjectApplication.class, args);
		System.out.println("*******************");
		System.out.println("static 변수 호출 : "+Outer.finalStaticStr);
		System.out.println("*******************");
	}

}

 

결과

 

Outer 클래스를 로드하지 않고 final static 상수를 호출했다.

 

 

5. Outer 클래스의 내부 클래스 호출

내부 클래스를 생성하기 위해선 외부 클래스를 먼저 생성하고 인스턴스화 하기 때문에 Outer 클래스와

내부 클래스인 Inner 클래스가 로드된다.

package com.obo.model;

public class Outer {
	// static 변수
	public static String staticStr = "Outer 클래스의 static 변수입니다.";
	
	// static final 상수
	public static final String finalStaticStr = "Outer 클래스의 static final 변수입니다.";
	
	// Outer 생성자
	public Outer() {
		System.out.println("Outer 생성자 초기화");
	}
	
	// static 메서드
	public static void getMethod() {
		System.out.println("Outer 클래스의 static 메서드 호출");
	}
	
	public class Inner {
		public Inner() { System.out.println("Outer 클래스 일반 이너 클래스입니다."); }
	}
	
	public static class staticInner {
		static String staticStr = "staticInner 이너 클래스의 static String 변수입니다.";
		static final String finalStaticStr = "staticInner 이너 클래스의 final Static String 변수입니다";
		
		public staticInner() {
			System.out.println("stataicInner 클래스 생성자입니다.");
		}
	}
}

 

package com.obo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.obo.model.Outer;

@SpringBootApplication
public class OboTestProjectApplication {

	public static void main(String[] args) {
		SpringApplication.run(OboTestProjectApplication.class, args);
		System.out.println("*******************");
		new Outer().new Inner();
		System.out.println("*******************");
	}

}

 

결과

 

Outer 클래스와 Inner 클래스 모두 로드된다.

 

6. static 내부 클래스 호출

Outer 클래스 내 static Inner 클래스는 외부 클래스인 Outer 클래스를 로드하지 않고 직접 인스턴스화가 가능하다.

즉, 외부 클래스를 생성하지 않고 내부 클래스를 호출할 수 있다.

package com.obo.model;

public class Outer {
	// static 변수
	public static String staticStr = "Outer 클래스의 static 변수입니다.";
	
	// static final 상수
	public static final String finalStaticStr = "Outer 클래스의 static final 변수입니다.";
	
	// Outer 생성자
	public Outer() {
		System.out.println("Outer 생성자 초기화");
	}
	
	// static 메서드
	public static void getMethod() {
		System.out.println("Outer 클래스의 static 메서드 호출");
	}
	
	public class Inner {
		public Inner() { System.out.println("Outer 클래스 일반 이너 클래스입니다."); }
	}
	
	public static class staticInner {
		static String staticStr = "staticInner 이너 클래스의 static String 변수입니다.";
		static final String finalStaticStr = "staticInner 이너 클래스의 final Static String 변수입니다";
		
		public staticInner() {
			System.out.println("stataicInner 클래스 생성자입니다.");
		}
	}
}
package com.obo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.obo.model.Outer;

@SpringBootApplication
public class OboTestProjectApplication {

	public static void main(String[] args) {
		SpringApplication.run(OboTestProjectApplication.class, args);
		System.out.println("*******************");
		new Outer.staticInner();	// Outer 클래스의 static Inner 클래스 호출
		System.out.println("*******************");
	}

}

 

결과

 

Outer 클래스를 로드하지 않고 static 내부 클래스만 로드하여 호출한다.

 

 

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.