감사한 분들 (참고한 글)
https://ttl-blog.tistory.com/m/368
[Java] 자바 JVM 메모리 관리 파헤치기 (스택, 힙, Reference Type, 가비지 컬렉터)
https://yaboong.github.io/java/2018/06/09/java-garbage-collection/
자바 메모리 관리 - 가비지 컬렉션
https://yaboong.github.io/java/2018/05/26/java-memory-management/
자바 메모리 관리 - 스택 & 힙
https://covenant.tistory.com/139
명저로 다시 보는 자바와 메모리 이야기 1편
윤성우님의 열혈 자바 강의(https://cafe.naver.com/cstudyjava)
난 정말 JAVA를 공부한 적이 없다구요(윤성우 저)
자바의 메모리 사용 방식
객체지향 프로그램에서는 T 메모리 구조(static 혹은 method, stack, heap)를 사용합니다.
-바이트코드가 올라가는 영역입니다.
-다만 바이트코드라고 하면 막연하니깐 메소드 혹은 스택이라고 하는 것입니다.
(클래스에 대한 모든 정보 등 결국 정보들이 바이트코드로 이루어집니다.)
(자바 프로그램은 메소드들로 이루어져 실행됩니다..)
-클래스 정보를 읽어들이는 순간, static 변수가 있으면 메모리 공간에 할당하고 초기화까지 끝내버립니다.
그런데, 왜 클래스(static)’변수’를 메소드 영역에 할당하는 것인가?
메소드 영역에는 한 번 할당되고 나면 지워지지 않는 애들을 넣어둡니다.
→ 프로그램 종료될 때까지 지우지 않는 것들.
class Boy {
static int average=0;
public void Run() {...}
}
class MyMain{
public static void main(String[] args) {
Boy b=new Boy(); // 인스턴스 생성
Boy.average+=5;// 클래스 변수 접근
...
}
메모리 흐름 이야기
자바 가상머신이 main 메소드를 실행하고 쭉 읽어나가다 보니,
Boy 인스턴스를 생성하라는 명령이 있어서 Boy 클래스에 대한 정보를
메소드 영역에 올리면서 읽다보니, static 변수가 있다.
따라서, Boy 클래스 정보를 읽어들이는 순간에 average를 할당하고 0으로 초기화.
(또한 인스턴스를 생성하지 않아도 Boy.average로 클래스 변수에 접근할 수 있습니다.)
class Boy{
static int average = 0;
public void Run() {
System.out.println("소년은 재빠르게 나아갔습니다.");
}
}
public class test1 {
public static void main(String[] args) throws IOException {
System.out.println(Boy.average);//0 출력
}
}
스택 메모리 영역은 스레드 별로 할당됩니다. 즉, 각 스레드는 하나의 스택 메모리 영역을 갖습니다.
한 스레드에게 할당된 스택 메모리 영역은 다른 스레드에서 접근할 수 없습니다.
스택 프레임을 저장하는 메모리 공간입니다.
쉽게 말해, 기본 자료형 byte, short, int, long, double, float, boolean, char 등의 지역 변수와 매개 변수의 데이터 값이 저장됩니다. 또한, 실제 객체를 참조하고 있는 변수를 저장합니다.
(실제 객체는 Heap 영역에 저장)
(기본 자료형의 데이터들에 대해서는 참조값이 아니라, 실제 값을 스택에 직접 저장합니다.)
스택 프레임은 메소드가 호출될 때 메모리에 할당되고 종료되면 메모리에서 제거됩니다.
스택 메모리의 맨 위(가장 늦게 들어온 스택 프레임)에 존재하는 하나의 스택 프레임만 활성화되며, 그 이전에 존재하는 스택 프레임은 모두 비활성화됩니다. 비활성화된 스택 프레임의 지역 변수에는 접근할 수 없습니다.
public static void main(String[] args) throws IOException {
int num1 = 10;
int num2 = 20;
System.out.println(adder(num1, num2));
System.out.println("end of program");
}
static int adder(int n1, int n2){
int result = n1 + n2;
return result;
}
여기서 임시로 저장해야 하는 것들은 n1, n2 ,result
adder 메소드가 실행되면서 가장 위의 스택 프레임은 adder가 됩니다. 그리고 return 이후에 해당 프레임은 pop됩니다.
바닥 | main | adder | 입구
adder 메소드를 빠져나가는 순간, n1 / n2 / result는 삭제됩니다.
가비지 컬렉션의 대상이 되는 영역입니다.
Heap 메모리에는 실제 객체, 인스턴스가 저장됩니다. Heap 영역에 존재하는 객체들은 Stack 영역의 변수들에 의해 참조됩니다.
긴 생명 주기를 갖는 데이터들이 저장됩니다.
모든 Object 타입(Integer, String, ArrayList 등)은 Heap 영역에 생성됩니다.
스레드 개수에 상관 없이, 단 하나의 Heap 영역만 존재합니다.
참조 변수는 스택에 존재합니다. 위의 simpleMethod() 호출이 종료되면 스택에 존재하는 참조 변수는 사라집니다. 그렇다면 힙 영역에 존재하는 인스턴스들을 가리키는 참조변수가 없기 때문에
우리가 조작할 수 없습니다. 그러므로 지워버리는 게 맞지 않을까요?
여기서, 하나의 인스턴스를 둘 이상의 참조변수가 참조할 수 있습니다.
즉, str1, str2가 "My String"을 가리킬 수 있습니다.
만약 str1, str2가 "My String"을 가리키는 상황에서 str1이 지워졌다고 해서
"My String"을 같이 소멸시켜버리면 str2는 머쓱해집니다.
따라서, 인스턴스는 따로 관리해야 합니다.
인스턴스를 없앨 때는, 그 누구도 자신을 가리키지 않는다고 확신이 생길 때만
없애야 합니다. (JVM이 힙 영역에 존재하는 인스턴스를 스캔한 후에 아무도 가리키지 않는 인스턴스는 알아서 제거합니다. - 가비지 컬렉션)
즉 힙 영역은 조심스럽게 지우기 위해서 존재하는 영역입니다.
--작성 중. 북마크
참고)
Object 클래스의 finalize 메소드
protected void finalize() throws Throwable
→ JVM에 의해서 가비지 컬렉션 될 때 자동으로 호출
→ 인스턴스가 사라질 때 호출되는 메소드.
→ 즉 ,힙 영역에 아무도 가리키지 않는 인스턴스가 있을 때 (가비지가 있을 때)
가비지 컬렉션이 실행되는 동안, 자바 프로그램의 실행 속도 감소.
→ 적절한 알고리즘을 가지고 가비지 컬렉션.
→ 따라서 finalize의 호출을 짐작하기 어렵다.
→ 다만 언젠가는 호출이 된다.
자바 가상머신이 힙 영역을 쭉 훑어보고 가비지 컬렉션이 필요한 것들을
체크하고 시간을 너무 많이 뺏은 것 같으면 다시 프로그램 실행으로 넘어간다.
이후 여유가 생기면 다시 돌아가서 체크해놓은 것들을 삭제한다.
Object 클래스에 정의되어 있는 이 메소드는 인스턴스 소멸 시 자동으로 호출.
자식 클래스에서 오버라이딩 할 수 있음.
Person이 상속하는 Object의 finalize가 무슨 일을 하고 있을까?
내가 상속하는 상위 클래스의 메소드를 오버라이딩 할 때, 그 오버라이딩 직전의 메소드도
어떤 연산을 하고 있을 수 있다. 즉, 그 메소드가 하는 일을 무조건 막아버리는 것은
문제가 될 수도 있다. 따라서, super.finalize();를 통해서 끼워넣기를 실행한다.
즉, 본래의 메소드도 실행하고, 내가 만든 finalize도 실행하게 만든다.(끼워넣기 오버라이딩)
부모 메소드를 건드리지 않고 오버라이딩.
위에서 내가 만든 finalize는 실행되지 않았다.
우선 알아두어야 할 것은 프로그램 종료 시에는 가비지 컬렉션이 필요없다.
프로그램이 종료되면 모든 메모리 영역이 사라지기 때문에 가비지 컬렉션x
소형 디바이스(임베디드 디바이스)에서 제한적인 메모리,cpu를 사용할 때
System.gc(); → JVM에게 가비지 컬렉션 부탁.
System.runFianlization();를 실행하기도 한다.