출처 : 책 자바 성능 튜닝이야기 -'왜 자꾸 String을 쓰지 말라는거야' 편

 

이 책에 나온 초보 개발자 나초보는 서비스를 개발하고 나서 보니 GC가 많이 발생된단 사실을 알게 되었고

성능을 올리기 위해 GC에 영향을 끼치고 있던 String 부분을 개선해보기로 하였다. 

 

문제를 일으키던 부분은

String a = "test"; / for문을 통해 a += "XXX";

이런식으로 String에다 계속 값을 더하는 부분이었다.

String을 이렇게 사용하면 성능 적인 부분에서 문제를 일으킬 수 있다.

 

for문이 100,1000번 이상 돌아간다 가정해보면 메모리 사용률이 올라가고, 응답속도는 낮아질 것이다. 

 

그렇다면 어떻게 해야 할까?

  • StringBuffer 클래스와 StringBuilder 클래스를 사용하여야 한다. 
    • append() 메서드를 활용

 

StringBuffer 클래스와 StringBuilder 클래스의 차이는 

StringBuffer 클래스는 스레드에 안전하게 설계되어 있으므로 여러 개의 스레드에서 하나의 StringBuffer객체를 처리해도 전혀 문제가 되지 않는다. 반면 StringBuilder 클래스는 단일 스레드에서의 안정성만을 보장한다. 

 

String a = new String();
StringBuffer b = new StringBuffer();
StringBuilder c = new StringBuilder();

for(int loop = 0 ; loop < 10000; loop++) {
	a += "abcde";
}

for(int loop = 0 ; loop < 10000; loop++) {
	b.append("abcde");
}
String tempB = b.toString();

for(int loop = 0 ; loop < 10000; loop++) {
	c.append("abcde");
}
String tempC = c.toString();

이런 소스가 실행된다면 결과가 어떻게 될까? 어느 것이 가장 빠르고 메모리를 적게 사용할까? 

 

  응답시간 생성된 임시 객체수 메모리 사용량
a (String) 95초 4,000,000 약 95Gb
tempB (StringBuffer) 0.24초 1400 약 37.5Mb
tempC (StringBuilder) 0.17초 1400 약 37.5Mb

그렇다, 응답시간과 메모리 사용량에서 엄청난 차이가 보인다

응답시간과 메모리 사용량에서 가장 좋은 성능을 보인건 StringBuilder이다. 

 

왜 이럴까?

새로이 더해진 문자열은 새로운 주소를 갖는 객체가 생성이 되기 때문이다. 

a에 "abcde"를 더하면 새로운 String클래스의 객체가 만들어지고, 이전에 있던 a객체는 필요 없는 쓰레기 값이 되어 GC대상이 된다. 이러한 작업이 반복 수행되면서 메모리를 많이 사용하게 되고, 응답 속도에도 많은 영향을 미치게 된다. GC를 하면 할수록 시스템의 CPU를 사용하게 되고 시간도 많이 소요된다. 그래서 프로그래밍을 할 때 메모리 사용을 최소화하는 것은 당연한 일이다. 

 

Wrap-up

  • String은 짧은 문자열을 더할 경우 사용한다.
  • StringBuffer는 스레드에 안전한 프로그램이 필요할 때나, 개발 중인 시스템의 부분이 스레드에 안전한지 모를 경우 사용하면 좋다. (클래스에 static으로 선언한 문자열을 변경하거나, singleton으로 선언된 클래스에 선언된 문자)
  • StringBuilder는 스레드에 안전한지의 여부와 전혀 관계 없는 프로그램을 개발할 때 사용하면 좋다. 메서드 내에 변수를 선언하는 경우가 이에 해당되겠다.
  • WAS나 시스템이 JDK5.0 이상을 사용한다면, 컴파일러에서 자동으로 StringBuilder로 변환해 주긴 한다. 하지만 반복 루프를 사용해서 문자열을 더할 때는 객체를 계속 추가한다는 사실에 변함이 없으므로 StringBuilder, StringBuffer사용을 권장한다. 

 

http://www.corej2eepatterns.com/SessionFacade.htm

 

자바 성능 튜닝 이야기 책에서 말한 핵심 디자인 패턴을 정리해보려고 한다. 

 

Business Delegate

Business Delegate를 사용하여 비즈니스 서비스에 대한 액세스를 캡슐화. Business Delegate는 조회 및 액세스 메커니즘과 같은 비즈니스 서비스의 구현 세부 정보를 숨깁니다.

 

Problem

비즈니스 서비스 구성 요소와의 원격 통신의 복잡성으로부터 클라이언트를 숨기고 싶습니다.

Forces

  1. 장치, 웹 서비스 및 리치 클라이언트와 같은 프레젠테이션 계층 구성 요소 및 클라이언트에서 비즈니스 계층 구성 요소에 액세스하려고 합니다.
  2. 클라이언트와 비즈니스 서비스 간의 결합을 최소화하여 조회 및 액세스와 같은 서비스의 기본 구현 세부 정보를 숨기려고 합니다.
  3. 원격 서비스의 불필요한 호출을 피하려고 합니다.
  4. 네트워크 예외를 애플리케이션 또는 사용자 예외로 변환하려고 합니다.
  5. 클라이언트에서 서비스 생성, 재구성 및 호출 재시도에 대한 세부 정보를 숨기려고 합니다.

Consequences

커플링 감소, 유지보수성 향상, 비즈니스 서비스 예외 번역, 가용성 향상, 비즈니스 계층에 더 간단하고 균일한 인터페이스를 제공, 성능 향상, 추가 레이어 도입

구현 방법

https://www.tutorialspoint.com/design_pattern/business_delegate_pattern.htm


Session Facade

비즈니스 계층 구성 요소를 캡슐화하고 거친 서비스를 원격 클라이언트에 노출합니다. 클라이언트는 비즈니스 구성 요소에 직접 액세스하는 대신 Session Facade에 액세스합니다.

 

Problem

비즈니스 구성 요소 및 서비스를 원격 클라이언트에 노출하려고 합니다.
Forces

 

  1. 클라이언트와의 긴밀한 결합을 방지하기 위해 클라이언트가 비즈니스 계층 구성 요소에 직접 액세스할 수 없도록 하고 싶습니다.
  2. Business Objects 및 기타 비즈니스 계층 구성 요소에 원격 액세스 계층을 제공하려고 합니다.
  3. 애플리케이션 서비스 및 기타 서비스를 집계하여 원격 클라이언트에 노출하려고 합니다.
  4. 원격 클라이언트에 노출되어야 하는 모든 비즈니스 논리를 중앙 집중화하고 집계하려고 합니다.
  5. 비즈니스 구성 요소와 서비스 간의 복잡한 상호 작용 및 상호 종속성을 숨겨서 관리 용이성을 높이고 논리를 중앙 집중화하고 유연성을 높이고 변경 사항에 대처하는 능력을 개선하려고 합니다.

Consequences

원격 클라이언트에 서비스를 제공하는 계층 도입, 계층 간의 결합 감소, 레이어링을 촉진하고 유연성과 유지보수성을 높입니다, 성능 향상, 세분화된 원격 방법 감소, 중앙 집중식 보안 관리, 트랜잭션 제어 중앙 집중화, 클라이언트에 더 적은 수의 원격 인터페이스 노출

구현 방법

https://www.tutorialspoint.com/design_pattern/facade_pattern.htm

 


Data Access Object

데이터 액세스 개체를 사용하여 영구 저장소에 대한 모든 액세스를 추상화하고 캡슐화합니다. 데이터 액세스 개체는 데이터를 가져오고 저장하기 위해 데이터 원본과의 연결을 관리합니다.

Problem

데이터 액세스 및 조작을 별도의 레이어에 캡슐화하려고 합니다.

Forces

  1. 영구 저장소의 데이터에 액세스하고 조작하기 위해 데이터 액세스 메커니즘을 구현하려고 합니다.
  2. 애플리케이션의 나머지 부분에서 영구 저장소 구현을 decouple하려고 합니다.
  3. RDBMS, LDAP, OODB, XML 리포지토리, 플랫 파일 등과 같은 다양한 유형의 데이터 소스에 대한 영구 메커니즘에 대한 균일한 데이터 액세스 API를 제공하려고 합니다.
  4. 데이터 액세스 논리를 구성하고 독점 기능을 캡슐화하여 유지 관리 및 이식성을 용이하게 하려고 합니다.

Consequences

중앙 집중식 제어, 투명한 사용성, 객체 지향 보기를 제공하고 데이터베이스 스키마를 캡슐화, 마이그레이션 용이, 모든 데이터 액세스 코드를 별도의 레이어로 구성

구현 방법

https://www.tutorialspoint.com/design_pattern/data_access_object_pattern.htm


Service Locator

서비스 로케이터를 사용하여 서비스 및 구성 요소 조회를 구현하고 캡슐화합니다. 서비스 로케이터는 조회 메커니즘의 구현 세부 정보를 숨기고 관련 종속성을 캡슐화합니다.

Problem

균일한 방식으로 비즈니스 구성 요소와 서비스를 투명하게 찾고자 합니다.

Forces

  1. JNDI API를 사용하여 엔터프라이즈 Bean 및 JMS 구성요소와 같은 비즈니스 구성요소와 데이터 소스와 같은 서비스를 조회하고 사용하려고 합니다.
  2. J2EE 애플리케이션 클라이언트에 대한 조회 메커니즘 구현을 중앙 집중화하고 재사용하려고 합니다.
  3. 레지스트리 구현에 대한 공급업체 종속성을 캡슐화하고 클라이언트로부터 종속성과 복잡성을 숨기려고 합니다.
  4. 초기 컨텍스트 생성 및 서비스 조회와 관련된 성능 오버헤드를 피하려고 합니다.
  5. 핸들 오브젝트를 사용하여 이전에 액세스한 엔터프라이즈 Bean 인스턴스에 대한 연결을 재설정하려고 합니다.

+ JNDI는 Java Naming and Directory Interface API의 머리글자입니다. 디렉터리 서비스에서 제공하는 데이터 및 객체를 발견(discover)하고 참고(lookup) 하기 위한 자바 API.

 

Consequences

추상화된 복잡성, 클라이언트에게 균일한 서비스 액세스 제공, EJB 비즈니스 구성 요소 추가 촉진, 네트워크 성능 향상, 캐싱을 통해 클라이언트 성능 향상

구현 방법

https://www.tutorialspoint.com/design_pattern/service_locator_pattern.htm


Transfer Object

전송 개체를 사용하여 계층 전체에 여러 데이터 요소를 전달합니다.

Problem

계층을 통해 여러 데이터 요소를 전송하려고 합니다.

Forces

  1. 클라이언트가 다른 계층의 구성 요소에 액세스하여 데이터를 검색하고 업데이트하도록 하려고 합니다.
  2. 네트워크를 통한 원격 요청을 줄이려고 합니다.
  3. 높은 네트워크 트래픽이 있는 응용 프로그램으로 인한 네트워크 성능 저하를 방지하려고 합니다.

Consequences

네트워크 트래픽 감소, 원격 개체 및 원격 인터페이스 단순화, 더 적은 수의 원격 호출로 더 많은 데이터 전송, 코드 중복 감소

 

구현 방법

 

+ Recent posts