클래스 로더와 클래스, 멤버 변수, static 멤버변수, static Inner 클래스 등 메모리 로딩 및 초기화
- -
JVM 이란?
Java Virtual Machine 으로 Java 와 OS 사이에서 가장 중요한 중간자 역할을 한다.
개발자가 작성한 .java 파일을 javac.exe 인 자바 컴파일러가 컴파일하면 (.class) 라는 바이트 코드가 된다.
컴파일된 바이트 코드(.class) 를 OS 에 맞게 기계어로 해석해주는 역할을 JVM 이 한다.
Method Area(또는 Metaspace)
- 클래스 로딩 시 클래스의 메타데이터 저장
- static 변수, static final 변수, static 메서드 정보 저장
Heap Area
- 인스턴스 변수(객체) 저장
- 클래스 인스턴스를 생성할 때 new 키워드로 만든 객체를 저장
Stack Area
- 메서드 호출 시 생성되는 지역 변수, 매개변수 등을 저장
💡 각 요소가 어디에 저장되는지
요소 | 저장 위치 | 설명 |
static 변수 | Method Area (Metaspace) | 클래스 로딩 시 초기화됨, 클래스마다 1개 존재 |
static final 변수 | Method Area | 컴파일 타임 상수인 경우 .class 파일에 리터럴로 저장됨 |
inner class (non-static) | Heap (인스턴스 생성 시) | 외부 클래스 인스턴스와 연결됨 (this 참조 유지) |
static inner class | Method Area | 일반 static class처럼 취급되며 별도 class로 로딩됨 |
일반 .class 파일 | Method Area | ClassLoader에 의해 로딩되어 메타정보 저장(클래스 구조, static 필드) |
- 일반 클래스 와 static Inner 클래스는 호출 및 사용시에만 로드된다.
둘다 Method Area 메모리에 저장된다. - static Inner 클래스는 독립적인 .class 로 존재한다.
Outer$StaticInner.class 처럼 독립적으로 생성된다. - 클래스 관련 정보(static 변수, static final 변수, static Inner class) 는 Method Area 메모리에 저장된다.
- 일반 Inner 클래스는 Heap Area(new 키워드로 인스턴스 생성) 메모리에 저장된다.
JVM 실행 순서
- 자바 소스코드(.java)를 자바 컴파일러가 바이트코드(.class) 로 변환한다.
- 변환된 바이트코드를 JVM 의 Class Loader(클래스로더) 로 전달한다.
- 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 클래스의 인스턴스 생성 시 필요하다고 판단하여 클래스를 메모리에 올려 로드한다.
로드된 .class 파일은 Method Area 에 저장되는데 메타데이터(클래스 구조, static 필드 등)에 저장된다.
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 클래스를 로드한 후 호출한다.
static 변수는 Method Area 메모리에 저장된다.
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 상수들은 저장되는 메모리 영역이 다르다.
static final 컴파일 타임의 상수는 Method Area 메모리에 저장되며 .class 파일에 리터럴로 저장된다.
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 클래스가 로드된다.
Outer 클래스와 Inner 클래스는 Heap Area 메모리에 저장된다.
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 클래스를 로드하지 않고 직접 인스턴스화가 가능하다.
즉, 외부 클래스를 생성하지 않고 내부 클래스를 호출할 수 있다.
static Inner 클래스는 별도의 .class 파일로 생성되어 일반 클래스와 동일하게 Method Area 메모리에 로드된다.
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 내부 클래스만 로드하여 호출한다.
'Java > Spring' 카테고리의 다른 글
Mybatis 의 Mapper 인터페이스 Bean 등록 방법 (0) | 2025.04.17 |
---|---|
@Bean 메서드의 매개변수 종속성 주입 (0) | 2025.04.16 |
자바 8 표준 API의 함수형 인터페이스(Supplier, Consumer) (0) | 2025.01.07 |
팩토리 메서드 패턴(Factory Method Pattern) (0) | 2025.01.03 |
Spring Security headers, frameOptions, contentSecurityPolicy, HTTP Strict Transport Security(HSTS) (0) | 2024.12.11 |
소중한 공감 감사합니다