Java Performance Tuning

Java Performance Tuning Thumbnail

안녕하세요. Java Performance Tuning 관련하여 내용들을 정리해보고자 합니다.
Java를 주력으로 하고 있음에도 너무 여러가지를 공부하다보니 정작 사이트에 자바와 관련된 내용들이 별로 없는 것 같아 작성합니다.

간단하게 어플리케이션을 만드는 일이라면 별로 신경쓰지 않아도 되는 부분입니다만, 실제 운영 서비스에 적용하면 생각보다 성능상 문제가 많이 발생합니다.
그럼 지금 작성하게 되는 내용에 대해 직면하게 됩니다.

지금부터 작성할 내용은 자바 성능 튜닝에 대해 작성할 예정인데, 지금 겪고 있는 문제로 인해 찾아본 내용들에 대해 정리하여 작성하는 부분이니 다른 의견이 있으시다면 댓글로 의견을 공유해주시면 감사하겠습니다.

코드 작성 방법 : 디자인 패턴

소프트웨어 공학에서 다루는 좋은 코드를 설계하기 위한 설계 디자인 방법론입니다.
좋은 코드란 ‘가독성이 좋다’, ‘간결하다’ 등의 여러 의미가 있을 수 있지만, 여기서 다루는 관점은 ‘설계적 관점’입니다.
디자인 패턴이란 어플리케이션을 설계하면서 발생했던 문제들에 대해 어떻게하면 회피할 수 있을지 고민하면서 사용된 패턴들을 말합니다.
여기서 말하는 문제는 여러가지가 있습니다.

  • 여러 사람들과 협업하면서 발생하는 문제
    • 다른 사람의 코드와의 연계
    • 기존 코드의 수정으로 인한 의도치 않은 결과 또는 버그
    • 새로운 기능 추가로 인한 의도치 않은 결과 또는 버그
  • 코드 재사용의 어려움

위에 적어놓은 문제 외에도 다양한 문제들을 접하게 되는데, 이런 문제들을 최소화하기 위해 고안된 설계 방법입니다.
어플리케이션의 종류가 다양하듯이 그 구조 또한 다양하게 적용될 수 있기 때문에 후보군으로 고려되는 디자인 패턴의 종류는 다양합니다.
이런 패턴을 적용하게되면 확장과 수정에 용이하여 차후 유지보수에도 어려움(비용)이 적습니다.

디자인 패턴의 종류 중 MVC 패턴이 가장 대표적이라고 할 수 있는데, 이 내용도 방대하여 다른 포스트에서 다룰 수 있도록 하겠습니다.

String 사용의 최소화

많은 퍼포먼스 튜닝 가이드에서 다루는 내용인데, String 객체 사용의 최소화를 권장합니다.
그 이유는 String 객체의 특성 때문인데, Java 책에서도 DataType을 다루면서 String을 따로 챕터로 빼내서 다루는 이유이기도 합니다.

String에 대해 이해하기 위해서는 JVM에 대해 어느정도 선수지식이 있어야 합니다. String은 Heap 메모리에 데이터를 저장합니다.
사용방법은 Int, char 같은 데이터 타입의 변수들과 크게 다르지 않아 동일하게 보는 경우가 많은데, 그 본질 자체가 다른 객체입니다.

String a = "String 객체";
System.out.println(a.hashCode());
a += "새로운 메모리 할당";
System.out.println(a.hashCode());

위와 같은 코드를 아무 클래스나 생성해서 실행해보면 제가 이야기하고 싶은 내용의 전체가 나온 것과 다름없습니다.

hashCode() 메소드는 해당 객체의 주소값을 출력해주는데, 똑같은 ‘a’에 대해 주소값을 출력했지만 주소가 다르게 나타나는 것을 알 수 있습니다.
String 클래스는 불변 클래스라 새로운 주소로 할당된 메모리에 연산된 값을 넣는다는 것을 알 수 있습니다.

메모리를 새로 할당받아 사용하는 것에는 큰 비용(Cost)가 소요됩니다. 한 두번의 절차라면 별로 상관 없을 수 있지만, 그 로직이 계속 반복되는 것이라면 성능에 크게 영향을 주게 됩니다.

그렇기 때문에 StringBuilder 사용을 권고합니다.
StringBuilder에서 위와 같은 연산을 할 때 aapend() 메소드를 사용하게 되는데, 이 방식은 기존 값에 새로운 값을 덧붙이는 작업을 해주기 때문에 String과는 다른 방식입니다.

StringBuilder sb = new StringBuilder();
sb.append("String 객체");
System.out.println(sb.hashCode());
sb.append("값의 변경만 있음");
System.out.println(sb.hashCode());

로그의 사용 방법

어플리케이션의 사용기록을 확인하거나 차후 유지보수를 위해 우리는 로그를 남기게 됩니다.
이 방법에 대해서도 고민해보고 사용해야 합니다.
흔한 Java 일반 개념을 다루는 지식서에서는 ‘System.out.println()’을 사용하는 것으로 그치게 됩니다.
하지만 실제 운영하기 위한 어플리케이션에서는 다른 방식으로 로그를 남겨야합니다.

‘System.out.println’ 메소드는 입출력을 통해 작업을 수행하는데, 여기서 사용되는 입출력 일련의 작업이 완전히 수행될 때까지 스레드는 대기하게 됩니다.

int[] array = new int[100];
int sum = 0;
for(int i = 0 ; i < array.length() ; i++){
    sum += array[i];
    //System.out.println(sum);
}

위 코드를 아무 클래스나 생성하여 실행해보시면 바로 이해하실 수 있습니다.
주석처리된 출력문이 있을 경우와 없을 경우를 비교하여 언제 빨리 작업이 마무리되는지 확인해보시면 될 것 같습니다.

일반적으로 로그를 남기는 방식으로 LOGGER를 사용합니다.
slf4j 와 Logback 두 오픈소스를 조합하여 사용하는데, 이 방법에 대해서도 차후 포스트를 통해 공유드리도록 하겠습니다.(이미 많은 포스트가 있어서 검색해보시면 많이 나올 것 같습니다.)

GC 튜닝

되도록 어플리케이션 설계 부분부터 코드 레벨에서 해결하는 것이 좋습니다.
그리고나서 최종적으로 GC튜닝 영역에 발을 들이시는 것을 추천드립니다.
실제로도 그렇게 권고하고 있습니다.
GC는 Garbage Collector의 준말로써, Heap 영역에서의 더이상 참조되지 않는 영역들을 정리해주어 개발자들의 수고를 덜어주는 자동 처리 시스템(?)이라고 보시면 되겠습니다.

GC에 대해서는 여러 포스트로 나누어 소개를 해드려야 할 것 같아 여기서는 간단하게만 작성하고 넘어가려고 합니다.

Updated by 20.09.29 Java Performance Tuning
Site : @ThinkGround
Instagram : @thinkground_official
Facebook : @ThinkGround
Twitter : @ThinkG_Flature

Leave a Reply