[JAVA] 프로파일링 툴과 System 클래스

황제연·2025년 7월 12일
0

CS학습

목록 보기
133/193
post-thumbnail

개요

2챕터의 내용은 자바 프로그램의 성능을 파악하기 위해 프로파일링툴과 APM 툴을 소개하고
System 클래스와 관련된 내용에 대해 설명하며, 불필요한 호출을 피하는 것에 대해 설명하고 있습니다

프로파일링 툴이란?

개발자가 사용하는 소스 수준 성능 분석 도구로 애플리케이션 내부 동작을 상세히 추적하여
클래스별, 메서드별 응답 시간 및 메모리 사용량을 분석합니다

응답 시간을 측정할 때 CPU 사용 시간과 대기 시간으로 구분하여 제공하며,
메소드별로 어느 부분에서 시간이 소비되는지 파악하여 상세 분석이 가능하고,
주로 개발 단계나 테스트 환경에서 사용됩니다

과거에 주로 사용한 프로파일링 툴은 JProfiler, YourKit 입니다

최신 프로파일링 툴은?

Oracle JDK의 상용 기능이었던 Java Flight Recorder (JFR)가 OpenJDK 11부터 오픈소스로
통합되어 추가 비용 없이 사용할 수 있게 되었습니다
JFR은 "애플리케이션의 실행 중 여러 이벤트를 기록하여 성능 문제를 진단"할 수 있으며,
수집된 기록은 Java Mission Control (JMC) GUI 도구로 분석 가능합니다
또한 VisualVM, Eclipse MAT과 같은 무료 오픈소스 툴도 등장하게 되었습니다

따라서 과거 "비싼 성능 분석 툴"에 대한 의존에서 벗어나,
이제는 "오픈소스나 JDK 내장 도구로도 프로파일링 가능"하며 비용없이 성능 튜닝이 가능해졌습니다

APM 툴

운영 중인 애플리케이션의 전체적인 상태 모니터링과 문제 진단에 초점을 둔 실시간 모니터링 도구입니다
서버의 접속 사용자 수, CPU/메모리 등의 리소스 사용량, 응답 속도 등을 실시간으로 모니터링합니다
주로 운영/배포 환경에서 애플리케이션의 성능 지표를 추적합니다

과거에는 코드 레벨의 세부 분석보다는 시스템 전반의 성능을 모니터링하는 용도로,
세부 소스 코드 분석 기능은 제공하지 못한다는 한계가 책에서 언급되었습니다,
그리고 과거의 대표적인 APM툴로 언급된 것이 New Relic입니다
CPU 코어 수 기반의 비용 측정 때문에 비용이 비싸다는 단점도 언급되었습니다

최신 APM툴은 이러한 단점을 보완하고 있습니다
Scouter는 여전히 많이 쓰이고 있고, 네이버의 Pinpoint, Prometheus/Grafana 등
다양한 오픈소스 APM/모니터링 도구가 등장하여 비용없이도 오픈소스 APM으로 충분히 모니터링을 구현할 수 있게 되었습니다

또한 단순 모니터링을 넘어 부분적인 코드 레벨 분석 기능을 제공합니다.
에이전트를 통해 SQL 쿼리 실행 시간, 외부 API 호출 시간 등을 추적하여 과거 프로파일러만 가능했던 정보도 제공합니다

java.lang.System 클래스 활용 및 주의사항

이어서 툴을 사용해서 측정하는 방법 뿐만 아니라 코드를 활용해서 측정하는 방법도 있습니다
System 클래스를 활용해서 측정할 수 있는데, 관련 기능을 알아보기 전에 해당 클래스의 유용한
기능들에 대해 조금 살펴보겠습니다
System.currentTimeMillis() 또는 System.nanoTime()을 사용하여
특정 코드 블록의 실행 시간을 측정할 수 있습니다

시스템 속성과 환경변수

자바의 JVM에서 사용할 수 있는 설정은 시스템 속성(Property)와 환경변수(Enviroment)입니다

시스템 속성은 JVM 또는 애플리케이션 레벨에서 -D 옵션 등으로 설정되는 값으로 System.getProperty(key)로 가져올 수 있습니다 (예: -Denv=prod)

환경 변수는 운영체제에 지정된 값으로 System.getenv(name)으로 가져올 수 있습니다
System.getProperties()는 모든 시스템 속성을, System.getenv()는 모든 환경 변수를
Map<String,String> 형태로 반환합니다

주의해야할 System 클래스 메소드

다음 메소드 사용은 사용하지 않아야 합니다

System.gc()

가비지 컬렉터를 강제로 실행하여 JVM이 사용하는 메모리를 해제하도록 요청하는 메소드입니다
명시적인 GC 호출은 성능에 악영향을 줄 수 있으므로 피해야합니다

조금만 더 자세하게 설명하면 현대 G1 GC와 같은 새로운 GC 알고리즘의 등장으로
불필요한 GC 호출의 악영향은 더욱 명확해졌습니다
현대의 GC는 JVM이 자동으로 최적 스케줄러에 따라 관리하고 있는데,
명시적으로 gc를 실행시키면 이 스케줄링에 악영향을 줄 수 있습니다
대표적으로 Stop-the-world GC로 인한 긴 정지 시간이 발생하여 서버의 응답 시간이 증가할 수 있습니다

System.exit(int):

현재 실행 중인 자바 가상머신(JVM)을 즉시 종료시키는 메소드입니다
서버 애플리케이션이나 라이브러리 코드에서 이 메서드를 호출하면 전체 애플리케이션이 중단되므로 절대 사용하면 안 됩니다

하지만 최신 서블릿 컨테이너들은 이 호출을 무시하거나 차단하고 있어,
멀티스레드 서버 환경에서는 System.exit()를 호출해도 종료되지 않도록 방어하는 기능이 있다고 합니다

System.runFinalization():

Object 객체에 있는 finalize()라는 메소드가 자동으로 호출되는데,
가비지 콜렉터가 알아서 해당 객체를 더이상 참조할 필요가 없을 때 호출됩니다

자바에서는 GC가 필요 시에 객체의 finalize()를 알아서 호출하기 때문에,
이를 수동으로 모두 실행시키는 것은 불필요한 오버헤드를 유발하고 시스템 성능을 저하시킵니다

Java 9에서 Object.finalize() 메서드가 Deprecated로 지정되었고,
Java 18에서는 Finalization을 아예 제거하기 위한 단계(JEP 421)가 진행되었다고 합니다
따라서 이 메소드를 사용할 필요도 없고 사용해서도 안됩니다

currentTimeMillis와 nanoTime

System 클래스에서 성능 측정을 하기 위해 활용되는 주요 메소드는
System.currentTimeMillis()System.nanoTime()이 있습니다

두 메소드 모두 특정 코드 블록의 실행 전후 시간을 측정하여 성능을 평가할 때 사용됩니다System.currentTimeMillis()는 밀리초(ms) 단위로,
System.nanoTime()은 나노초(ns) 단위로 훨씬 더 정밀한 측정이 가능합니다
따라서 정확한 시간 측정이 필요한 성능 테스트에서는 nanoTime() 사용이 권장됩니다

JMH

JMH는 자바용 마이크로벤치마크 라이브러리로, JVM 내부 최적화와 JIT 컴파일 영향을 최소화하고 안정적인 벤치마크 결과를 얻기 위해 설계되었습니다. 주요 특징은 다음과 같습니다.

  • @Benchmark 어노테이션으로 벤치마크 메서드를 지정
  • 워밍업 단계(warmup)측정 단계(measurement)를 구분하여 JVM 최적화가 완료된 후 측정
  • 포크(fork) 설정으로 별도의 JVM 프로세스를 통해 벤치마크 실행
  • 스레드 수(threads), 측정 단위(timeUnit), 반복 횟수(iterations) 등 다양한 파라미터 조정

예시:

@State(Scope.Thread)
public class MyBenchmark {

    @Benchmark
    public void testArrayCopy(Blackhole bh) {
        int[] src = new int[1000];
        int[] dest = new int[1000];
        System.arraycopy(src, 0, dest, 0, src.length);
        bh.consume(dest);
    }
}

성능 측정을 위해, JMH란 라이브러리를 사용해도 되고
직접 System 클래스의 코드를 작성해서 간단하게 측정해봐도 됩니다!

참고

자바 성능 튜닝 이야기 - 2챕터

profile
Software Developer

0개의 댓글