Question
변수 선언할때 int와 Integer의 차이가 뭔 줄 알니?
boolean과 Boolean은?
대문자요 라고 하면 팀장님한테 혼난다.
여기서 나는 처음에 기본형과, 참조형이다 라는 변수 타입의 차이만 알고 있었고, 습관적 int 변수 선언만 하던 어느날 DB에 저장 해야 하는 값의 null체크를 해야하는 상황과 마주했다. 그런데 당연히도(그때는 당연히 몰랐던) 기본형인 int는 null체크를 할 수 없었고, null체크를 위해서는 참조형을 사용해야 했다. 왜인지도 모르고 쓰다보면 그냥 쓰다가 또 잊어버린다. 그래서 깊게는 아니더라도 JAVA의 메모리에 대해서 함께 공부해야 한다..
단순히 기본형과 참조형이 무엇이냐가 아닌, JAVA의 메모리 구조에 대해서 알아야만 코드상에서 적재적소에 사용할 수 있으며, 실제로 메모리의 효율적 사용에도 연관이 있다는 것을 알게됐다.(코드가 수 만 줄 쌓이다보면,프로그램의 성능과도 연관이 있겠지?)
목차
1. 메모리 할당 - OS로부터 메모리를 할당받고, 그 메모리를 용도에 따라 나누어 관리 하는 JVM
2. 메모리 구조 - 변수가 메모리 영역에 어떻게, 어디에 올라가는지? Static, Stack, Heap
3. 기본형과 참조형 Primitive / Reference
4. JAVA의 메모리 관리 Garbage Collector
위 순서로 정리해보려 하는데, 이 순서는 내가 JAVA의 메모리 구조 대해서 공부했을때 개인적으로 이해하기 쉬웠던 순서이다.
1-1. JAVA의 컴파일
사람이 소스코드를 입력하면 그 실제 코드가 src(source)파일에 저장이 되고(*.java 확장자),
이 *.java 파일을 Java 컴파일러가 컴파일하면(javac.exe) (이클립스, 인텔리J 같은 IDE가 컴파일을 도와준다.) 확장자가 .class인 바이트 코드로 변환되며, bin(Binary)에 생성된다
java의 컴파일을 직접 해보고 싶다면 간단하다.
javac.exe는 컴파일 할 때 사용
java.exe는 실행할 때,
1. java 확장자의 소스코드를 작성하여 저장 후 cmd로 해당경로로 이동하여 javac로 컴파일한다.
cd study
javac CompileTest.java
2. dir 명령어로 컴파일이 완료된 CompileTest.class 파일이 생성된걸 확인한다.
dir
3. java로 실행시킨다.
// do
java CompileTest
// then
// 환경변수 설정에서 classpath에 보통 확장자까진 지정해주지 않기 때문에,
// 인식하지 못하는 오류가 발생할 수 있다.
// 이 경우, 환경변수 설정을 변경 하거나 아래 명령어로 실행시키면 된다.
// do
java -classpath . CompileTest
<명령어 입력 캡쳐본>
1-2. JVM(Java Virtual Machine)
"write once, run anywhere (WORA)" 타이틀을 달고 있는 언어답게 여러 플랫폼 혹은 OS에서 동작 가능하도록 설계되었다.
JVM은 Java Byte Code를 OS에 맞게 해석해주는 역할이다.
Java Compiler가 *.java 소스 파일을 컴파일 하면 .class라는 java byte code로 변환시켜 주며, bytecode 는 기계어가 아니기 때문에 OS에서 바로 실행이 되지 않습니다. 이 때 JVM이 OS가 이해할 수 있도록 해석해줍니다.
bytecode 파일은 JVM 구동 명령어(java.exe)에 의해 JVM에서 해석되고, 해당 운영체제에 맞게 실제 컴퓨터가 인식할 수 있는 기계어로 번역된다.
중간 단계 언어 (java bytecode)가 존재함으로 JAVA가 지향했던 플랫폼에 독립적인 언어가 될 수 있었다.
(즉, java bytecode는 자바 가상 머신만 설치되어 있으면 어떤 운영체제에서라도 실행될 수 있다.)
2. Java의 메모리 구조
모든 자바 프로그램은 자바 가상 머신(JVM)을 통해서 실행된다. 자바 프로그램이 실행되면, JVM은 운영 체제로부터 해당 프로그램을 수행할 수 있도록 필요한 메모리를 할당받는다.
애플리케이션을 최적의 방식으로 실행하기 위해 JVM은 메모리를 스택과 힙 메모리로 나눕니다. 새로운 변수와 객체를 선언하거나, 새로운 메서드를 호출하거나, 문자열을 선언 하거나, 유사한 작업을 수행할 때마다 JVM은 스택 메모리 또는 힙 공간에서 이러한 작업에 대한 메모리를 지정합니다.
이 글에서는JAVA에서의 메모리 구조가 위 사진과 같구나, 하는 틀을 대략적으로 이해하고 넘어가고, 기본형과 참조형의 데이터는 어느 영역에 올라가는지에 대해 알아보겠다. 여기서 기억할 것은 Stack 영역과 Heap 영역이다.
메모리 구조에서의 기본형과 참조형의 차이
기본형 변수는 Stack에 실제 값을 저장하여 사용하며,
참조형은 마치 객체를 저장하는 것처럼 Heap에 실제 값을 저장하고 해당 주소를 Stack에 저장하는 방식을 사용한다.
Stack은 정적인 메모리 영역이며 데이터를 찾기가 쉽지만(Stack 알고리즘의 push pop으로 데이터를 찾는다.), Heap은 동적인 메모리 영역이며 new키워드로 클래스를 할당하면 데이터를 주소 값으로만 찾을 수 있습니다.
Stack 영역
JVM은 자바 프로그램에서 메소드가 호출되면, 메소드의 호출과 관계되는 지역 변수와 매개변수를 스택 영역에 저장한다. 이렇게 스택 영역은 메소드의 호출과 함께 할당되며, 메소드의 호출이 완료되면 소멸한다. 스택 영역에 저장되는 메소드의 호출 정보를 스택 프레임(stack frame)이라고 한다.
* primitive 타입의 데이터(int, double, byte, long, boolean 등) 에 해당되는 지역변수, 매개 변수 데이터 값이 저장 되는 공간.
* 참조형의 heap에 실제 값이 저장되는 주소를 저장하는 공간.
이 메모리에 대한 액세스는 후입선출(LIFO) 순서로 이루어진다.
Stack 메모리의 주요 기능
스택 메모리의 다른 기능은 다음과 같습니다.
- 새 메서드가 호출되고 반환될 때마다 크기가 커지고 줄어듭니다.
- JVM 스택은 메소드를 호출할 때마다 프레임(Frame)을 추가(push)하고 메소드가 종료되면 해당 프레임을 제거(pop)한다.
- 스택 내부의 변수는 변수를 생성한 메서드가 실행되는 동안에만 존재합니다.
- 각 스레드마다 하나씩 존재, 스레드가 시작될 때 할당된다.
- 메서드 실행이 완료되면 자동으로 할당 및 할당 해제됩니다.
- 이 메모리가 가득 차면 Java에서 java.lang.StackOverFlowError가 발생합니다.
- 이 메모리에 대한 액세스는 힙 메모리와 비교할 때 빠릅니다.
Heap 영역
자바 프로그램에서 사용되는 모든 클래스 객체(인스턴스)와 배열이 저장되는 영역으로, JVM이 관리하는 프로그램 상에서 데이터를 저장하기 위해 런타임 시 동적으로 할당하여 사용하는 영역이다.
프로세스 처리를 실행하는 과정에서 동적으로 변화하는 데이터는 기본적으로 여기에 할당된다. 즉, JVM은 자바 프로그램에서 new 연산자를 사용하여 인스턴스가 생성되면, 해당 인스턴스의 정보를 힙 영역에 저장한다.
Example ex = new Example(10);
에서 new Example(10); 에 해당.
힙 공간은 런타임 시 Java 개체 및 JRE 클래스의 동적 메모리 할당에 사용됩니다 . 새 객체는 항상 힙 공간에 생성되며 이러한 객체에 대한 참조는 스택에 저장됩니다.
이러한 개체에는 전역 액세스 권한이 있으며 응용 프로그램의 어디에서나 액세스할 수 있습니다.
참조형(Reference Type) 데이터 타입을 갖는 객체(인스턴스), 배열 등이 저장 되는 공간
단, Heap 영역에 있는 오브젝트들을 가리키는 레퍼런스 변수는 stack에 적재
그럼 이 Stack 메모리 설정을 높이면 되지 않을까 생각하는데.. 맞습니다. Stack 메모리를 높이면 StackOverflow는 해결됩니다. 그러나 이 Stack 메모리는 구조가 Stack 알고리즘 구조로 push와 pop으로 데이터를 찾는 건데, 당연히 메모리가 커지면 느려집니다. 꼭, Stack이 아니더라도 탐색 알고리즘으로 되어 있는 자료 구조는 커지면 느려집니다. 즉, Stack 메모리가 많아지면 프로그램은 느려집니다.
Heap은 이렇게 Stack처럼 정형화 된 자료 구조가 아니기 때문에 용량이 큽니다. 그러나 이를 참조하기 위해선 반드시 메모리 주소 값이 있어야 하는데, 이 메모리 주소 값을 Stack 영역에 보관을 하는 것입니다.
자바에서는 이 주소 값을 확인할 수 있는 함수가 hashcode입니다.
힙 메모리의 주요 기능
- Young Generation, Old 또는 Tenured Generation, Permanent Generation을 포함하는 복잡한 메모리 관리 기술을 통해 액세스됩니다.
- 힙 공간이 가득 차면 Java에서 java.lang.OutOfMemoryError가 발생합니다.
- 이 메모리에 대한 액세스는 스택 메모리보다 상대적으로 느립니다.
- Heap 영역은 Stack 영역과 다르게 보관되는 메모리가 호출이 끝나더라도 삭제되지 않고 유지된다.
그러다 어떤 참조 변수도 Heap 영역에 있는 인스턴스를 참조하지 않게 된다면, GC(Garbage Collector)에 의해 메모리에서 청소된다. - 메모리 사용의 효율성을 유지하기 위해 사용하지 않는 개체를 해제하려면 Garbage Collector가 필요합니다.
- 스택과 달리 힙은 스레드로부터 안전하지 않으며 코드를 적절하게 동기화하여 보호해야 합니다.
- stack은 스레드 갯수마다 각각 생성되지만, heap은 몇개의 스레드가 존재하든 상관없이 단 하나의 heap 영역만 존재
3. 기본형 변수(Primitive Variable)
int number = 1;
// 여기서는 1이 literal
기본타입(Primitive type)으로 선언된 변수는 실제 값을 저장하며 아래와 같이 8가지가 있다.
소스 코드 내에서 직접 입력된 값을 리터럴(literal)이라고 부른다.
- boolean
- char
- byte, short, int, long
- float, double
특징
- 산술 연산이 가능함.
- null로 초기화 할 수 없음!!
참조형 변수(Reference Variable)
Integer number = new Integer(1);
참조 타입(Reference type)으로 선언된 변수는 어떤 값이 저장되어 있는 메모리의 주소를 값으로 가진다.
8개의 기본형을 제외한 나머지 모든 타입으로 배열, 열거, 클래스, 인터페이스를 이용해 선언된 변수다.
ex) Integet, Boolean, String....
특징
- 산술 연산 불가
- null로 초기화 할 수 있음.
- null이 필요한 경우 사용 할 수 있음, null체크 등.
정리! 기본형과 참조형의 차이기본형으로 선언된 변수는 Stack 영역에 생성되고, 직접 값을 저장하고 있지만참조형으로 선언된 객체는 Heap영역에 생성되고, 스택 영역에는 힙 영역의 객체 주소 값을 가지고 있다. 주소를 통해 객체를 참조한다는 뜻에서 참조 타입 이라고 한다. |
4. JAVA의 메모리 관리
Garbage Collector
JAVA 의 메모리 관리는 개발자가 할 필요가 없다.
Java에서는 개발자가 명시적으로 메모리를 할당 및 할당 해제할 필요가 없다. JVM과 더 구체적으로 Garbage Collector가 자동으로 메모리를 관리해준다.
메모리 공간에서 할당된 객체 중 거의 사용하지 않거나 오래된 객체의 메모리를 반환함으로써, 메모리 사용의 효율을 증가시킨다. (C/C++와 반대됨)
GC는 heap 메모리를 살펴보고 사용 중인 개체와 사용하지 않는 개체를 식별하고 사용하지 않는 개체를 삭제하는 프로세스.
GC가 실행될 때마다 애플리케이션의 성능에 영향을 미칩니다. 이는 가비지 수집기 스레드가 효과적으로 작업을 수행할 수 있도록 애플리케이션의 다른 모든 스레드를 중지해야 하기 때문입니다.
https://www.baeldung.com/java-memory-management-interview-questions
https://www.baeldung.com/java-stack-heap
https://www.devkuma.com/docs/jvm/memory-structure/
https://medium.com/@lazysoul/jvm-%EC%9D%B4%EB%9E%80-c142b01571f2
'BackEnd > JAVA' 카테고리의 다른 글
[JAVA] 클래스(class)와 생성자 (1) | 2023.01.14 |
---|---|
[JAVA 기초] Static (0) | 2023.01.11 |
[JAVA] Static (정적) (0) | 2022.06.02 |
[JAVA] model.addAttribute JSON 형태로 넘기기 / JSON으로 받기 (0) | 2021.11.25 |
메이븐(Maven)이란? (0) | 2021.10.12 |