본문 바로가기
IT 지식/디자인 패턴

[디자인 패턴] Singleton Pattern

by 이민우 2021. 5. 24.
728x90
반응형

한 클래스가 있고, 다섯 명의 사용자가 그 클래스를 이용한다고 하자.

 

그러면 기존의 자바에서는 각 사용자가 new 키워드를 이용해 그 클래스를 생성하고 이용하는 방식을 활용했다.

 

new 키워드란 무엇인가? 새로운 객체를 만들어내는 키워드이다. 조금 구체적으로 말하자면, 객체에 메모리를 할당하여 새로운 객체를 메모리 상에 올리는 작업이다.

 

다섯 명이 각자 한 클래스를 new 키워드를 사용해 선언한다면? 당연히 다섯 개의 메모리가 할당된다.

물론 고작 다섯 개 가지고 OOM과 같은 에러가 발생할 리는 없다. 하지만 사용자들이 더 많아지면 당연히 더 많은 메모리가 할당되고, 그 수준이 서버가 감당할 수 없는 수준에 이르면 OOM이 일어나게 된다.

 

그렇다면 얼마나 많은 메모리를 사용하게 될까? 한 번 확인해보자.

간단한 클래스를 작성한다.

 

function.java

package singletonPra;

public class function {
	public void sayHi() {
		System.out.println("hi!");
	}
}

 

user.java

package singletonPra;

public class user {
	private function say = new function();
	
	public void sayHi() {
		say.sayHi();
	}
}

 

대충 5,000명의 사용자가 있다고 가정하고, 이 사용자들이 각자 new 키워드를 사용해 function을 사용하게 되면 얼마나 많은 메모리를 사용할 지 확인해보자.

 

메모리는 GC를 이용해 확인한다.

 

main.java

package singletonPra;

public class main {

	public static void main(String[] args) {
		System.gc(); //GC로 메모리 초기화.
		
		//실행 전 메모리 사용량
		long beforeMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
		
		for (int i=0; i<5000; i++) {
			user usr = new user();
			//usr.sayHi();
		}
		
		//실행 후 메모리 사용량
		long afterMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

		System.out.println("used : " + (afterMemory - beforeMemory));
	}

}

167,808 의 메모리를 사용했다.

*물론 메모리 사용량은 환경에 따라 달라질 수 있다.

 

 

SingletonPattern이란 이러한 중복 선언을 방지하는 디자인 패턴이다.

어플리케이션이 시작될 때 최초로 한 번만 new 키워드를 사용함으로써 메모리를 할당하고, 그 메모리에 인스턴스를 만들어 사용하는 디자인 패턴이다.

 

참고로 공기업 면접에서 자주 나오는 문제이니 공기업을 희망한다면 외워두자.

 

그렇다면 어떻게 한 번만 new 키워드를 사용하고 사용할 수 있을까?

정답은 변수로 자기 자신을 갖는 것이다.

거기에 추가로 외부에서 생성하지 못하도록 private으로 생성자를 구축한다.

 

SingletonPattern을 사용하면 얼마나 많은 메모리가 절약될까? 확인해보자.

 

main은 그대로 놔두고 function과 user만 바꿔주자.

 

function.java

package singletonPra;

public class function {
	private static function innerFunction = null;
	
	//private으로 만들어 외부에서 사용할 수 없게 만들기.
	private function() {}
	
	public void sayHi() {
		System.out.println("hi!");
	}

	//getInstance로 내부 function 반환
	public static function getInstance() {
		if(innerFunction == null) {
			innerFunction = new function();
		}
		
		return innerFunction;
	}
}

 

user.java

package singletonPra;

public class user {
	private function say = function.getInstance();
	
	
	public void sayHi() {
		say.sayHi();
	}
}

 

 

그리고 실행시키면 결과는 다음과 같다.

83,904

 

 

메모리 사용량이 약 두 배 정도 줄어들었음을 확인할 수 있다.

 

이처럼 싱글톤 패턴은 고정된 메모리 영역으로 한 번만 객체를 할당하기에 메모리 낭비를 방지할 수 있다.

또한 이 인스턴스는 전역 인스턴스이기 때문에 객체들이 데이터를 공유하여 공통된 객체를 여러 개 만들어 자료를 공유해야 하는 상황에 유용하게 사용할 수 있다.

 

하지만 그렇다고 싱글톤 패턴이 만능은 아닌 것이, 싱글톤이 너무 많은 일을 하거나 너무 많은 데이터를 공유하면 다른 클래스의 인스턴스간 결합도가 높아지게 되고, 수정이 어려워지고 테스트가 어려워진다.

 

또한 멀티 쓰레드 환경에서는 동기화가 필수이다. 만약 그렇지 않으면 인스턴스가 여러 개가 생성될 수 있다.

 

 

 

 

+) 갑자기 생긴 궁금증

그러면 그냥 new로 한 번만 선언하고 static으로 관리하면 어떨까? 한 번 해보자.

 

function.java

package singletonPra;

public class function {
	//private으로 만들어 외부에서 사용할 수 없게 만들기.
	public function() {}
	
	public void sayHi() {
		System.out.println("hi!");
	}
}

 

user.java

package singletonPra;

public class user {
	private static function say = new function();
	
	
	public static void sayHi() {
		say.sayHi();
	}
}

 

똑같다. 아무래도 똑같이 new는 한 번만 사용해서 그런 것 같다.

728x90
반응형

'IT 지식 > 디자인 패턴' 카테고리의 다른 글

[디자인 패턴] MSA 아키텍처  (0) 2021.05.17