[Java] call by value vs call by reference

devdo·2022년 1월 14일

Java

목록 보기
13/63
post-thumbnail

call by value (값에 의한 호출)

함수 호출 시 전달되는 변수(arguments)의 값(그러니까 사본이)을 복사하여 함수의 매개변수로 전달하는 것을 말한다.

나루토 분신술처럼 내모습을 복사하고 원래값은 놔두는 것이다.(자바의신 1권 209p)

Method1, Method2가 있다고 해보자.

    public void method_1() {
        int a = 10;
        int b = 5;
        
        method_2(a, b);	// arguments 전달인자
        
    }

    private void method_2(int a, int b) {	// 완전 새로운 변수임. 
        a = 20;
        b = 10;
        // method_1 의 전달인자 a, b와 완전 다른 지역변수임.
    }

자바에서는 Call by Value 방식을 수행할 때, 값을 넘겨받은 메소드에서
값을 복사하여 새로운 지역 변수에 저장하는 것이다.

이 때 Method_2는 Method_1의 변수를 사용한 것이 아니라, 자신이 새롭게 생성한 지역 변수에
복사한 Method_1의 변수 이름과 변수 값을 사용한 것이다.

이 때문에 아무리 Method_2에서 a와 b의 값을 바꾸어도 Method_1의 a, b에게
영향을 끼치지 않는다. 이 a, b 는 이름만 같을 뿐 별개의 메모리에 위치한 친구들이기 때문이다.


call by reference (주소에 의한 호출)

함수 호출 시 해당 reference 객체(메모리 주소를 담고있는 변수, 주소값)를 넘겨주는 것을 말한다.

만약 자바가 Call by Reference 방식을 사용한다면
a와 b에 영향을 끼칠 수 있게 된다. 왜냐면 이 둘은 다른 변수가 아니라 같은 주소를
공유하는 변수이기 때문
이다.

애초에 '참조 타입'인 이유가
Heap Memory 영역에 생성된 객체의 주소값을 참조하기 때문에 '참조 타입'이라고 불리는 것이다.
따라서 Method_1에서 Method_2로 넘겨준 건 객체의 주소값이고,
Method_1이 가지고 있는 주소값과 동일한 주소값을 가지고 이 객체의 상태를 변경하면

당연히 넘겨준 객체는 동일한 주소를 참조하기 때문에 변화시켜주면 영향을 주는 것이다!

어쨌든 call by reference 호출 방식도
call by value 처럼 값을 복사한 과정은 같다. 전달인자가 원시타입 value이냐 객체타입 value(주소값)이냐 다를 뿐이다.

그래서 자바는 call by value만 있다고 말하는 것이다.


🚀 자바 성능 이야기: 프리미티브 vs 참조 타입, 누가 더 빠를까?

자바 개발을 하다 보면 “프리미티브 타입보다 참조 타입이 더 빠르지 않을까?”라는 오해를 종종 듣습니다. 하지만 실제로는 프리미티브 타입이 훨씬 빠릅니다. 이번 글에서는 JMH(Java Microbenchmark Harness)를 이용해 직접 벤치마크를 돌려보고 그 이유를 설명해보겠습니다.


🔎 왜 프리미티브가 빠른가?

  • 프리미티브 타입(int, double 등)

    • 값 자체가 스택이나 레지스터에 저장됨
    • CPU가 직접 연산 → 오버헤드 없음
  • 참조 타입(Integer, Double 등)

    • Heap에 객체 생성 후 참조(주소)만 저장
    • 접근 시 Heap 메모리 참조 → 오버헤드 발생
    • 박싱(Boxing)/언박싱 과정에서 추가 비용 발생
    • GC 관리 대상 → 메모리 할당/해제 비용 증가

📊 벤치마크 코드 예시

import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Thread)
public class PrimitiveVsReferenceBenchmark {

    int primitiveValue = 42;
    Integer referenceValue = 42;

    @Benchmark
    public int testPrimitiveAdd() {
        return primitiveValue + 1;
    }

    @Benchmark
    public int testReferenceAdd() {
        return referenceValue + 1; // 언박싱 발생
    }
}

🧪 결과 (예시)

  • Primitive Add: 수십억 번 연산에도 빠르게 처리
  • Reference Add: 언박싱 비용 + Heap 접근 때문에 상대적으로 느림

실제 JMH 결과를 보면, 프리미티브 연산이 참조 타입보다 수 배 이상 빠른 성능을 보여줍니다.


✅ 결론

  • 자바는 Call by Value만 존재한다.
  • 프리미티브는 값 자체를 복사, 참조 타입은 참조(주소) 값을 복사한다.
  • 성능 면에서는 프리미티브 타입이 참조 타입보다 훨씬 빠르다.
  • 참조 타입은 편리하지만, 성능이 중요한 코드에서는 프리미티브를 적극 활용하는 것이 좋다.

👉 이런 벤치마크는 실제로 대용량 데이터 처리, AI 프롬프트 토큰 관리, 고성능 서버 애플리케이션에서 큰 차이를 만들어냅니다.


⚠️ 얇은 복사&깊은 복사와는 관련이 없다.

얇은 복사 : 주소값을 복사해 버리는 것 = call by Ref ?
깊은 복사 : 값 자체를 복사해 버리는 것 = call by value ?

헷갈릴 수 있어서 기록해본다. 관련이 없다.



참고

https://velog.io/@ahnick/Java-Call-by-Value-Call-by-Reference

profile
자바 스프링 백엔드 개발자입니다. 배운 것을 기록합니다.

0개의 댓글