자바에서 배열을 복사할 때, System.arraycopy()가 빠르다는 이야기를 종종 들을 수 있다. 하지만 왜 이 메서드가 다른 방법보다 빠를까? 그 이유를 찾아가며 System.arraycopy()와 다른 방식(예: for 루프)의 실행 시간을 비교해보았다.
System.arraycopy()는 자바에서 배열을 복사하는 대표적인 메서드이다. 이 메서드는 매우 효율적으로 배열을 복사할 수 있는데, 이는 단순한 자바 코드에서 이루어지는 것이 아니라, JVM이 내부적으로 최적화를 적용할 수 있기 때문이다.
System.arraycopy()가 빠른 이유를 찾아보던 중, 이 메서드에 @IntrinsicCandidate라는 어노테이션이 붙어 있는 것을 확인했다.
@IntrinsicCandidate는 HotSpot VM이 내부적으로 최적화할 수 있는 메서드 후보임을 나타내는 어노테이션이다. 이 어노테이션이 붙은 메서드는 JVM이 최적화된 네이티브 코드를 호출할 수 있도록 처리된다. 즉, System.arraycopy()는 자바 레벨에서 배열을 하나하나 복사하는 대신, JVM이 배열을 효율적으로 처리할 수 있도록 최적화된 방법으로 변환하는 것이다.
하지만 이 어노테이션이 붙었다고 해서 반드시 항상 최적화가 적용된다는 보장은 없다. JVM의 설정이나 실행 환경에 따라 달라질 수 있기 때문이다.
이제 System.arraycopy()와 일반적인 for 루프를 사용하여 배열을 복사할 때의 실행 시간을 비교해보자. 아래는 두 방법의 성능을 비교하는 코드이다.
public class Main {
public static void main(String[] args) {
int[] src = new int[1000000];
int[] dest1 = new int[1000000];
int[] dest2 = new int[1000000];
// 배열을 채우기
for (int i = 0; i < src.length; i++) {
src[i] = i;
}
// System.arraycopy() 사용
long startTime1 = System.nanoTime();
System.arraycopy(src, 0, dest1, 0, src.length);
long endTime1 = System.nanoTime();
System.out.println("System.arraycopy() 시간: " + (endTime1 - startTime1) + " ns");
// for 루프 사용
long startTime2 = System.nanoTime();
for (int i = 0; i < src.length; i++) {
dest2[i] = src[i];
}
long endTime2 = System.nanoTime();
System.out.println("for 루프 시간: " + (endTime2 - startTime2) + " ns");
}
}
코드의 실행 결과는 아래와 같다.

실행 결과, System.arraycopy()가 일반적인 for 루프보다 훨씬 빠르게 배열을 복사하는 것을 확인할 수 있다. 그 이유는 System.arraycopy()가 JVM에서 네이티브 코드 수준으로 배열 복사 작업을 처리하기 때문이다. 반면 for 루프는 각 배열 요소를 하나씩 복사하기 때문에 자바 레벨에서 작업이 수행되며, 상대적으로 시간이 더 오래 걸린다.
System.arraycopy()가 빠른 이유는 @IntrinsicCandidate 어노테이션 덕분에 JVM이 내부적으로 최적화할 수 있는 메서드로 처리되기 때문이다. 이 메서드는 JVM이 배열 복사 작업을 네이티브 코드로 최적화하여 성능을 극대화할 수 있다.
성능이 중요한 배열 복사 작업에서는 System.arraycopy()를 사용하는 것이 일반적인 for 루프보다 훨씬 효율적이다. 이번 글을 통해 왜 이 메서드가 빠른지 이해하고, 실제 성능 차이를 확인할 수 있었다.