-
[Java] 클래스 로드와 클래스 초기화 (StaticInnerClass, InnerClass, MemoryLeak)프로그래밍 언어/Java 2022. 11. 6. 14:21
코드리뷰를 받던 중, "왜 내부클래스를 static 으로 선언하셨어요?" 라는 질문을 받게 되었다.
학생때부터 어떤 이유 때문에 습관적으로 써왔던 것이지만 기억이 안나던 나는 어버버 했다.
그 후 검색을 하여 메모리누수 때문이었다는게 기억이 났고, 다시 리뷰어분과 말씀을 나눴다.
그리고 다시 이런 질문을 받았다.
"static 도 메모리에 올라가고, non-static 도 메모리에 올라가는건 마찬가지 아닌가요? 인스턴스가 초기화 되지 않으면 메모리에 안올라가는건 non-static 이지 않나요?"
클래스 로드와 클래스 초기화는 다르지 않나? 다른게 맞나..?
static 은 클래스뼈대만 stack 영역에 올라가고, 인스턴스 초기화에만 heap 에 올라가는게 아닌가?
non-static 은 인스턴스 초기화 시, heap 에 클래스뼈대정보 + 인스턴스 정보가 올라가는게 아닌가?
non-static 이더라도 heap 에 인스턴스 정보만 올라가고, stack 에 클래스 뼈대만 정보를 올리는게 재사용성이 좋을 것 같은데
JVM 관련 개발자들은 그렇게 개발하지 않았을까?
추측만 무성하고 뭐 하나 자신감있게 제대로 말할 수 없는 나 자신을 보면서 회의감이 들어 다시 정리해보기로 했다.클래스 로드와 클래스 초기화는 다른 용어이다.
클래스 로드는 클래스를 메모리에 로드(클래스 초기화)할 때의 필요한 정보들을 세팅하는 단계이다.
클래스 로드에 사용하는 메모리영역과 클래스 초기화에 사용하는 메모리영역 또한 다르다.class 정보는 JVM 8 버전 이상에서 MetaSpace 영역에 저장된다.
* MetaSpace 는 OS 영역의 NativeMemory 이다.
MetaSpace는 Java의 Classloader가 현재까지 로드한 Class들의 Metadata가 저장되는 공간이다.
static 이던 non-static 이던 로드가 된 Class는 MetaSpace 에 저장된다.
클래스 뼈대정보만 저장이 되므로, 해당 클래스가 인스턴스화를 몇개를 하던지 메모리에는 1개의 정보만 저장이 된다.
아래 스크린샷은 그 증거이다.// Ping9OuterClass.java public class Ping9OuterClass { static{ System.out.println(">> Ping9OuterClass called!"); } public NonStaticInner getNonStaticClass(){ return new NonStaticInner(); } class NonStaticInner { } static class StaticInner{ } }
1. Static InnerClass
2. NonStatic InnerClass
3. Do Nothing
클래스 로드
static class 면 클래스로딩 시점에 클래스가 로드가 되는가?
답은 "아니다" 이다.
static class 이던 inner class 이던 참조가 되는 시점에 클래스가 로드 된다.검증 예제 1
VM option 에
-verbose:class
옵션을 주고 실행하면 로드된 클래스들을 확인할 수 있다.
아래의 예제코드를 돌려보면 로드된 클래스는 OtherOuterClass 와 Ping9OuterClass 둘 뿐이다.
StaticInner 와 NonStaticInner 는 로드되지 않았다.
또한 클래스가 로드만 됬을 뿐 클래스 초기화가 아니기 때문에 "Ping9OuterClass called!" 는 출력되지 않는다.// OtherOuterClass.java public class OtherOuterClass { public static void main(String[] args) { System.out.println(Ping9OuterClass.class); } } // Ping9OuterClass.java public class Ping9OuterClass { static{ System.out.println(">> Ping9OuterClass called!"); } public NonStaticInner getNonStaticClass(){ return new NonStaticInner(); } class NonStaticInner { } static class StaticInner{ } }
-verbose:class 옵션 결과 검증 예제 2
디버그 모드로 한 라인씩 실행해본다.
아래의 스크린샷과 같이 참조되는 시점에 클래스가 로드됨을 볼 수 있다.
클래스 초기화1 - OuterClass
이번에는 Ping9OuterClass 인스턴스를 생성한다.
클래스가 초기화 되며 "Ping9OuterClass called!" 가 출력되지만
여전히 inner class 들은 로드가 되지 않는다.// OtherOuterClass.java public class OtherOuterClass { public static void main(String[] args) { System.out.println(Ping9OuterClass.class.getClassLoader()); Ping9OuterClass cl = new test.Ping9OuterClass(); } } // Ping9OuterClass.java public class Ping9OuterClass { static{ System.out.println(">> Ping9OuterClass called!"); } public NonStaticInner getNonStaticClass(){ return new NonStaticInner(); } class NonStaticInner { } static class StaticInner{ } }
클래스 초기화2 - Nested Static Class
이번에는 nested static class 만 따로 인스턴스화 한다.
역시 Ping9OuterClass 는 로드도 되지않고, 당연히 JVM 메모리(Java Heap)에도 없다.클래스 초기화3 - Inner Class
이번에는 Inner class 만 따로 인스턴스화 한다.
참조가 일어나는 Ping9OuterClass 와 NonStaticInner 만 클래스 로드 된다.
또한, 메모리에도 NonStaticInnerClass 특성상 OuterClass 도 같이 인스턴스화 된다.
이것이 바로 memory leak 의 원인이다.심지어 InnerClass 갯수만큼 OuterClass 도 같은 갯수로 인스턴스화 된다.
마치며
클래스가 로드 되면 NativeMemory 영역인 MetaSpace(OS) 에 저장이되고,
클래스가 인스턴스화 되면 JVM 메모리영역(Java Heap)에 저장이 된다.
NonStatic Inner Class 이던 Static Inner Class 이던 MetaSpace 에 클래스정보가 저장된다.
NonStatic Inner Class 는 클래스가 인스턴스화 되면 OuterClass 도 같이 인스턴스화 된다.
메모리누수방지를 위해 Static InnerClass 를 사용하자!+ 추가정보
인스턴스 생성, non-final 정적변수 사용, 정적 메소드 호출 시점에 클래스는 로드 되며
해당 클래스의 모든 static 블럭은 초기화 된다.
또한 여러 쓰레드에서 동시에 사용하더라도 1번만 로드 된다.non-final static 변수 사용시 클래스 로드 & static 블럭 초기화
public class Ping9OuterClass { public static final int a = 0; public static int b = 0; static{ System.out.println(">> Ping9OuterClass called!"); } } public class MainClass { public static void main(String[] args) { System.out.println("BEFORE a==="); System.out.println(a); System.out.println("a==="); System.out.println("BEFORE b==="); System.out.println(b); System.out.println("b==="); }
Reference
[Java] 자바 메타스페이스(Metaspace)에 대해 알아보자.
Java Metaspace
jaemunbro.medium.com
Java 8에서 JVM의 변화 : PermGen이 사라지고 Metaspace가 등장하다.
Index
goodgid.github.io
JVM의 Garbage Collection · 안녕 프로그래밍
모든 자바 애플리케이션은 JVM 환경에서 작동한다. JVM의 Runtime Data Area의 메모리 구조와 마찬가지로 Garbage Collection 역시 자바 어플리케이션의 응답 시간과 성능에 밀접한 관계를 가지게 된다. Garba
www.holaxprogramming.com
Item 7. 다 쓴 객체는 참조를 해제하라 | Carrey`s 기술블로그
서론 Java의 장점 중 하나는 가비지 컬렉션을 지원하는 언어라는 점이다. C, C++ 처럼 개발자가 메모리를 직접 할당하고 해제하는 방식이 아니기 때문에 Java에서는 메모리 관리가 굉장히 편하다는
jaehun2841.github.io
[Java] Java 클래스 로딩 과정(Java Class Loading Process)
| 개요(Introduction) Java에서 객체가 어떻게 형성되고 관리되는 지 이해하려면 .java 파일로 작성되었던 소스코드가 어떻게 JVM위로 로딩되는 지 아는 것이 대단히 중요합니다. 왜냐하면 클래스 로딩
engkimbs.tistory.com
When does static class initialization happen?
When are static fields initialized? If I never instantiate a class, but I access a static field, are ALL the static blocks and private static methods used to instantiate private static fields calle...
stackoverflow.com
[Java] static inner class 는 언제 로드가 될까? 로드와 초기화?
😣서론 최근 싱글톤을 직접 구현하여 사용하게 되었고, 스레드 세이프를 하기 위해서 static inner class를 사용하게 되었다. public class LottoTicketBooth { private LottoTicketBooth() { } private static class LottoTicket
kdhyo98.tistory.com
728x90'프로그래밍 언어 > Java' 카테고리의 다른 글
[Java] Static Block 실행시점과 싱글톤(Singleton)에서의 활용 (0) 2022.12.28 [Java] multipartFile 이미지 리사이징 예제코드 (0) 2022.11.13 [Java/Kotlin] Static nested class 와 Inner Class 중 무엇을 써야할까? (0) 2022.11.06 [Java] 바이트코드 조작하기 (JavaAgent, JVM, Jacoco) (0) 2022.06.06 [Java] JVM 이 무엇인지, 어떻게 작동하는지 대충 훑어보자! (feat. 클래스로더) (1) 2022.05.29