💙 들어가며
우리가 자바에서 만나는 코드에는 많은 변수와 연산들이 존재한다.
1. 자바는 이것들을 처리하기 위해 어떤 식으로 메모리를 사용하고 있을까?
자바에서 말하는 Garbage Collector는 무엇일까?
2. 그리고 많은 작업 중에서 우리가 발견하지 못한 오류와 그를 처리할 수 있는 툴인 디버거에 대해서도 알아보자.
✏️ 학습내용 정리
#메모리
우선 메모리란 무엇일까? 컴퓨터에서 메모리는 크게 2가지로 나뉜다. 보조기억장치와 주기억장치.
- 보조기억장치: 하드 디스크(HDD), SDD
- 주기억장치: 메인 메모리(ROM, RAM)
주기억장치는 휘발성이기 때문에 컴퓨터가 꺼지면 안에 내용을 모두 날려버린다.
그러다 컴퓨터가 다시 켜지면 보조기억장치(영구기억장치)의 도움을 빌어서 메모리를 불러오는데, 이 때문에 우리는 로드라는 개념이 필요하게 된다.
(전원이 들어오면 주기억장치에는 아무런 내용이 없기 때문에 보조기억장치에서 내용을 불러와야 함!)
#변수는 준비물
그렇다면 우리가 코드를 짜면서 값을 대입하는 변수는 언제 메모리에 로드(할당)될까?
일단 변수는 코드를 준비하기 위한 준비물로 해석된다.
변수를 선언하는 것은 일차적으로 컴파일러에게 그 기호를 쓰겠다고 알리는 것이며, 나아가 실행환경에서 변수가 공간으로서의 의미를 가지기 때문에 미리 공간을 마련해야 한다는 선포이기도 하다.
(정수형 공간이면 정수크기 만큼의 공간이 만들어지는 것)
#언제 준비하나?
그렇다면 여러 개의 변수들은 한 번에 모두 완벽하게 사전 준비되는 것일까?
for, while, if 등 어떠한 것으로 감싸져서 선언되었을 때도 한 번에 미리 준비되는 것일까?
정답은 땡, 아니다. 이유는 간단하다. 조건문이라고 가정해보자.
어떤 블럭은 조건에 따라 실행될 수도 있고 안될 수도 있는데 굳이 실행안될지도 모르는 녀석을 미리 준비한다는 것은 비효율적이다. 또한 어마무시 하게 많은 줄의 코드의 변수를 다 미리 준비한다는 것도 메모리 상 비효율적이다.
때문에 선언된 변수를 모두 준비하는 것이 아니라, 블록이 실행될 때마다 준비물을 새롭게 추가하여 마련한다.
어찌되었든 미리 준비물을 준비하는 것이기 때문에 변수공간은 정적이라고 할 수 있다.
(모든 블록들에 있는(지역에 있는) 공간은 지역에 들어가기 전에 미리 준비)
#stack과 heap
그렇다면 구체적으로 자바에서는 메모리를 어떻게 로드할까?
그전에 먼저 생각해볼 것이 변수는 한 번 선언되면 계속 유효한 것일까?에 대한 것이다. 답은.. 당연히 아니다.
변수는 상황과 성격에 따라서 특정 메모리 상에 할당되었다가 사라진다.
위의 이미지를 참고했을 때 자바에서 사용하는 메모리는 크게 stack과 heap 영역으로 나누어 메모리 상에 데이터가 올라가는 것으로 이해해 볼 수 있겠다. (static은 나중에 다뤄볼 예정이라고 하셨음)
위 그림만 보면 두 메모리가 각각의 영역을 가지고 있는 것처럼 보이지만, 두 개의 메모리가 나뉘어져 있는 것은 아니다.
한 영역을 융통성있게 나눠서 사용하는 것인데, stack에서는 차올라오고 heap에서는 차내려오는 그런 느낌이랄까?
어찌 되었든 두 개가 비대해지면 메모리가 꽉 차게 되는 흐름이다.
stack | heap |
● 적재한다는 의미, 위로 쌓는 이미지 ● 변수(기본형의 값, 참조형이라면 이름만) ● main 블록이 진행되기 전에 미리 준비하는 공간 ● 미리 예약되는 영역, 정적 (연산이 진행되면서 갑자기 중간에 값이 들어갈 수 없음) ● 모든 블럭을 한 번에 미리 다 준비하는 것 아님, 중첩된 블럭이 있다면 그 블록이 시작될 때에야 메모리를 할당 (블록 안에 블록은 그 블록이 시작될 때 값을 할당) ● 블록이 시작될 때 생겼다가 블록이 끝나면 사라짐 (짐을 쌓았다가 내렸다가.. 적재한다. stack) |
● 참조변수의 값 ● stack 예약석에 앉지 못한 나머지는 다 heap ● 코드 실행 중간에 메모리 할당, 동적 (new라는 연산을 통해 실행되면서 중간에 메모리에 할당) ● 블록이 끝난다고 해서 stack처럼 메모리가 바로 사라지지 않음 |
#Garbage Collector
stack영역의 데이터들은 블록이 끝나면 자동으로 사라지지만 heap영역의 데이터들은 바로 사라지지 않는다.
따라서 모든 작업이 끝나도 남아있는 이 데이터들을 정리하는 역할이 필요한데, 이것이 바로 Garbage Collector이다.
Garbage Collector가 메모리를 정리해 가는 때는 딱히 정해져 있지 않다.
CPU가 한가할 때 알아서 정리해가는 식이기 때문에 알아서 정리해가는 청소부라고 생각하면 될 것 같다.
(물론, heap이 너무 비대해져서 stack의 자리가 없을 때는 Garbage Collector가 바로 출동!)
여기서 깨닫게 되는 사실이 하나 있다.
즉, Garbage Collector는 heap에만 있다는 것!(메모리 전체를 도는 것이 아니다.)
때문에 어슬렁 거리는 Garbage Collector가 좁은 범위에서 만난 데이터일 수록 금방 사용되고 끝나기 때문에 빨리 수거될 가능성이 높겠다.
#오류
자바는 오류를 어떻게 처리할까?
오류는 크게 2가지로 정리해볼 수 있겠다.
- 구문오류 : 컴파일이 되지 않으며 무엇이 문제인지 컴파일러가 바로 알려준다. (빨간줄)
- 논리오류 : 당장 오류가 나지 않지만 나중에 보면 조금씩 데이터 손실이 있거나(소수점 날라간다거나..)
오래 쌓이면 문제가 되는 것들(버그)
그러면 논리오류와 같은 것들에 대해 주의하며 프로그램을 짜야겠는데.. 들어올 데이터들이 한 두개도 아니고.. 어떻게 값을 확인하면서 작업을 할 수 있을까?
#디버거
이럴 때 사용하는 것이 바로 디버거이다.
우리가 쓴 코드에서 오류가 난다면 컴파일러가 바로 무엇이 문제인지 알려주지만, 내가 쓴 코드가 아니라 입력값이나 계산된 결과값의 오류를 찾을 때라면 디버거를 사용해서 문제점을 찬찬히 찾아볼 수 있다.
디버거를 실행하는 방법은 간단하다.
Ctrl하고 F11을 누르면 Run이 되지만 그냥 F11을 누르면 Debugger를 실행한다.
- Run: Ctrl + F11
- Debuger: F11
오류가 나는 부분을 정확하게 알고 싶을 때 디버거를 잘 활용할 수 있겠다.
💙 마치며
1.
말로만 많이 들어봤던 메모리의 stack영역과 heap영역.
앞으로 사용할 변수들이 정적인 것인지 동적인 것인지에 대해 잘 이해하고 있어야겠다.
2.
그리고 디버거를 잘 활용할 줄 알아야겠다.
사실 문제가 있는데 컴파일이 되는 것이 더 두려운 상황인 것 같다.
항상 자료형에서 데이터 손실이 없는지 잘 확인하고, 명시적으로 코드를 짜는 버릇을 들여야겠다.
'JAVA' 카테고리의 다른 글
[뉴렉처 6기] JAVA│배열(Array)를 활용한 데이터 편집│최소값, 최대값, 자리바꾸기(230622) (1) | 2023.06.29 |
---|---|
[뉴렉처 6기] JAVA│반복과 검사 do while│메뉴 switch(230620) (0) | 2023.06.27 |
[뉴렉처 6기] JAVA│바둑판 찍기 코드 2가지 리뷰 및 바둑돌 놓기(230619) (1) | 2023.06.26 |
[뉴렉처 6기] JAVA│배열(Array)의 뜻과 사용 방법(230621) (1) | 2023.06.22 |
[뉴렉처 6기] JAVA│①if ②if~else ③if~elseif~else 차이와 활용 방법(230616) (2) | 2023.06.18 |