JVM

midas·2022년 5월 23일
0

Java Code → .class → JVM 위에서 실행됩니다.

JVM이 .class를 실행하는 절차

  1. 해당 클래스를 현재 디렉토리에서 찾는다.
  2. 찾으면 클래스 내부의 static 키워드가 있는 메서드를 메모리에 로딩
    method area → static zone에 로딩 됩니다.
  3. main() 메소드를 호출하여 실행되게 됩니다.
    Stack에 main이 들어가고, 얘가 비워지면 프로그램 종료!

📌 Method Area

  • method의 byte code가 저장되는 영역
  • static vs none-static 으로 나눠집니다.

JRE Java Runtime Evironment

  • JVM
  • Java Class Libraries
  • Class Loader

JVM Memory 할당 방식

🗃 Stack

  • Primitive 타입 변수들이 저장 됩니다.
  • 좁은 메모리 공간
  • 각 Thread는 자신만의 Stack을 가집니다.
    각 스레드에서는 다른 Stack 영역에는 접근할 수 없습니다.
  • 지역 변수들은 scope에 따른 visibility를 가집니다.

📦 Heap

  • 복잡한 타입의 클래스 타입, Interface, ArrayType 변수들이 저장
  • new 연산자를 통해 객체가 생성되는 영역
  • 넓은 메모리 공간
    값을 할당, 해제하는데 많은 비용이 듭니다.
    (자주 하면 Stack 보다 수십배의 비용...?)
  • 몇개의 Thread가 존재하든 상관없이 단 하나의 heap영역만 존재합니다.
  • Heap 영역에 있는 오브젝트들을 가리키는 레퍼런스 변수가 stack에 올라가게 됩니다.

⚠️ 주의사항

Wrapper Class - Integer, Double, String...

이들은 모두 Immutable 합니다.
Heap에 있는 같은 오브젝트를 레퍼런스 하고 있는 경우라도, 새로운 연산이 적용된 순간!
새로운 오브젝트가 Heap에 할당 됩니다.

// ❓ 문제1. after a의 결과 출력값은?
@Test
void printReferenceInteger() {
  Integer a = 10;
  System.out.println("before: " + a);
  changeInteger(a);
  System.out.println("after: " + a);
}

private void changeInteger(Integer num) {
  num += 10;
}

// ❓ 문제2. after s의 결과 출력값은?
@Test
void printReferenceString() {
  String s = "hello";
  changeString(s);
  System.out.println(s);
}

private static void changeString(String str) {
  str += " world";
}

위의 결과는 아래와 같습니다.
| 문제 1 : after: 10
| 문제 2 : hello

왓더...!? 변하지 않았다. 머선129 🤔

아래의 그림을 살펴봅시다.

  1. 처음에 a 변수에 10이 할당된 것을 볼 수 있습니다.
    (이것은 처음에 10을 가리키고 있습니다.)
  2. changeInteger()의 매개변수로 넘어간 순간 매개변수 num이 a를 복제하여 새롭게 생성됩니다.
    (num은 처음에는 a와 같이 10을 참조 하고 있습니다.)
  3. 하지만 += 10 이라는 연산을 하는 순간, Heap에 연산된 20이라는 새로운 값이 올라가게 됩니다.
    (num은 이제 20이라는 새로운 값을 가리키게 되죠.)

그리고 마지막으로 메서드를 벗어나게 되면, num이라는 값은 stack에서 사라지게 됩니다.
method라는 지역에서 벗어났기 때문이죠.

보시다시피 a라는 녀석은 여전히 10을 참조하고 있기 때문에, 값이 변하지 않은 채로 그대로 출력되게 되는 것이옵니다.

Java는 무조건 Call by value이다?

Java에는 Call by reference가 없다고 합니다.
하지만 매개변수로 받아서 값이 변하는 경우는 무엇일까요?

class Sample {
  public int value;
  Sample(int num) {
    value = num;
  }
}

class Main {
  public static void main(String[] args) {
    Sample sam = new Sample(10);
    run(sam);
    System.out.println(sam.value); // 🔎 어떤 값이 출력 될까요?
	}

  static void run(Sample sample) {
    sample.value = 20;
  }
}

결과값은 20인 값이 출력 되게 됩니다.
그럼 Call by reference인것 아닌가?! → 아닙니다.

  1. 일단 Stack에 sam이라는 값이 올라가게 됩니다.
    (Heap에는 10이라는 값을 가리키고 있겠죠?)
  2. 그리고 run() 메서드로 넘어가게 되면 sample이라는 변수가 stack에 새롭게 생성되게 됩니다.
    여기서 sample은 sam이 가지는 주소값을 복제하여 생성되게 됩니다.
    그래서 sample의 값을 변경하게 되면, sam이 가리키는 10이라는 primitive값에 접근하여 20으로 변경됩니다.

이렇게 주소라는 값을 복제하였기 때문에, 변경이 되는 것입니다.
즉 이건 Call by value라고 볼 수 있습니다.

그래서 결론은? 'Java에 Call by reference는 없다.' 입니다.

🔖 참고

profile
BackEnd 개발 일기

0개의 댓글