IT/JAVA

코드 실행시간 비교

iamhyeon 2025. 3. 26. 18:23

프로그래머스 문제를 풀다가 코드 실행 시간을 비교해 보고 싶었다.

 

 

📝  Java에서 성능 측정을 위한 헬퍼 클래스

작업을 여러 번 실행하여 평균 실행 시간을 측정하고 결과를 출력

제네릭을 사용하여 다양한 타입의 작업을 처리 

package Java.helpers;

public class BenchmarkHelper {

    /**
     * 작업을 여러 번 실행하여 평균 실행 시간 측정하고 결과를 출력한다. 
     * @param name : 벤치마크 테스트 이름 
     * @param iteration : 작업 반복 횟수
     * @param task : 벤치마크 테스트에서 실행할 작업 
     */
    public static <T> void runBenchmark (String name, int iteration, BenchmarkTask<T> task) {
        long sum = 0;
        T result = null;

        for (int i=0; i<iteration; i++) {
            long start = System.nanoTime();
            result = task.run();
            long end = System.nanoTime();
            sum += end-start;
        }

        System.out.println(name + " 결과 : " + result);
        System.out.println(name + " 실행 시간 : " + (sum/iteration) + "ns");
        System.out.println("------------------------------------");
    }

    /**
     * 벤치마크 테스트에서 실행할 작업 정의 
     */
    public interface BenchmarkTask<T> {
        T run();        
    }
}

 

 

📝 BenchmarkHelper 클래스 사용 예제

import Java.helpers.BenchmarkHelper;

public class 괄호회전하기_test {
    
    public static void main(String[] args) {
        String s = "[](){}[](){}[](){}[](){}[](){}[](){}[](){}[](){}[](){}[](){}";

        괄호회전하기 s1 = new 괄호회전하기();
        괄호회전하기_2 s2 = new 괄호회전하기_2();

        // 람다 표현식을 사용하여 BenchmarkTask 인터페이스의 run 메서드를 구현 
        BenchmarkHelper.runBenchmark("Solution 1", 10, ()->s1.solution(s));        
        BenchmarkHelper.runBenchmark("Solution 2", 10, ()->s2.solution(s));
    }
}

 


 

📝 System.gc()

Java에서 가비지 컬렉션(Garbage Collection)을 명시적으로 요청하는 메서드이다
하지만 "요청"일 뿐, 반드시 실행된다는 보장은 없다

Java의 가비지 컬렉터(GC)에게 "지금 메모리 좀 청소해줘~"라고 요청하는 메서드이다 
내부적으로는 Runtime.getRuntime().gc()를 호출한다

가비지 컬렉션(GC)
Java에서는 new 키워드로 만든 객체가 더 이상 사용되지 않으면 자동으로 메모리에서 제거된다 
이걸 담당하는 것이 JVM의 가비지 컬렉터(Garbage Collector)이다 

 

GC를 “실행해주면 좋겠다”라는 요청이어서 JVM이 무시할 수도 있다
성능을 떨어뜨릴 수 있기 때문에, 일반 애플리케이션에서 남용 금지
메모리 상태를 비우고 공평하게 만들기 위해 쓰기도 하지만, 오히려 왜곡된 결과가 나올 수도 있다 

 

벤치마크 코드에서, 각 실행 전 메모리 초기화를 위해 시도해볼 수 있다 
단, GC는 무겁기 때문에 오히려 벤치마크에 지장을 줄 수 있다 
메모리 릭 테스트 중 GC 반응 확인용으로 시도한다 

 

대부분 추천되지 않는다
JVM은 GC 실행 타이밍을 스스로 제어하고, 대부분의 경우 System.gc() 없이도 충분히 잘 관리한다 

벤치마크용 warm-up 루틴으로 GC 대신 몇 번 미리 실행하고 평균을 재는 게 더 효과적이다 

 


 

 JVM의 메모리 구조

주요 영역 

영역 설명
Heap 객체가 생성되는 공간. GC의 대상이 되는 핵심 영역
Stack 메서드 호출 시 지역 변수, 매개변수, 반환 주소 등이 저장됨
Method Area 클래스 정보(메타데이터), static 변수 등
PC Register 현재 실행 중인 명령어의 주소 저장
Native Method Stack C/C++ 등 네이티브 코드 호출 시 사용

가비지 컬렉션은 Heap 영역에서만 동작한다 

 
GC의 기본 동작 단계
대표 방식 : Mark-Sweep-Compact 
단계 설명 
Mark GC Root(스레드, static 변수 등)부터 시작해 접근 가능한 객체를 마킹
Sweep 마킹되지 않은 객체를 삭제
Compact 메모리 단편화를 막기 위해 남은 객체를 압축 정렬

 

 

Stop-the-World(STW)

가비지 컬렉션 중 JVM이 모든 애플리케이션 스레드를 멈추는 현상 

GC 중에 다른 스레드가 메모리를 수정하면 일관성 문제가 생긴다 
GC가 실행되는 동안 모든 작업을 멈춘다 → STW 발생
GC 알고리즘에 따라 STW 시간의 길이와 빈도가 다르다 

 

System.gc()를 호출하면 JVM은 Full GC를 유도하는 경우가 많다
Full GC는 Old 영역을 정리하는 비용 높은 작업이라, STW 시간이 길어질 수 있다
👉 불필요하게 자주 호출하면 프로그램이 멈춘 것처럼 느려질 수 있다 

 


- System.gc() : GC를 요청하지만 실행 보장은 없다 
- GC 대상 : Heap에 존재하는 더 이상 참조되지 않는 객체들
- GC 과정 : Mark → Sweep → (Compact)
- STW : GC 수행 시 모든 애플리케이션 스레드 정지

'IT > JAVA' 카테고리의 다른 글

JMH (JMH 라이브러리를 활용한 Java 코드 성능테스트)  (2) 2025.04.11
ArrayList vs LinkedList  (0) 2025.04.08
HashSet  (0) 2025.03.20
Pattern, Matcher Class  (0) 2025.02.25
StringBuilder  (0) 2025.01.19