클래스를 사용하기 위해서는 선언과 생성을 해야한다.
아래와 같은 클래스가 있다.
class Car {
String name;
public Car(String name) {
this.name = name;
}
public void drive() {
System.out.println(this.name + "를 운전합니다.");
}
}
1. 인스턴스 생성
new Car("audi");
인스턴스를 생성하면 Car 객체에 정의된 필드, 메소드를 담아 실제 메모리 공간에 할당한다.
실제 출력해보면 메모리에 할당됨을 볼 수 있다.
System.out.println(new Car("audi"));
Output
hello.itemservice.domain.item.ItemTest$Car@399c4be1
메모리상에 인스턴스를 만들기만해서는 해당 인스턴스를 사용할 수 없다. 만들어진 인스턴스를 참조(가리키고 있을 수 있는)할 수 있는 무엇인가가 필요한다. 이는 참조 변수 선언을 통해 할 수 있다.
2. 참조변수 선언
Car myCar;
Car Type의 참조 변수 myCar를 선언하는 것이다. 선언을 했다고 인스턴스를 사용할 수 있는 것은 아니다. 선언만 이루어진다면 그저 껍데기에 불과하다. 선언한 참조 변수가 어떤 인스턴스를 가르킬지를 말해주어야 한다. 이는 선언과 생성의 연결을 통해 가능하다.
우리는 아래와 같은 방식으로 일반적으로 선언과 생성을 연결한다.
Car car = new Car("audi");
3.JVM의 메모리 관리
JVM은 운영체제로부터 할당받은 메모리 공간의 효율적인 사용을 위해 메모리 공간을 3가지로 나눈다.
JVM은 메모리를 다음과 같이 3 영역으로 나눈다.
메소드 영역 : 메소드의 바이트코드, static 변수
스택 영역 : 지역 변수, 매개변수
힙 영역 : 인스턴스
참조 변수 선언과 인스턴스 생성은 스택, 힙 영역과 큰 관련이 있다. 그 전에 메소드 영역을 알아보자.
3.1 메소드 영역
아래와 같이 참조 변수 선언, 인스턴스 생성 및 필드에 접근하기 위해서는 먼저 해당 클래스의 바이트코드가 메모리 공간에 로딩되어야 한다. 로딩은 메모리 공간에 올려진다는 의미이다.
Car car = new Car("audi");
car.name;
car.drive();
이러한 클래스가 컴파일 되면서 JVM이 실행 가능한 바이트코드로 바뀌게 된다. 이러한 바이트코드가 로딩되는 공간을
메소드 영역이라고 한다.
class Car {
String name;
public Car(String name) {
this.name = name;
}
public void drive() {
System.out.println(this.name + "를 운전합니다.");
}
}
스택 영역과 힙 영역에 들어가기에 앞서 위에서 설명했던 참조 변수 선언과 인스턴스 생성을 나타내는 코드를 살펴보자.
Car myCar1 = new Car();
Car myCar2 = new Car();
해당 코드를 메모리 영역으로 나누면 도식화 해보면 아래와 같다.
3.2 스택 영역
스택 영역에는 기본적으로 원시 타입(int, string, boolean 등)이 실제 값과 함께 저장되어 있습니다. 추가로 지역 변수, 매개변수가 들어갑니다. 아래 코드에서 스택 영역에 들어가는 것은 세가지가 있습니다.
1. 메인 메서드의 매개변수 args
2. 참조 변수이자 지역 변수인 myCar1
3. 참조 변수이자 지역 변수인 myCar2
arg > myCar1 > myCar2 순으로 스택 영역에 쌓이게 됩니다.
public static void main(String[] args) {
Car myCar1 = new Car();
Car myCar2 = new Car();
}
스택 영역의 변수들은 중괄호를 기점으로 생성되고 소멸됩니다. 아래 사례를 보겠습니다.
public static void main(String[] args) {
int a = 10;
int b = 20;
add(a, b);
System.out.println("..."); // 현재 실행 위치
}
public static Integer add(int x, int y) {
int result = x + y;
return result;
}
main 메서드를 한줄씩 실행하면 스택영역에는 다음과 같이 변수들이 할당 됩니다.
1. 매개 변수 args
2. 지역 변수 a
3. 지역 변수 b
4. add 메소드 매개변수 x
5. add 메소드 매개변수 y
6. add 메소드 지역변수 result
6번이 진행되고 result가 return 되면서 중괄호는 끝나게 됩니다. 스택 영역의 변수들은 중괄호를 기점으로 생성 및 소멸된다고 했습니다. 그래서 현재 실행 위치 System.out.println("..."); 에서는 변수 x, y, result 가 소멸됩니다.
이제 힙 영역을 정리해보겠습니다.
3.3 힙 영역
도식화된 그림에서 보시다시피 힙 영역에는 인스턴스가 들어갑니다. 이쯤되면 의문이 생길수도 있습니다.
이쯤 와서 다시 상기시키면 JVM은 메모리의 효율적인 관리를 위해 메모리를 메소드, 스택, 힙 영역으로 나눕니다.
앞서 스택 영역이 변수를 생성하고 소멸하는 것을 보았습니다. 힙 영역을 따로 두었다는 것은 스택 영역과 힙 영역이 변수를 다른 방식으로 관리하기 때문일 것입니다.
스택 영역은 변수가 중괄호를 기점으로 소멸됩니다. 스택 영역의 변수들은 컴파일과 동시에 운명이 정해집니다.
하지만 힙 영역의 변수들은 우리가 알아서 관리해야 합니다. Java에서는 힙 영역의 인스턴스에 대한 관리(더 정확히는 소멸)를 Garbage Collection이 효율적으로 도와줍니다. 그래서 개발자가 메모리 관리 부담을 어느 정도 GC에게 부담할 수 있게 됩니다. 참고로 C 같은 언어는 이와 같은 힙 영역의 인스턴스 소멸을 수동으로 해야되는 것으로 알려져 있습니다.(안써봐서 모름 아무튼 그렇다고 합니다.)
그렇다면 힙 영역의 인스턴스는 어떤 기준으로 소멸될까요? 어떠한 참조 변수에게도 선택을 받지 못할때요!
맨 처음 인스턴스의 생성에서 봤듯, 인스턴스가 의미를 가지려면 참조 변수가 자신을 가리켜야 합니다. 그래야 인스턴스를 사용할 수 있으니까요. 사용하지 못하는 인스턴스는 의미가 퇴색되지 않을까요? (생성 그 자체가 의미없다는 것은 아닙니다.)
그래서 위처럼 어떠한 참조 변수에게도 참조 받지 못하는 new Car() (참조값 Car@399c4be1) 인스턴스는 소멸의 대상이 됩니다.
여기서부터는 그냥 누군가가 그렇다고 해서 그렇구나~ 하고 넘어간 내용입니다.
스택 영역은 빠른대신 작은 공간을 차지하고 있습니다. 그래서 자주 사용하는 원시타입은 스택 영역에 상주하고 있습니다. 한편 reference 타입은 그 실제가 힙 영역에 존재하고 스택 영역에는 그 참조 값만을 가지고 있게 됩니다.
'Java' 카테고리의 다른 글
쓰레드 - 1. 쓰레드란? (0) | 2022.07.03 |
---|---|
JAVA - 함수 호출 방식 call by value, call by reference (0) | 2022.06.29 |
연산자 == /equals() 그리고 equals()와 hashCode()를 같이 재정의해야 하는 이유 (0) | 2022.06.18 |
java - equals/hashcode (0) | 2022.05.09 |
Java - equals 재정의시 주의점 (0) | 2022.05.09 |