스택 영역
함수 호출 시에 사용되는 지역 변수와 매개 변수, 그리고 함수의 복귀 주소 등을 저장합니다. 스택은 후입선출(Last In First Out) 구조를 가지며, 함수 호출이 시작될 때는 그 함수에 필요한 공간이 스택의 최상단에 할당되며 함수 호출이 끝나면 그 공간이 해제됩니다. 이러한 과정을 스택 프레임(Stack Frame)이라고 합니다.
힙 영역
동적으로 메모리를 할당할 때 사용됩니다. 프로그램 실행 중에 필요한 메모리 공간을 할당하고 해제하는 데 사용됩니다. 이를 위해 malloc(), free()와 같은 함수를 사용할 수 있습니다. 힙 영역에 할당된 메모리는 전역적으로 접근 가능하며, 포인터를 사용하여 메모리를 할당하고 해제합니다.
스택과 힙은 모두 메모리 공간을 할당하고 해제하는 데 사용됩니다. 그러나 스택은 정적으로 할당되며, 실행 시간 동안 크기를 변경할 수 없습니다. 반면 힙은 동적으로 할당되며, 실행 시간 동안 크기를 변경할 수 있습니다. 따라서 스택은 속도가 빠르지만 제한적인 메모리를 사용하며, 힙은 느리지만 유연한 메모리를 사용합니다.
저는 이런 개념 설명을 보고 '엥? 이게 뭔소리야' 라는 생각을 했습니다. 너무나도 추상적인 내용이기 때문에 예를 보면서 생각을 해보도록 합시다! 예제가 아기 개발자들에게는 조금 어렵지만 한번 듣고 나면 힙과 스택에 대한 이해가 뽝 될 것입니다.
예제를 통한 이해
//main method 파일
public class TestService {
public static void main(String[] args) {
ObjectInitService service=new ObjectInitService();
Friend friend=new Friend("제니",18);
service.passOne(friend);
System.out.println("1번 예상:"+friend.getName()+" "+friend.getAge());
service.passTwo(friend);
System.out.println("2번 예상:"+friend.getName()+" "+friend.getAge());
}
}
//객체 초기화를 위한 메소드 파일
public class ObjectInitService {
public void passOne(Friend friend) {
friend=new Friend("하니", 16);
}
public void passTwo(Friend friend) {
friend.setName("아이유");
}
}
//새로운 객체를 만드는 class 파일
public class Friend {
private String name;
private int age;
public Friend(String name, int age) {
super();
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;
}
}
이 세가지 파일을 하나의 패키지 안에 생성하고 어떠한 값이 나오는지 맞춰보세요!
힙과 스택에 대한 이해이기 때문에 힙과 스택을 그려서 진행해보겠습니다.
1. 메인 메소드를 실행하면 첫 번째 줄 실행
ObjectInitService service=new ObjectInitService();
이러면 Stack 메모리에 명령어가 올라가고 실행됩니다. 이렇게 되면 Heap 메모리에 객체가 생성되고 Stack 메모리에는 service 변수에 Heap 메모리의 임시 주소 값이 생성됩니다.
2. 다음 두 번째 줄도 같은 방식으로 생성 될겁니다.
Friend friend=new Friend("제니",18);
3. 세 번째 줄
service.passOne(friend);
주소 값이 200인 freind 를 매개변수로 보내서 service의 함수를 실행하는 단계입니다.
public void passOne(Friend friend) {
friend=new Friend("하니", 16);
}
함수를 보면 매개변수로 받은 friend에 새로운 인스턴스를 연결해주는 메소드인데요.....
매개변수 friend의 현재 주소는 200인데 새로운 인스턴스를 생성하면서 매개변수 friend의 주소값이 바뀌겠네요.
변수에 instance를 할당하는 것은 주소값으로만 할당하기 때문에 매개변수 friend의 주소 값이 201로 변경되는 것이지
Heap에 있는 200 번 주소를 가진 인스턴스는 변경되지 않습니다.
main 메소드에 있는 friend의 값이 변하는 것이 아닌 매개변수로 던져준 friend의 주소 값이 변하는 것이기 때문에 다음 1번의 답은?
다음 함수가 종료되면 스택에서 사라집니다. 하지만 이해를 돕기 위해 돼지 꼬리로 삭제 된 것을 표현하겠습니다.
그리고 주소 201도 사용하지 않게 되므로 가비지 컬랙터가 heap에서 없애버릴 겁니다.
1번 예상:제니 18
4. 4번째 줄
service.passTwo(friend);
public void passTwo(Friend friend) {
friend.setName("아이유");
}
주소 200의 name 값을 "아이유"로 변경하라는 함수가 있네요. 이렇게 되면 Heap에 저장되어있는 주소 200의 name이 변경됩니다.
이러고 동작이 완료되면 stack 에 있는 저 실행 함수는 삭제됩니다.
이러고 2번째 println을 확인하면 무슨 답이 나올까요?
맞습니다.
2번 예상:아이유 18
그리고 프로그램이 종료되면 stack과 heap 메모리에 쌓인 데이터들이 다 삭제 될 것입니다. (따로 그리지는 않을게요.)
소감
처음에는 너무나도 추상적인 이야기라서 이해하기 어렵지만 예제를 통해 이해하면 흥미도 생기고 뭔가 java의 동작이 어떻게 되는지 알겠다는 느낌이 듭니다. 저도 처음에 그냥 때려 맞춰서 맞았는데(틀린거나 마찬가지), 확실히 이해를 하고 보니까 다른 문제가 나왔을 때는 완벽하게 대답할 수 있다고 생각했습니다. 이렇게 예제를 통해 배우고 개념을 다시 터득하는 방법으로 지속해보려고 합니다.
'코딩 개발 > Java' 카테고리의 다른 글
Java - this (0) | 2023.04.03 |
---|---|
Java - 캡슐화 (Encapsulation) (0) | 2023.04.03 |
Java - Cohesion & Coupling(응집도, 결합도) (0) | 2023.04.02 |
Java - OOP(Object OrienTed Programming) (0) | 2023.04.02 |
Java - IDE 개념 및 간단한 사용법 (feat. Eclipse) (0) | 2023.04.02 |