Java 문법 (8) - 자바 메모리 구조와 static
Java 문법 (8) - 자바 메모리 구조와 static
자바 메모리 구조
자바의 메모리 구조는 크게 메서드 영역, 스택 영역, 힙 영역 3개로 나눌 수 있습니다.
메서드 영역
메서드 영역은 프로그램을 실행하는데 필요한 공통 데이터를 관리하는 영역입니다. 프로그램의 모든 영역에서 공유합니다.
- 클래스 정보(클래스의 실행코드, 필드, 매서드, 생성자 등)를 보관
- static 변수들을 보관
- 런타임 상수 풀: 공통 리터럴 상수들을 보관 (ex “hello”)
스택 영역
스택 영역은 자바 실행시 쓰레드 별로 하나씩 스택이 생성됩니다. 각 스택 프레임은 지역변수, 중간 연산 결과, 메서드 호출 정보 등을 포함합니다.
- 매서드를 호출할 때마다 하나의 스택 프레임이 쌓이고, 메서드가 종료되면 해당 스택 프레임이 제거
- 자바는 스택 영역을 사용해서 메서드 호출과 지역변수, 매개변수를 관리
- 메서드를 계속 호출하면 스택 프레임이 계속 쌓임
- 스택 프레임이 종료되면 지역변수도 함게 제거, 스택 프레임이 모두 종료되면 프로그램도 종료
힙 영역
객체(인스턴스)와 배열이 생성되는 영역입니다. GC(가비지 컬렉션)이 이루어지는 주요 영역이고, 더 이상 참조되지 않는 객체는 GC에 의해서 제거됩니다.
스택 영역 예시
main에서 method1을 호출하고, method1에서 method2를 호출한 예시입니다. 각각 함수마다 start와 end를 시작과 끝에 넣어주면 스택이 어떻게 실행되는지 눈으로 확인할 수 있습니다.
main 시작 -> method1 시작 -> method2 시작 -> method2 종료 -> method1 종료 -> main 종료 순으로 실행됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package memory;
public class JavaMemoryMain1 {
public static void main(String[] args) {
System.out.println("main start");
method1(10);
System.out.println("main end");
}
static void method1(int m1){
System.out.println("method1 start");
int cal = m1 * 2;
method2(cal);
System.out.println("method1 end");
}
static void method2(int m2){
System.out.println("method2 start");
System.out.println("method2 end");
}
}
// 실행결과
// main start
// method1 start
// method2 start
// method2 end
// method1 end
// main end
스택 영역과 힙 영역
아래 예시코드는 스택과 힙 영역이 함께 사용되는 경우를 보기 위한 코드입니다. method1에서 Data의 객체 data1이 생성되고, 이 객체의 참조값을 method2의 매개변수로 전달합니다. 실행 순서를 보면 아래와 같습니다.
main()스택 프레임 생성, (main start 출력)method1()스택 프레임 생성, (method1 start 출력)Data의 객체 힙 영역에 생성,data1이라는 참조값을 담는 지역변수 스택 프레임에 보관method2()스택 프레임 생성,data1을 매개변수data2로 받음, (method2 start 출력)method2종료,method2()스택 프레임 및data2제거, (method2 end 출력)method1종료,method1()스텍 프레임 및data1제거, (method1 end 출력)Data의 객체를 참조하는 곳이 없음, GC가 제거main종료,main()스택 프레임 제거, (main end 출력)- 프로그램 종료
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// memory.java
package memory;
public class Data {
private int value;
public Data(int value) {
this.value = value;
}
public int getValue(){
return value;
}
}
// JavaMemoryMain2.java
package memory;
public class JavaMemoryMain2 {
public static void main(String[] args) {
System.out.println("main start");
method1();
System.out.println("main end");
}
static void method1(){
System.out.println("method1 start");
Data data1 = new Data(10);
method2(data1);
System.out.println("method1 end");
}
static void method2(Data data2){
System.out.println("method2 start");
System.out.println("data.value = " + data2.getValue());
System.out.println("method2 end");
}
}
/* 실행결과
main start
method1 start
method2 start
data.value = 10
method2 end
method1 end
main end
*/
Static 변수
static은 주로 멤버변수와 메서드에 사용됩니다.
클래스의 멤버변수는 두가지 종류로 나뉩니다.
- 인스턴스 변수 : static이 붙지 않은 멤버변수
ex) private String name;- 인스턴스 변수는 인스턴스를 생성해야 사용할 수 있고, 인스턴스에 소속되어 있어서 인스턴스 변수
- 인스턴스를 만들 때 마다 새로 만들어짐
- 클래스 변수 : static이 붙은 멤버 변수
ex) public static int count;클래스 변수,정적 변수,static 변수등으로 불림- static 변수는 클래스 자체에 소속되어 있어서 클래스명으로 바로 접근이 가능하며, 메모리의 메서드 영역에 만들어잠
- 자바 프로그램을 시작할 때 딱 1개가 만들어지며 보통 여러곳에서 공유하는 목적으로 사용됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Data3.java
package static1;
public class Data3 {
public String name;
public static int count;
public Data3 (String name){
this.name = name;
count++;
}
}
// DataCountMain3.java
package static1;
public class DataCountMain3 {
public static void main(String[] args) {
// static으로 count를 공유하기 때문에 공용으로 사용 가능한 특별하게 관리되는 변수
// static 변수는 메서드 영역에서 관리함
Data3 data1 = new Data3("A");
System.out.println("A Count: " + Data3.count); // static 변수는 클래스명으로 접근 가능하다.
Data3 data2 = new Data3("B");
System.out.println("B Count: " + Data3.count);
Data3 data3 = new Data3("C");
System.out.println("C Count: " + Data3.count);
// 추가 인스턴스를 통한 접근도 가능
// 권장하지 않음. count가 인스턴스변수인지 static 변수인지 코드만 보고 알 수 없다.
Data3 data4 = new Data3("D");
System.out.println(data4.count);
// 클래스를 통한 접근
System.out.println(Data3.count);
}
}
Static 메서드
매서드 앞에 static이 붙는 것을 정적 메서드 또는 클래스 메서드라고 합니다. 클래스 자체에 소속되어 있어서 객체 생성없이 클래스명으로 메서드를 바로 호출할 수 있습니다.
static 메서드는 같은 static만 호출할 수 있습니다. 정적 변수나 정적 메서드만 호출이 가능합니다. 인스턴스 변수나 인스턴스 메서드는 사용할 수 없습니다. 반대로 인스턴스 변수나 메서드는 static 메서드를 호출할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package static2;
public class DecoData {
private int instanceValue;
private static int staticValue;
// static은 static만 접근 가능
public static void staticCall(){
staticValue++; // 정적 변수 접근
staticMethod(); // 정적 메서드 접근 가능
// instanceValue++; // static method는 인스턴스 변수 접근 불가능
// instanceMethod(); // 인스턴스 메서드 접근 불가능
}
// instance는 static, instance 전부 접근 가능
public void instanceCall(){
instanceValue++; // 인스턴스 변수 접근 가능
instanceMethod(); // 인스턴스 메서드 접근 가능
staticValue++; // 스태틱 변수 접근 가능
staticMethod(); // 스태틱 메서드 접근 가능
}
private void instanceMethod(){
System.out.println("instanceValue= " + instanceValue);
}
private static void staticMethod(){
System.out.println("static methpd= " + staticValue);
}
}