java virtual machine, call stack, java byte code
DESCRIPTION
Java Virtual Machine의 내부 구조, method call stack, java byte code를 읽기 위해 opcode 이해를 다룬다.TRANSCRIPT
JVM 및 Call Stack
학습 목표Java Virtual Machine 의 메모리 관리
자바 바이트 코드 읽기
JVM 개요
Class Loader System
Operating System
Java Threads
Execution Engine
Run-time Data Area
Garbage Collector
Java Application
JavaAPI
Native Method Librarie
s
Java Virtual Machine
Verify
Link
Prepare
Class Load
Resolve Initialize
ByteCode
ByteCode
Class File
Memory
Class Loader
Class Loader System
Runtime Data Areas
PC Registers
JVM Stacks
Java API Execution EngineJava Class
Files
Native Method Libraries
Method Area Heap Native
Method Stacks
JVM 은 Class Loader 를 활용해 컴파일한 Byte Code 를 Runtime Data Areas 의 Method Area 에
실행 가능한 상태로 Load 한다 .
Class Loader System
Operating System
Java Threads
Execution Engine
Run-time Data Area
Garbage Collector
Java Application
JavaAPI
Native Method Librarie
s
Execution Engine
Class Loader System
Runtime Data Areas
PC Registers
JVM Stacks
Java API Execution EngineJava Class
Files
Native Method Libraries
Method Area Heap Native
Method Stacks
JVM 의 Execution Engine 은 Method Area 에 Load 되어 있는 Byte Code 정보를
활용해 자바 프로그램을 실행한다 .
Execution Engine 은 ByteCode 를 한 라인씩 실행 (interpreting 방식 ) 한다 .
Method Area
모든 Thread 들이 공유하는 메모리 영역이다 .
자바 프로그램을 실행하기 위한 Class(Type), Method, Field 정보를 가진다 .
프로그램에서 공유할 필요가 있는 정보를 가진다 .
Method Area
모든 Thread 들이 공유하는 메모리 영역이다 .
Method Area 에 저장되는 정보
Type Information : 클래스와 관련한 모든 정보
Constant Pool : 문자열 상수와 같은 리터럴 상수 , Symbolic Reference
Field Information : Field 이름 , Data Type, Modifier 등
Method Information : Method 이름 , 입출력 DataType, Modifier 등
Class Variables : static 으로 선언되는 모든 클래스 변수
Reference to Class Class Loader : 특정 Type 을 Load 한 ClassLoader 정보를 관리
Reference to Class class
Method Table : Class 의 Method 에 대한 Direct Reference 를 가진다 .
Class Loader System
Runtime Data Areas
PC Registers
JVM Stacks
Java API Execution EngineJava Class
Files
Native Method Libraries
Method Area Heap Native
Method Stacks
Method Area 의 Byte Code 정보를 활용해 프로그램을 실행할 때는 JVM Stacks, Native
Method Stacks 공간을 활용한다 .
JVM Stacks, Native Method Stacks
각 Thread 마다 서로 다른 메모리가 할당된다 .
Thread 가 시작할 때 생성된다 .
각 Thread 마다 서로 다른 메모리를 사용하기 때문에 여러 명의 사용자가 동시에 같은 method 에
접근해도 문제가 발생하지 않는다 .
Class Loader System
Runtime Data Areas
PC Registers
JVM Stacks
Java API Execution EngineJava Class
Files
Native Method Libraries
Method Area Heap Native
Method Stacks
JVM 은 Heap 에서 자바 프로그램을 실행할 때 자바 클래스의 인스턴스와 Array 에 대한 메모리를
관리한다 .
Method Area
Thread A( 사용자 A)
JVM Stacks
main() Stack frame
Operand Stack
Local Variable
0 args
constant pool
프로그램을 실행하면 사용자마다 독립적인 JVM Stack 이 생성된다 .
JVM Stack 안에는 메서드가 호출 될 때마다 Stack Frame 이 생성된다 .
JVM Stacks
main() Stack frame
Operand Stack
Local Variable
0 args
JVM Heap
Thread B( 사용자 B)
Method Area
constant pool
JVM Heap 과 Method Area 는 Thread 가 메모리를 공유한다 .
JVM Heap
Thread B( 사용자 B)
Thread A( 사용자 A)
Method Area
constant pool
Method Area 는 클래스 ByteCode 와 변하지 않는 값이 존재하기 때문에 이슈가 없다 .
Thread 가 데이터를 공유함으로써 이슈가 있는 부분은 JVM Heap 메모리이다 .
JVM Heap
Thread B( 사용자 B)
Thread A( 사용자 A)
Heap
모든 Thread 들이 공유하는 메모리 영역이다 .
JVM 에서 대부분의 메모리 이슈가 집중되어 있는 부분이다 .
JVM 에서 Memory 가 할당 , 해제 (Garbage Collection) 의 이슈는 대부분 Heap 메모리이다 .
package net.slipp;
public class Adder { int add(int i, int j) { return i + j; } public static void main(String[] args) { Adder adder = new Adder(); int result = adder.add(5,2); System.out.println(result); }}
package net.slipp;
public class Adder { int add(int i, int j) { return i + j; } public static void main(String[] args) { Adder adder = new Adder(); int result = adder.add(5,2); System.out.println(result); }}
JVM Stacksmain() Stack
frameOperand
Stack
Local Variable
0 args
1. main method 실행을 위한 stack frame
생성
package net.slipp;
public class Adder { int add(int i, int j) { return i + j; } public static void main(String[] args) { Adder adder = new Adder(); int result = adder.add(5,2); System.out.println(result); }}
JVM Stacksmain() Stack
frameOperand
Stack
Local Variable
0 args
1. main method 실행을 위한 stack frame
생성
2. Adder 생성자를 위한 stack frame 생성
Adder 생성자 Stack frame
Operand Stack
Local Variable
0 this
package net.slipp;
public class Adder { int add(int i, int j) { return i + j; } public static void main(String[] args) { Adder adder = new Adder(); int result = adder.add(5,2); System.out.println(result); }}
JVM Stacksmain() Stack
frameOperand
Stack
Local Variable
0 args
1. main method 실행을 위한 stack frame
생성
2. Adder 생성자를 위한 stack frame 생성
3. 생성자 실행이 끝나면 Adder 생성자를
위한 stack frame 제거
JVM Stacksmain() Stack
frameOperand
Stack
Local Variable
0 args
1. main method 실행을 위한 stack
frame 생성
2. Adder 생성자를 위한 stack frame 생성
3. 생성자 실행이 끝나면 Adder 생성자를
위한 stack frame 제거
4. add method 를 위한 stack frame 생성
package net.slipp;
public class Adder { int add(int i, int j) { return i + j; } public static void main(String[] args) { Adder adder = new Adder(); int result = adder.add(5,2); System.out.println(result); }}
add method Stack frame
Operand Stack
Local Variable
0 this
51
22
JVM Stacksmain() Stack
frameOperand
Stack
Local Variable
0 args
1. main method 실행을 위한 stack
frame 생성
2. Adder 생성자를 위한 stack frame
생성
3. 생성자 실행이 끝나면 Adder 생성자를
위한 stack frame 제거
4. add method 를 위한 stack frame 생성
5. add method 에 대한 stack frame 제거
package net.slipp;
public class Adder { int add(int i, int j) { return i + j; } public static void main(String[] args) { Adder adder = new Adder(); int result = adder.add(5,2); System.out.println(result); }}
JVM Stacks
1. main method 실행을 위한 stack
frame 생성
2. Adder 생성자를 위한 stack frame
생성
3. 생성자 실행이 끝나면 Adder 생성자를
위한 stack frame 제거
4. add method 를 위한 stack frame
생성
5. add method 에 대한 stack frame
제거
6. main method 에 대한 stack frame
제거
package net.slipp;
public class Adder { int add(int i, int j) { return i + j; } public static void main(String[] args) { Adder adder = new Adder(); int result = adder.add(5,2); System.out.println(result); }}
자바 바이트 코드 읽기
Execution Engine
컴파일한 바이트 코드를 한 라인씩 읽어내려가면서 자바 프로그램을 실행한다 .
javap –c HelloWorld.class
public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!"); }}
D:\next-workspace\workspace\java\bin>javap -c HelloWorld.classCompiled from "HelloWorld.java"public class HelloWorld { public HelloWorld(); Code: 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V 4: return
public static void main(java.lang.String[]); Code: 0: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #22 // String Hello World! 5: invokevirtual #24 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return}
D:\next-workspace\workspace\java\bin>javap -verbose HelloWorld.classClassfile /D:/next-workspace/workspace/java/bin/HelloWorld.class Last modified 2013. 3. 20; size 534 bytes MD5 checksum 77129a8bac38a3ac8dc870dd7362668c Compiled from "HelloWorld.java"public class HelloWorld SourceFile: "HelloWorld.java" minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPERConstant pool: #1 = Class #2 // HelloWorld #2 = Utf8 HelloWorld #3 = Class #4 // java/lang/Object #4 = Utf8 java/lang/Object #5 = Utf8 <init> #6 = Utf8 ()V #7 = Utf8 Code #8 = Methodref #3.#9 // java/lang/Object."<init>":()V #9 = NameAndType #5:#6 // "<init>":()V #10 = Utf8 LineNumberTable #11 = Utf8 LocalVariableTable #12 = Utf8 this #13 = Utf8 LHelloWorld; #14 = Utf8 main #15 = Utf8 ([Ljava/lang/String;)V #16 = Fieldref #17.#19 // java/lang/System.out:Ljava/io/PrintStream; #17 = Class #18 // java/lang/System #18 = Utf8 java/lang/System #19 = NameAndType #20:#21 // out:Ljava/io/PrintStream; #20 = Utf8 out #21 = Utf8 Ljava/io/PrintStream; #22 = String #23 // Hello World! #23 = Utf8 Hello World! #24 = Methodref #25.#27 // java/io/PrintStream.println:(Ljava/lang/String;)V #25 = Class #26 // java/io/PrintStream #26 = Utf8 java/io/PrintStream #27 = NameAndType #28:#29 // println:(Ljava/lang/String;)V #28 = Utf8 println #29 = Utf8 (Ljava/lang/String;)V #30 = Utf8 args #31 = Utf8 [Ljava/lang/String; #32 = Utf8 SourceFile #33 = Utf8 HelloWorld.java
javap –verbose HelloWorld.class
{ public HelloWorld(); flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LHelloWorld;
public static void main(java.lang.String[]); flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #22 // String Hello World! 5: invokevirtual #24 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 3: 0 line 4: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 args [Ljava/lang/String;}
Bytecode Visualizer Eclipse 플러그인 활용 (http://www.slipp.net/questions/292)
Java opcode(bytecode instruction)
http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings
Pre-fix/
SuffixOperand Type
i integer
l long
s short
b byte
c character
f float
d double
z boolean
a reference
JVM Stacksadd method Stack frame
Operand Stack
Local Variable
0 this
51
22
load opcode
Local Variable 영역의 값을 Operand Stack 영역으로 복사하는 명령어(instruction)
JVM Stacksadd method Stack frame
Operand Stack
Local Variable
0 this
51
22
iload_1 실행 후
50
JVM Stacksmain method Stack frame
Operand Stack
Local Variable
0 this
adder1
store opcode
Operand Stack 영역의 값을 Local Variable 영역으로 이동하는 명령어(instruction)
istore_2 실행 후
70
JVM Stacksmain method Stack frame
Operand Stack
Local Variable
0 this
adder1
72
invoke* opcode : 생성자 , method 를 호출할 때
사용하는 opcode
• invokestatic : 클래스 (static) method 를 호출
• invokeinterface : interface method 를 호출
• invokespecial : 생성자 또는 인스턴스 method( 다형성 X) 호출
• Invokevirtual : 인스턴스 method 를 호출 ( 다형성 O)
public class StringConcatenations { public String concat1(String start, String end) { return start + end; } public void concat2(StringBuffer start, String end) { start.append(end); }}
Case 1
public class StringLiterals { public static void main(String[] args) { String one = "Test"; String two = "Test"; String three = "T" + "e" + "s" + "t"; String four = new String("Test"); }}
Case 2
Case 2
public class MyFor { public int sum(int[] values) { int sum = 0; for (int i = 0; i < values.length; i++) { sum +=- values[i]; } return sum; }}
Case 3
Execution Engine Simulation1
package net.slipp;
public class Adder { int add(int i, int j) { return i + j; } public static void main(String[] args) { Adder adder = new Adder(); int result = adder.add(5,2); System.out.println(result); }}
D:\next-workspace\workspace\java\bin>javap -c net/slipp/AdderCompiled from "Adder.java"public class net.slipp.Adder { public net.slipp.Adder(); Code: 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V 4: return
int add(int, int); Code: 0: iload_1 1: iload_2 2: iadd 3: ireturn
public static void main(java.lang.String[]); Code: 0: new #1 // class net/slipp/Adder 3: dup 4: invokespecial #21 // Method "<init>":()V 7: astore_1 8: aload_1 9: iconst_5 10: iconst_2 11: invokevirtual #22 // Method add:(II)I 14: istore_2 15: getstatic #24 // Field java/lang/System.out:Ljava/io/PrintStream; 18: iload_2 19: invokevirtual #30 // Method java/io/PrintStream.println:(I)V 22: return}
D:\next-workspace\workspace\java\bin>javap -c Adder.classCompiled from "Adder.java"public class Adder { public Adder(); Code: 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V 4: return
int add(int, int); Code: 0: iload_1 1: iload_2 2: iadd 3: ireturn
public static void main(java.lang.String[]); Code: 0: new #1 // class Adder 3: dup 4: invokespecial #21 // Method "<init>":()V 7: astore_1 8: aload_1 9: iconst_5 10: iconst_2 11: invokevirtual #22 // Method add:(II)I 14: istore_2 15: getstatic #24 // Field java/lang/System.out:Ljava/io/PrintStream; 18: iload_2 19: invokevirtual #30 // Method java/io/PrintStream.println:(I)V 22: return}
Execution Engine 은 interpreting 방식으로 한번에 한 라인씩 실행한다 .
한 라인을 instruction 이라고 한다 .
instruction 은 offset, opcode(operation code), 피연산자 (operand) 로 구성된다 .
JVM Stacks Method Area
JVM Heap
JVM 은 Method Area 에 저장되어 있는 Bytecode 를 interpreting 방식으로 읽어 실행해 나간다 . 먼저 Adder 의 main() 가 Entry Point 가 된다 .
main() Stack frame
Operand Stack
Local Variable
0 args
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
public static void main(java.lang.String[]); Code: 0: new #1 // class net/slipp/Adder
Method Area 에 있는 Adder 정보를 활용해 Heap 메모리에 Adder Instance 를 생성한 후 Operand Stack 0 에 Adder Instance 에 대한 reference 를 저장
Adder Instance
adder0
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
public static void main(java.lang.String[]); Code: 0: new #1 // class net/slipp/Adder 3: dup
Operand Stack 0 에 Adder Instance 에 대한 reference 를 Operand Stack 1 에 복사한다 .
Adder Instance
adder0
adder1
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
public static void main(java.lang.String[]); Code: 3: dup 4: invokespecial #21 // Method "<init>":()V
Adder 클래스의 기본 생성자 (Object 기본 생성자 ) 를 호출한다 . Adder 기본 생성자 Stack Frame 이 생성된다 .
Adder Instance
adder0
Adder 생성자 Stack frame
Operand Stack
Local Variable
0 this
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
public net.slipp.Adder(); Code: 0: aload_0
Adder 생성자 Stack Frame 의 Local Variable의 index 0 의 값을 Operand Stack 으로 로드한다 .
Adder Instance
adder0
Adder 생성자 Stack frame
Operand Stack
Local Variable
0 this
this0
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
public net.slipp.Adder(); Code: 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V
Adder Instance 에 대한 실질적인 초기화가 이 단계에서 진행된다 .
초기화가 완료된 Adder Instance
adder0
Adder 생성자 Stack frame
Operand Stack
Local Variable
0 this
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
public net.slipp.Adder(); Code: 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V 4: return
Adder 기본 생성자에 대한 Stack Frame 이 JVM Stack 에서 제거된다 .
adder0
초기화가 완료된 Adder Instance
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
public static void main(java.lang.String[]); Code: […] 4: invokespecial #21 // Method "<init>":()V 7: astore_1
Operand Stack 에서 pop 한 결과를 Local Variable 의 index 1 번에 저장한다 .
adder1
초기화가 완료된 Adder Instance
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
public static void main(java.lang.String[]); Code: […] 7: astore_1 8: aload_1
Variable 의 index 1 번에 저장된 값을 Operand Stack 에 push 한다 .
adder1
초기화가 완료된 Adder Instance
adder0
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
public static void main(java.lang.String[]); Code: […] 8: aload_1 9: iconst_5
Method Area 의 constant pool 에 존재하는 상수 값 5 를 Operand Stack 에 push 한다 .
adder1
초기화가 완료된 Adder Instance
adder0
1
constant pool
2
…
51
5
Method Area
JVM Stacks Method Area
JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
public static void main(java.lang.String[]); Code: […] 9: iconst_5 10: iconst_2
Method Area 의 constant pool 에 존재하는 상수 값 2 를 Operand Stack 에 push 한다 .
adder1
초기화가 완료된 Adder Instance
adder0
1
constant pool
2
…
51
5
22
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
public static void main(java.lang.String[]); Code: […] 10: iconst_2 11: invokevirtual #22 // Method add:(II)I
add() 메소드에 대한 Stack frame 을 JVM Stack 에 push 하고 main() 메소드의 operand stack 의 값을 add() 메소드에 복사한다 .
adder1
초기화가 완료된 Adder Instance
1
constant pool
2
…
5
add() Stack frame
Operand Stack
Local Variable
0 adder
51
22
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
int add(int, int); Code: 0: iload_1
add() 메소드 Stack Frame 의 Local Variable의 index 1 번 값을 Operand Stack 에 push한다 .
adder1
초기화가 완료된 Adder Instance
1
constant pool
2
…
5
add() Stack frame
Operand Stack
Local Variable
0 adder
51
22
50
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
int add(int, int); Code: 0: iload_1 1: iload_2
add() 메소드 Stack Frame 의 Local Variable의 index 2 번 값을 Operand Stack 에 push한다 .
adder1
초기화가 완료된 Adder Instance
1
constant pool
2
…
5
add() Stack frame
Operand Stack
Local Variable
0 adder
51
22
50
21
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
int add(int, int); Code: 0: iload_1 1: iload_2 2: iadd
Operand Stack 의 인자를 iadd 에 전달해 덧셈을 실행한다 . 실행한 결과를 Operand Stack 에 저장한다 .
adder1
초기화가 완료된 Adder Instance
1
constant pool
2
…
5
add() Stack frame
Operand Stack
Local Variable
0 adder
51
22
70
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
int add(int, int); Code: […] 2: iadd 3: ireturn
add() Stack Frame 의 operand stack 값을 main() 메소드로 복사하고 add() Stack Frame을 pop 한다 .
adder1
초기화가 완료된 Adder Instance
1
constant pool
2
…
5
70
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
Operand Stack 값을 pop 해서 Local Variable의 index 2 번에 저장한다 .
adder1
초기화가 완료된 Adder Instance
1
constant pool
2
…
572
public static void main(java.lang.String[]); Code: […] 11: invokevirtual #22 // Method add:(II)I 14: istore_2
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
PrintStream 에 대한 reference 를 얻어 operand stack index 0 에 저장한다 .
adder1
초기화가 완료된 Adder Instance
1
constant pool
2
…
572
public static void main(java.lang.String[]); Code: […] 14: istore_2 15: getstatic #24 // Field java/lang/System.out:Ljava/io/PrintStream;
PrintStream Instance
out0
Method Area
class variables
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
Local Variable index 2 값을 operand stack에 push 한다 .
adder1
초기화가 완료된 Adder Instance
1
constant pool
2
…
572
public static void main(java.lang.String[]); Code: […] 15: getstatic #24 // Field java/lang/System.out:Ljava/io/PrintStream; 18: iload_2
out0
71
Method Area
PrintStream Instance
class variables
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
PrintStream 인스턴스의 println() 메소드에 operand 값을 인자로 전달한다 .
adder1
초기화가 완료된 Adder Instance
1
constant pool
2
…
572
public static void main(java.lang.String[]); Code: […] 18: iload_2 19: invokevirtual #30 // Method java/io/PrintStream.println:(I)V
Method Area
PrintStream Instance
class variables
println() Stack frame
Operand Stack
Local Variable
this0
71
JVM Stacks JVM Heap
애플리케이션을 종료한다 .
1
constant pool
2
…
5
public static void main(java.lang.String[]); Code: […] 19: invokevirtual #30 // Method java/io/PrintStream.println:(I)V 22: return
시간이 지나면 Garbage Collector 에 의해 GC 대상이 된다 .
초기화가 완료된 Adder Instance
Method Area
PrintStream Instance
class variables
Execution Engine Simulation2
package net.slipp;
public class Adder { private int i; private int j;
Adder(int i, int j) { this.i = i; this.j = j; }
public int add() { return i + j; }
public static void main(String[] args) { Adder adder = new Adder(5, 2); int result = adder.add(); System.out.println(result); }}
D:\next-workspace\workspace\java\bin>javap -c net/slipp/AdderCompiled from "Adder.java"public class net.slipp.Adder { net.slipp.Adder(int, int); Code: 0: aload_0 1: invokespecial #11 // Method java/lang/Object."<init>":()V 4: aload_0 5: iload_1 6: putfield #14 // Field i:I 9: aload_0 10: iload_2 11: putfield #16 // Field j:I 14: return
public int add(); Code: 0: aload_0 1: getfield #14 // Field i:I 4: aload_0 5: getfield #16 // Field j:I 8: iadd 9: ireturn
public static void main(java.lang.String[]); Code: 0: new #1 // class net/slipp/Adder 3: dup 4: iconst_5 5: iconst_2 6: invokespecial #26 // Method "<init>":(II)V 9: astore_1 10: aload_1 11: invokevirtual #28 // Method add:()I 14: istore_2 15: getstatic #30 // Field java/lang/System.out:Ljava/io/PrintStream; 18: iload_2 19: invokevirtual #36 // Method java/io/PrintStream.println:(I)V 22: return}
JVM Stacks Method Area
JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
public static void main(java.lang.String[]); Code: 0: new #1 // class net/slipp/Adder 3: dup
Adder Instance
adder0
adder1
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
public static void main(java.lang.String[]); Code: […] 4: iconst_5 5: iconst_2
adder1
Adder Instance
adder0
1
constant pool
2
…
51
5
22
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
public static void main(java.lang.String[]); Code: […] 4: iconst_5 5: iconst_2 6: invokespecial #26 // Method "<init>":(II)V
adder1
Adder Instance
1
constant pool
2
…
5
Adder 생성자 Stack frame
Operand Stack
Local Variable
adder0
51
22
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
net.slipp.Adder(int, int); Code: […] 4: aload_0 5: iload_1
adder1
Adder Instance
1
constant pool
2
…
5
Adder 생성자 Stack frame
Operand Stack
Local Variable
adder0
51
22
adder0
51
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
net.slipp.Adder(int, int); Code: […] 5: iload_1 6: putfield #14 // Field i:I
adder1
1
constant pool
2
…
5
Adder 생성자 Stack frame
Operand Stack
Local Variable
adder0
51
22
Adder Instance
int i = 5
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
net.slipp.Adder(int, int); Code: […] 9: aload_0 10: iload_2 11: putfield #16 // Field j:I
adder1
1
constant pool
2
…
5
Adder 생성자 Stack frame
Operand Stack
Local Variable
adder0
51
22
Adder Instance
int i = 5int j = 2
Method Area
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
public int add(); Code: 0: aload_0 1: getfield #14 // Field i:I 4: aload_0 5: getfield #16 // Field j:I
adder1
1
constant pool
2
…
5
add() Stack frame
Operand Stack
Local Variable
adder0
초기화가 완료된 Adder Instance
int i = 5int j = 2
adder0
51
22
Method Area
나머지 과정은 Simulation1 과 비슷하다 .
JVM Stacks vs Heap
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
adder1
Adder Instance
1
constant pool
2
…
5
add() Stack frame
Operand Stack
Local Variable
0 adder
51
22
50
21
Method Area
public class Adder { int add(int i, int j) { return i + j; }}
JVM Stacksmain() Stack frame
Operand Stack
Local Variable
0 args
adder1
add() Stack frame
Operand Stack
Local Variable
0 adder
51
22
50
21
사용자 1 = Thread 1
JVM Stacksmain() Stack frame
Operand Stack
Local Variable
0 args
adder1
add() Stack frame
Operand Stack
Local Variable
0 adder
51
22
50
21
사용자 2 = Thread 2
JVM Stacks JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
adder1
1
constant pool
2
…
5
Adder Instance
int i = 5int j = 2
Method Area
JVM Heap
Adder Instance
int i = 5int j = 2
사용자 1 = Thread 1
사용자 2 = Thread 2
public class Adder { private int i; private int j;
Adder(int i, int j) { this.i = i; this.j = j; }}
Class Loader System
Runtime Data Areas
PC Registers
JVM Stacks
Java API Execution EngineJava Class
Files
Native Method Libraries
Method Area Heap Native
Method Stacks
JVM 의 Stacks 영역은 Thread 별로 ( 사용자 ) 생성된다 . 따라서 각 Stacks 영역 사이에는 영향을 미치지 않는다 . 이를 Thread Safe 하다고 한다 .
JVM 의 Heap 영역은 모든 Thread 가 공유한다 . 따라서 Heap 에 존재하는 Object 의 상태 값은 Thread 사이에 영향을 받는다 . 이를 Thread Safe 하지 않다고 한다 .
public class Adder { int add(int i, int j) { return i + j; } public static void main(String[] args) { Adder adder = new Adder(); int result = adder.add(5,2); }}
사용자 1 = Thread 1
사용자 2 = Thread 2
Adder 는 Heap 영역에서 관리하는 상태 값은 없다 . 모든 값은 각 Thread 별로 생성되는 Stacks 에서 관리한다 .
위 Adder 클래스는 Heap 영역에 인스턴스 하나를 생성한 후 모든 Thread 에서 재사용할 수 있다 .
public class Adder { private int i; private int j;
Adder(int i, int j) { this.i = i; this.j = j; }
public int add() { return i + j; }
public static void main(String[] args) { Adder adder = new Adder(5, 2); int result = adder.add(); System.out.println(result); }}
사용자 1 = Thread 1
사용자 2 = Thread 2
Adder 는 Heap 영역에서 i, j 에 대한 상태 값을 관리하고 있다 . 즉 , Thread Safe 하지 않다 .
위 Adder 클래스는 각 Thread 별로 독립적으로 실행하려면 매번 인스턴스를 생성해야 한다 .
Call by value 와 Call by reference
public class Calculator { int add(int i, int j) { i = 5; j = 3; return i + j; }
public static void main(String[] args) { Calculator calculator = new Calculator(); int i = 3; int j = 2; System.out.println("before i : " + i); System.out.println("before j : " + j);
calculator.add(i, j);
System.out.println("after i : " + i); System.out.println("after j : " + j); }}
JVM Stacks Method Area
JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
calculator1
초기화가 완료된 Calculator Instanceadd() Stack frame
Operand Stack
Local Variable
3
2
2
3
public static void main(String[] args) { Calculator calculator = new Calculator(); int i = 3; int j = 2; calculator.add(i, j);}
JVM Stacks Method Area
JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
calculator1
초기화가 완료된 Calculator Instanceadd() Stack frame
Operand Stack
Local Variable
0 calculator
31
223
2
2
3
int add(int i, int j) { i = 5; j = 3; return i + j;}
JVM Stacks Method Area
JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
calculator1
초기화가 완료된 Calculator Instanceadd() Stack frame
Operand Stack
Local Variable
0 calculator
51
323
2
2
3
int add(int i, int j) { i = 5; j = 3; return i + j;}
Call by value
자바의 기본 자료형 (primitive type) 은 call by value 에 의해서 동작한다 .
즉 , 메소드를 호출할 때 전달되는 인자의 값이 복사된다 .
public class Student { String name; public Student(String name) { this.name = name; }}
public class MessageRenderer { public void callByReference(Student student) { student.name = " 예은 "; } public static void main(String[] args) { Student student = new Student(" 주한 "); MessageRenderer mr = new MessageRenderer(); mr.callByReference(student); System.out.println("Name : " + student.name); }}
Case 1
JVM Stacks Method Area
JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
student1
name = “ 주한” Student Instance
callByReference() Stack frame
Operand Stack
Local Variable
Case 1public static void main(String[] args) { Student student = new Student(" 주한 "); MessageRenderer mr = new MessageRenderer(); mr.callByReference(student); System.out.println("Name : " + student.name);}
JVM Stacks Method Area
JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
student1
name = “ 주한” Student Instance
callByReference() Stack frame
Operand Stack
Local Variable
Case 1public static void main(String[] args) { Student student = new Student(" 주한 "); MessageRenderer mr = new MessageRenderer(); mr.callByReference(student); System.out.println("Name : " + student.name);}
public void callByReference(Student student) { student.name = " 예은 ";}
student0
JVM Stacks Method Area
JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
student1
name = “ 예은” Student Instance
callByReference() Stack frame
Operand Stack
Local Variable
Case 1public static void main(String[] args) { Student student = new Student(" 주한 "); MessageRenderer mr = new MessageRenderer(); mr.callByReference(student); System.out.println("Name : " + student.name);}
public void callByReference(Student student) { student.name = " 예은 ";}
student0
JVM Stacks Method Area
JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
student1
name = “ 예은” Student Instance
Case 1public static void main(String[] args) { Student student = new Student(" 주한 "); MessageRenderer mr = new MessageRenderer(); mr.callByReference(student); System.out.println("Name : " + student.name);}
public class Student { String name; public Student(String name) { this.name = name; }}
public class MessageRenderer { public void callByReference(Student student) { student = new Student(" 예은 "); } public static void main(String[] args) { Student student = new Student(" 주한 "); MessageRenderer mr = new MessageRenderer(); mr.callByReference(student); System.out.println("Name : " + student.name); }}
Case 2
JVM Stacks Method Area
JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
student1
name = “ 주한” Student Instance
callByReference() Stack frame
Operand Stack
Local Variable
Case 2public static void main(String[] args) { Student student = new Student(" 주한 "); MessageRenderer mr = new MessageRenderer(); mr.callByReference(student); System.out.println("Name : " + student.name);}
JVM Stacks Method Area
JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
student1
name = “ 주한” Student Instance
callByReference() Stack frame
Operand Stack
Local Variable
Case 1public static void main(String[] args) { Student student = new Student(" 주한 "); MessageRenderer mr = new MessageRenderer(); mr.callByReference(student); System.out.println("Name : " + student.name);}
public void callByReference(Student student) { student = new Student(“ 예은” );}
student0
JVM Stacks Method Area
JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
student1
name = “ 주한” Student Instance
callByReference() Stack frame
Operand Stack
Local Variable
Case 1public static void main(String[] args) { Student student = new Student(" 주한 "); MessageRenderer mr = new MessageRenderer(); mr.callByReference(student); System.out.println("Name : " + student.name);}
public void callByReference(Student student) { student = new Student(“ 예은” );}
student0
name = “ 예은” Student Instance
JVM Stacks Method Area
JVM Heap
main() Stack frame
Operand Stack
Local Variable
0 args
student1
name = “ 주한” Student Instance
Case 1public static void main(String[] args) { Student student = new Student(" 주한 "); MessageRenderer mr = new MessageRenderer(); mr.callByReference(student); System.out.println("Name : " + student.name);}
Call by reference
자바의 모든 객체 ( 인스턴스 ) 는 call by reference 에 의해서 동작한다 .
즉 , 메소드를 호출할 때 전달되는 인자는 값이 아니라 참조하고 있는 주소 값이 전달된다 .
참고 문서