일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- RequestBody
- Test Doulbe
- ObjectMapper
- Java
- backend
- enumSet
- 인프콘2023
- Service 계층 테스트
- FCM
- Spring
- 자바
- 소프티어
- proxyFactory
- 공룡책
- JPQL
- 일상
- Server
- OS
- Test code
- JPA
- Coputer Science
- Junit 5
- db
- Test
- 테크쇼
- mapstruct
- modelmapper
- softeer
- MySQL
- ExceptionResolver
- Today
- Total
공부내용공유
java inner 클래스 static? non-static? (이펙티브 자바 item 24) 본문
서론
아주 예전에 자바 공부를 할 때 'inner 클래스는 비정적으로 사용하면 메모리 누수가 일어나니 조심하자' 라는 내용의 블로그 글을 본적이 있었고 어렴풋하게 내 머리에 남아있었다.
그러던 도중 이펙티브 자바 item 24. 멤버 클래스는 되도록 static으로 만들어라 라는 주제에 대해 스터디를 하면서 비정적 멤버 클래스(non-static)
, 정적 멤버 클래스(static)
의 차이와 어떤 때 사용하게 되는지 알게 되었고 해당 글에서 간단히 정리할 예정이다.
본론
이 글은 자바의 멤버 클래스 (inner class)와 static에 대한 기본적인 이해가 있다는 가정하에 작성되어졌다.
목차는
- 비정적 멤버 클래스를 사용했을 때 생길 수 있는 문제점
- HashMap은 이걸 사용한다.
- 문제가 있을 수 있나?
로 이루어질 예정이다.
비정적 멤버 클래스를 사용했을 때 생길 수 있는 문제점
일단 이전에 블로그 글에서 봤던 메모리 누수 문제는 어떻게 발생하는 것일까?
public class Member {
private int age;
private String name;
private String id;
private String [] items;
public Member(int age, String name, String id, int size) {
this.age = age;
this.name = name;
this.id = id;
this.items = new String [size];
}
public class Job {
private String jobName;
private int rank;
public Job(String name, int rank) {
this.name = name;
this.rank = rank;
}
}
pulbic Job getJob() {
return new Job(RandomJob.getJob(), RankUtil.getRankByAge(this.age));
}
}
이런식으로 외부 클래스와 멤버 클래스가 있다고 할 때 현재 코드에서는 확인할 수 없지만 컴파일된 코드에서는 내부 클래스 생성자에 외부 클래스가 변수로 들어가게된다.
즉, 내부 클래스가 외부 클래스를 참조한다는 것이다. 이게 어떤 문제를 유발할 수 있을까?? 해당 실험은 내가 예전에 봤던 블로그에서 실험을 굉장히 잘 정리해 주셨는데 내 예시로 똑같이 실험을 한다고 하면
ArrayList<Object> list = new ArrayList<>();
for(int i = 0; i < 100; i++) {
list.add(new Member(14,"name","id",1000000).);
}
대략 이런식으로 멤버의 job 배열을 만든다고 해보자, 우리는 현재 job 정보만 필요하기에 job 정보만 list에 넣고있고 큰 데이터를 가지고 있는 멤버 데이터는 gc에 의해 삭제될거라 생각한다.
하지만 아까 위에서 언급한 것처럼 Job은 Member를 참조하고 있기 때문에 Member 인스턴스들은 힙 메모리에 누적되고 (메모리 누수) oom이 터질 수 있다.
그러면 비정적 멤버 클래스는 절대 사용하면 안되는걸까??
HashMap은 이걸 사용한다.
final class KeySet extends AbstractSet<K> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<K> iterator() { return new KeyIterator(); }
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
public final Spliterator<K> spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public Object[] toArray() {
return keysToArray(new Object[size]);
}
public <T> T[] toArray(T[] a) {
return keysToArray(prepareArray(a));
}
public final void forEach(Consumer<? super K> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (Node<K,V> e : tab) {
for (; e != null; e = e.next)
action.accept(e.key);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
HashMap 클래스에서 지원하는 KeySet이라는 멤버 클래스이다, 그리고 코드에서 확인할 수 있듯이 비정적 클래스이다.
웬만하면 멤버 클래스는 비정적으로 만들라고 했는데 왜 이렇게 작성했을까? 언제 이렇게 사용해야할까? 이펙티브 자바에서는 어댑터 패턴, 즉 어떠한 클래스에서 다른 타입의 인스턴스를 리턴하기 위해 사용한다고 나와있다.
HashMap에서도 key에 대해 Set 클래스의 인터페이스를 제공하기 위해 해당 클래스를 비정적으로 선언해 놓은것이다.
코드에서 보면 알 수 있듯이 실제로 Set이라는 자료구조를 만들지 않는다. HashMap에 존재하는 내부 자료구조를 통해 인터페이스만 지원하고 remove, size, clear 등은 있지만 add와 같은 인터페이스는 존재하지 않음을 볼 수 있다.
문제가 있을 수 있나?
결국 keySet은 HashMap을 사용하면서 좀 더 편한 사용을 위해 제공하는 인터페이스이기에 keySet만 필요한 상황이 드물것이다, 하지만 만약 hashMap을 다 사용하고 keySet만 필요하다.
그리고 HashMap의 value 데이터 크기가 큰편이라고 하면 단순히 KeySet으로 Set만 사용하니 GC가 처리해주겠지가 아니라 key 데이터만 실제 Set에 옮겨 닮으면 좀 더 메모리를 아낄 수 있지 않을까라고 생각한다.
결론
스터디를 통해 애매하게 알던 개념을 잘 정리하게 되었다. 또 실무에서 잘 안사용하는 문법이라 생각했는데 이렇게 자주 사용하는 라이브러리를 보니 중요성을 새삼 느끼게 되었다.
'Server > Java' 카테고리의 다른 글
java 예외 처리에 관하여 (feat: 이펙티브 자바 chapter 10 / 예외) (2) | 2024.09.01 |
---|---|
java 태그 달린 클래스 vs 계층 구조 (feat : 오브젝트 chap 7 객체 분해) (0) | 2024.07.07 |
Java Random, SecureRandom, RandomStringUtil 살펴보기 (0) | 2024.06.28 |
java package private (feat : default 접근 제어자) (0) | 2024.06.09 |
Value object Null 값 처리하기 (feat: null object pattern) (0) | 2024.06.02 |