메모리 구조
스택에는 함수 내에 존재하는 로컬 변수가 저장되고, 힙에는 클래스 내에 존재하는 인스턴스 변수가 저장된다. 실제 객체를 생성할 때 스택과 힙에 어떻게 할당될까? (자바의 메모리 영역이 스택과 힙만 있는 것은 아니지만 객체 생성 시 가장 관련이 있는 것이 두 메모리이다.)
객체가 생성될 때 메모리에서 어떤 일이 일어나는지 살펴보기 위해 앞서 살펴본 코드를 조금 수정한다.
먼저 Developer 클래스는 다음과 같이 약간 수정하여 먹고, 자고, 코딩하는 함수를 제거하였다.
public class Developer {
private String name;
private int age;
public Developer() {
System.out.println("[default] Developer Object is created!");
}
public Developer(String name, int age) {
System.out.println("[name : " + name + " age : " + age + "] Developer Object is created!");
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
HelloWorld 클래스는 다음과 같이 수정하였다.
public class HelloWorld {
public static void main(String[] args) {
String name = null;
int age = 0;
Developer javaDeveloper = new Developer();
javaDeveloper.setName("Tom");
javaDeveloper.setAge(23);
name = javaDeveloper.getName();
age = javaDeveloper.getAge();
System.out.println("name = " + name+ ", age = " + age);
Developer kotlinDeveloper = new Developer("Jerry", 20);
name = kotlinDeveloper.getName();
age = kotlinDeveloper.getAge();
System.out.println("name = " + name+ ", age = " + age);
}
}
[default] Developer Object is created!
name = Tom, age = 23
[name : Jerry age : 20] Developer Object is created!
name = Jerry, age = 20
이제 위 코드가 실행되면서 객체가 생성될 때 실제 스택과 힙에 할당되는 값이 무엇인지 하나씩 살펴보겠다.
String name = null;
int age = 0;
name, age는 main 함수 내에 선언된 로컬 변수이다. 로컬 변수는 스택에 저장된다고 했다. 따라서 다음과 같이 스택에 로컬 변수가 생성된다. 아직 힙에는 아무런 변화가 없다.
다음은 Developer 객체를 생성하는 부분이다. 해당 부분을 조금씩 나눠 살펴보자. 먼저 Developer javaDeveloper 부분이 실행될 때 스택에 로컬 변수 javaDeveloper가 생성된다.
Developer javaDeveloper = new Developer();
다음으로 객체가 실제 생성되는 new Developer()로 인스턴스화가 되면서 실제 힙에 Developer 클래스의 요소들이 생성된다. 이때 생성된 힙 메모리 주소는 0x100번지라고 가정한다. 또한 함수명은 생략하고 함수라고만 표기하였다.
Developer javaDeveloper = new Developer();
객체가 기본 생성자를 통해서 만들어졌기 때문에 추가적인 작업이 없이 마무리되었다. 따라서 최종적으로 아래의 구문이 끝나게 된 것이다. 이때 스택에 있는 javaDeveloper 변수에 객체의 위치 정보인 힙의 주소가 할당된다.
Developer javaDeveloper = new Developer();
setter 함수를 통해서 인스턴스 변수에 값을 할당하면 힙에 해당 값이 할당된다.
javaDeveloper.setName("Tom");
javaDeveloper.setAge(23);
반대로 getter 함수를 통해서 인스턴스 변수의 값을 로컬 변수에 할당할 수도 있다.
name = javaDeveloper.getName();
age = javaDeveloper.getAge();
코드를 보면 하나의 객체를 더 만드는데 기본 생성자가 아니라 오버로딩한 생성자를 사용했다. 이때도 로컬 변수 생성하는 단계에서는 차이점이 없다.
Developer kotlinDeveloper = new Developer("Jerry", 20);
다음으로 객체를 생성하는 과정도 같다.
Developer kotlinDeveloper = new Developer("Jerry", 20);
그리고 오버로딩된 생성자를 사용하면서 나타나는 차이점이 있다. 이 경우는 곧바로 인스턴스 변수를 생성자의 인자 값으로 초기화하기 때문에 인스턴스 변수의 값이 최종적으로 다음과 같이 된다.
Developer kotlinDeveloper = new Developer("Jerry", 20);
최종적으로 해당 구문이 모두 실행되면 kotlinDeveloper에 힙 메모리 주소가 할당된다.
Developer kotlinDeveloper = new Developer("Jerry", 20);
나머지 getter, setter 함수를 사용하는 부분은 동일하다. 따라서 생략한다.