mm Home

Java - static 본문

개발/Java

Java - static

jess_m 2017. 9. 6. 15:56

중첩 클래스를 생성할 일이 생겼었다. static으로 만들까 non-static 으로 만들까 고민을 했다.

그런데 static을 그저 객체 생성없이 사용 가능하게하는 키워드로만 알고 있었으니, static 중첩 클래스는 객체가 생성이 안되는것인가?? 라는 착각마저 불러일으켰다.

static에 대해서 잘 모르는 부분이 있어서 공부를 해보았다.

 

 

 

 

static이란?

정적이라는 뜻의 단어이다. 이 단어만 가지고서는 잘 이해하기 어려운것 같다. 고정된이라는 뜻이면 좀 더 이해가 쉬울까?

어쨋든.. static 은 프로그램상의 고정된 자원이다.

동적인 자원과 달리, 라이프사이클이 어플리케이션의 구동과 함께 생성되어지고, 종료시에 사라지게 될 것이다. 

 

 

 

 

모든 클래스가 static?

본론에 들어가기 앞서 일반 클래스는 static 키워드를 사용하지 못한다. 왜 사용하지 못할까?

Java의 모든 클래스가 사실상 'static' 클래스이기 때문에 추가적인 플래그를 사용할 필요가 없어서이다. 

 

왜 모든 클래스가 정적 클래스인지 살펴보자. 

 

우선 모든 클래스는 객체가 2개 생성된다.

해당 클래스의 객체java.lang.Class 의 객체이다.

 

예를 들어.. 아래와 같이 Student 객체를 만들게 된다면 Class 객체와 Student 객체가 생성된다.

Student jess = new Student(‘Jess.m’);

클래스는 클래스로더에 의해 클래스가 참조되는 순간에 JVM에 올라간다.(클래스로더가 다수라면 다수의 클래스가 올라간다.) 이때 Class 객체를 생성하여 JVM 내 metaspace + heap 공간에 저장한다. (static 객체의 메타데이터는 metaspace 에 저장되지만, 실제 객체는 heap 에 저장된다고 한다).  Metaspace 에는 클래스의 메타 정보가 들어있다. 클래스 정보에는 메소드(bytecode 포함), 상수풀, 클래스명, internal 객체(String, Exception 등 JVM에 의해 생성되는 객체) 등의 클래스와 관련된 정보들이 있다. 

 

이야기가 옆으로 샌게 아니고.. Java 에서 모든 클래스는 객체로 생성된다. 사용자가 객체 생성을 하지 않고 사용하는 static 메소드나 필드가 있는 클래스도 객체를 생성해 놓는다는 이야기이다. 결국 JVM 입장에서는 모든 클래스가 metaspace라는 공간에 정적인 자원처럼 가지고 있다는 것이다. 그래서 모든 클래스가 사실상 정적이다라고 볼수 있으며, static 키워드를 붙일 필요가 없다.

 

아래의 코드를 보면 조금 더 쉽게 이해가 될 것이다.

 

 

위에 2개의 클래스를 선언했다. 

하나는 Reflection을 이용해 Class 객체를 가져와서 메소드를 invoke 시키는 테스트 코드이고, 하나는 invoke 시킬 타겟 클래스이다.

타겟 클래스에는 non-static과 static 메소드가 있는데, 이미 알고 있듯이 static 메소드의 경우에는 객체가 없이도 invoke 된다. Class 객체를 통해 메소드를 invoke 시킨 것이다. JVM입장에서는 모든 클래스의 java.lang.Class 객체를 가지고 있기 때문에 모든 클래스는 정적이라고 말한다.

 

 

 

 

이와 비슷하게 non-static 메소드에서 static 필드를 사용할 수 없는 이유도 유추할 수 있을 것이다. static은 Class 객체에 정보들을 담고 heap에는 데이터를 생성하지 않는다. 따라서 static 은 동적인 객체가 갖는 다른 라이프사이클을 고려하지 않고 사용하지 못하도록 한 것이다.

 

 

 

 

 

그럼 중첩 클래스에서 static과 non-static의 차이는?

중첩 클래스는 하나의 클래스를 통해 논리적으로 그룹화할 수 있고, 캡슐화와 쉬운 코드가 가능하다. 상황에 따라 사용할 일이 있을 것이다.

여기서 중첩 클래스는 static 또는 non-static으로 구현할 수 있다. (중첩 클래스를 구현할 수 있는 방법은 4가지가 있지만.. 그걸 설명하려던건 아니므로 static과 inner class 으로만 보겠다.)

 

코드로 사용상 차이점을 살펴보면 쉬울것 같다.

 

B는 static 중첩 클래스이고, C는 Inner 클래스이다. 사용상 큰 차이점은 A객체를 생성해야 하느냐 차이이다. 

사실상 C는 A에 의존하고 있다. B는 A클래스가 어떤 상태이든 상관하지 않으므로 덜 의존적이다. (아예 의존이 없다고 할 수는 없을것 같고...)

 

그렇다면 메모리상에서는 어떤 차이가 있을까?

A클래스와 C클래스 객체는 당연히 힙메모리에 일반 객체와 같이 들어가게 될것이다. 그럼 static 중첩 클래스인 B 객체는 어떻게 될까?

답부터 이야기하면 당연히 힙메모리로 들어간다.

 

static 메소드와 혼동하면 안된다. 객체 생성이 없이 사용 가능한 static 메소드는 heap 메모리에 안올라 간다. static 메소드는 Class 객체에서 가지고 있는 바이트코드를 실행하는 것이다.

(메소드의 바이트코드가 metaspace 영역에 실행가능한 상태로 로드되어 있다.) 당연히 과정 중 객체 생성이 없다. static 필드도 마찬가지.

 

static 중첩 클래스는 Outer 클래스 A의 프록시 형태처럼 보이는 객체를 생성한다. 생성된 객체를 살펴보면 아래와 같다.

A 객체 : A@7b1d7fff 

C 객체 : A$C@61064425

B 객체 : A$B@e73f9ac

 

$ 표시로 내부적으로 이너클래스를 가리킨다.

 

 

정리하자면

static 키워드는 Outer 클래스의 객체 생성이 필요 없다는 것이다.

static 중첩 클래스도 마찬가지로 Outer 클래스의 객체 생성이 필요 없다는 것이지, 해당 클래스의 객체 생성이 필요없다는 것이 아니다.

 

 

 

 

 

ps.

Java 8 에서 Permanent 영역이 Metaspace 로 변경되었다. 차이가 무엇일까?

크게 변경된 점은 없다. 단지 더 적합한 이름으로 변경된 것이다. 

Permanent 영역은 고정된 크기의 사이즈만 가지고 있었다. 반면 Metaspace는 동적으로 크기가 커질 수 있다. Permanent 영역이 도입되었을 때는 동적으로 클래스 언로딩(unloading)이 없었다. 그래서 JVM이 내려가기 전까지 영구적이었기 때문에 Permanenet 영역이었다. Java 8에서부터 클래스는 JVM LifeCycle 동안 로드&언로드 될 수 있다. 따라서 permanent 라는 명칭보다 metaspace 라는 의미가 타당하기 때문에 변경되었다.

Permanent 영역이 heap 영역이었는데, non-heap 영역으로 변경되므로 heap 영역이 더 커지게 되었고, metaspace에서 OOM이 발생할 수 있으며 GC도 일어난다.

 

 

 

 

 

참고

http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

https://blogs.oracle.com/jonthecollector/presenting-the-permanent-generation

https://programmingmitra.blogspot.kr/2016/10/why-outer-java-class-cant-be-static.html

https://stackoverflow.com/questions/3849634/static-allocation-in-java-heap-stack-and-permanent-generation/3849819#3849819

https://stackoverflow.com/questions/27131165/what-is-the-difference-between-permgen-and-metaspace

https://plumbr.eu/outofmemoryerror/metaspace

https://www.slideshare.net/javajigi/java-virtual-machine-call-stack-java-byte-code

 

'개발 > Java' 카테고리의 다른 글

Java - Generics  (0) 2019.12.04
Java 9 features  (0) 2017.11.10
Java 8 - Stream  (0) 2017.08.11
Java 8 - Lambda Translation  (0) 2017.08.10
Java 8 - 람다 표현식  (2) 2017.08.10
Comments