2017년 6월 30일 금요일

Garbage Collection

Garbage Collection 이란?

말 그대로 garbage를 모으는 작업이다.
garbage란 Heap과 Method Area에서 사용되지 않는 Object를 의미한다.
사용되지 않는다는 의미를 어디까지 볼 것인지가 포인트
 - 현재 사용되지 않는 Object(객체)를 garbage로 판단하고 있는것이다.
   현재의 사용여부는 Root Set과의 관계로 판단한다. Root Set에서 어떤 식으로든 Reference 관계가 있다면 Reachable Object라고 한다. 이것을 현재 사용하고 있는 Object로 간주하게 된다.

 Root Set
 1) Stack의 참조정보, Local Variable Section과 Operand Stack에 Object의 Reference 정보가 있다면 이것은 Reachable Object이다. 이 Object들은 현재 Thread들이 사용하고 있는 것으로 간주되는 것이다.
 2) Method Area에 로딩된 클래스, 그 중에서도 Constant pool에 있는 Reference정보

call by reference, call by value in java

call-by-value 는 함수로 인자를 전달할 때 전달될 변수의 값을 복사하여 함수의 인자로 전달한다. 또한 복사된 값은 함수내에서 지역적으로 사용되는 local value라는 특징을 가지고 있다. 그리고 호출한 caller는 인자값을 복사방식으로 넘겨주었으므로, callee내에서 어떤 작업을 하더라도 caller는 영향을 받지 않는다.

 void swap(int target1, int target2){
   int tmp = target1;
   target1 = target2;
   target2 = tmp;
}
 int main(int argc, char** argv){
   int x = 10, y = 20;
   swap(x, y);
   printf("x=%d, y=%d\n",x,y);
   return;
}

결과 => 10, 20

swap 함수를 수행하고 x와 y의 값은 변경되지 않았음, 이는 swap 함수에 아규먼트가 전달될 때, x와 y를 복사해서 값만 넘겨주기 때문이다. swap 메소드 안에 메모리 영역에 새로운 x,y변수가 생성된다는 뜻이다. 이것이 call by value, 값에 의한 호출의 핵심이다.


Call-by-refrence는 인자로 사용될 변수의 레퍼런스(주소)를 함수로 전달하며, 그것이 변수의 값은 아니다라고 되어있다.
 C언어는 포인터로 call-by-reference를 구현할 수 있다.

void swap(int* target1, int* target2){
  int tmp = *target1;
  *target1 = *target2;
  *target2 = tmp;
}

int main(int argc, char** argv){
  int x = 10, y =20;
  swap(x,y);
  printf("x=%d, y=%d\n",x,y);
  return ;
}

결과 20, 10

swap 함수에 x, y가 저장된 곳 자체를 넘겼기 때문에 함수의 아규먼트로 레퍼런스를 넘겨서 변경 사항을 함수를 호출한 곳에서도 적용되게끔 하는 방식이 'call by reference' 이다.


자바에서는 기본적으로 call-by-value방식으로 값을 전달하지만,
참조자료형인 변수가 아규먼트 일때는 ref값을 넘기기 때문에 call-by-reference처럼 보이게 되는것이다.

2017년 6월 28일 수요일

Runtime Data Area simulation


JvmInternal.java

class JvmInternal {
   static int cv = 0;
   final static int fcv = 100;

   public static void main(String[] args){
       int a, b, c;
       a = Integer.parseInt(args[0]);
       b = Integer.parseInt(args[1]);
       c = addTwoArgs(a,b);
   }

   static int addTwoArgs(int x, int y){
      cv = fcv;
      return (x + y);
   }

}
====================================================

javap -c JvmInternal.class

class JvmInternal {

  static int cv;

  static final int fcv;


  JvmInternal();

    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);

    Code:
       0: aload_0
       1: iconst_0
       2: aaload
       3: invokestatic  #2                  // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
       6: istore_1
       7: aload_0
       8: iconst_1
       9: aaload
      10: invokestatic  #2                  // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
      13: istore_2
      14: iload_1
      15: iload_2
      16: invokestatic  #3                  // Method addTwoArgs:(II)I
      19: istore_3
      20: return

  static int addTwoArgs(int, int);

    Code:
       0: bipush        100
       2: putstatic     #5                  // Field cv:I
       5: iload_0
       6: iload_1
       7: iadd
       8: ireturn

  static {};

    Code:
       0: iconst_0
       1: putstatic     #5                  // Field cv:I
       4: return
}

JvmInternal 의 수행을 위해서 ClassLoader에 의해 JVM에 적재되는 과정을 거치고, Class가 Loading이 되면 우선 Method Area에 Class의 정보가 올라가게 되고, 이 작업이 완료되면 Heap에는 JvmInternal의 Instance가 하나 생성된다.


JvmInternal();
 => 생성자, Hidden this Method
    Code:
     0: aload_0
       => local variable영역의 0번 인덱스에서 args의 reference를 로딩
     1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       => instance의 초기화 메소드를 호출
       => Heap에 JVMInternal 인스턴스를 생성
     4: return
       => return ""

/.......

  static {};  // static 초기화 블럭?
    Code:
       0: iconst_0
       1: putstatic     #5                  // Field cv:I
       4: return
}

iconst_0 와 putstaatic은 클래스변수인 cv에 값0을 집어 넣었음을 의미함.
fcv 값은 상수이기 때문에 Constant Pool에 들어가 있다.

main() 메소드를 수행하게되면, 이 메소드에 해당하는 Stack에 새로운 Stack Frame이 하나 생성되어 Push된다. 그리고 이 Method를 호출하면서 args[]의 Reference데이터를 그대로 넘겨 주었다.
stack Frame은 메소드의 정보를 바탕으로 알맞은 크기로 생성됨. 

public static void main(java.lang.String[]);
  Code:
   0: aload_0
 => Local Variable 영역의 0번 인덱스를 load
   0번 인덱스의 값은 args임
   1: iconst_0
 => Reference를 통해 가져온 args의 인덱스 값으로 0이라는 상수를 취함
    Array args에서 어떤 값을 가졍ㄹ지에 대해 array index를 지정
   2: aaload
 => args[0]의 값을 heap에서 reference를 통해 가져옴
   3: invokestatic  #2       // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
 => parseInt라는 Method를 호출하여 실행
   6: istore_1
 => local variable의 1번 인덱스에 위의 결과를 저장

만약 static 메소드가 아닌 instance 메소드라면 local variable의 0번 인덱스에는 this에 해당하는 Reference가 들어가게 된다.
args[0]의 값을 알기위해 Local Variable에 저장된 args[]의 Reference를 Operand Stack에 Push한다.
 그 후 값을 가져올 args[]의 인덱스를 지정한다. Heap에 저장된 args[]의 Array Data에서 0번지의 값을 Pop하여 그 Reference로 10을 찾아 Operend Stack으로 집어 넣는다. 

     7: aload_0
     8: iconst_1
     9: aaload
    10: invokestatic  #2                  // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
    13: istore_2
    14: iload_1
 => Local Varible 1 번 인덱스에서 값을 load함 a의 값을 load
    15: iload_2
 => Local Varible 2 번 인덱스에서 값을 load함 b의 값을 load
    16: invokestatic  #3                  // Method addTwoArgs:(II)I
 => Static Method addTwoArgs를 호출


    19: istore_3
 => local variable의 3번 인덱스에 결과를 저장
    20: return
 => void형 Method에서 return

차례로 Stack Frame을 삭제하고 프로그램을 종료...

//====================

static int addTwoArgs(int, int);
  Code:
     0: bipush        100
 => stack에 byte값 100을 집어넣음
    값이 byte의 범위에 있으므로 bipush임
    128이 넘으면 short으로 인식하여 sipush로 처리함
     2: putstatic     #5                  // Field cv:I
 => static 변수 cv에 100을 집어넣음
     5: iload_0
 => local varible 0번 인덱스에서 값을 load함
     6: iload_1
 => local varible 1번 인덱스에서 값을 load함
     7: iadd
 => 두 int를 더함
     8: ireturn
 => Method에서 int값을 반환

Frame Data에 저장되는 Constant Pool Resolution에는 이 Method에서 사용되는 java.lang.Integer 객체나 java.lang.String객체의 심볼릭레퍼런스에 대한 실제 포인터가 저장되어있음
또한 main() 메소드를 호출한 Hidden this() 메소드의 주소값도 가지고 있다. 이는 main() Method가 정상 종료 후 복귀하기 위한 용도로 사용된다.

2017년 6월 26일 월요일

Runtime Data Areas - Method Area , Heap

■ 정의 : Runtime Data Areas는 Process로서의 JVM이 프로그램을 수행하기 위해 OS로 부터 할당받는 메모리 영역


Runtime Data Areas의 5개의 영역

PC Registers 
Method Area
Heap
Native Method Stacks
Java Virtual Machine Stack


※ Method Area
 Method Area는 모든 Thread들이 공유하는 메모리 영역이다. 이 영역은 Load된 Class나 Interface를 저장하는 논리적인 공간이다.
 저장되는 정보는 클래스나 인터페이스의 바이트코드 뿐만 아니라, 모든 변수, 상수, referenc, Method Data 등이 포함된다. 또한 클래스나 인스턴스, 인터페이스의 초기화때 사용되는 Class Variable과 Method와 생성자의 정보도 포함된다. 이 정보들은 ClassLoader에게 넘겨받은 Class File에서 Type관련 정보를 추출하여 저장하게 된다.
 Method Area는 JVM이 기동할 때 생성이 되며 Garbage Collection의 대상이 된다. 이 Method Area 역시 벤더마다 다르게 구현되어 있으며, Hotspot JVM의 경우는 Permanent Area라는 명칭으로 특정 메모리 영역으로 구분되어 있다.

Type == class, interface

- Method Area의 Type정보는
 Type Information, Constant Pool, Field Information, Method Information, Class Variables, Reference to class ClassLoader, Reference to class Class
의 7가지 정보로 구성됨

Type Information : 가장 기본이 되는 정보로 Type에 대한 전반적인 내용이 포함된다.
- Package.class의 형태를 지니는 Type의 전체이름(Full Qualified Name)
- Type의 직계 super class의 전체 이름, Type이 Interface이거나, java.lang.object class이거나 super class가 없는 경우는 제외됨
- Type이 Class 인지 Interface인지의 여부
- public, abstrace, final 등 type의 Modifier
- Interface의 경우 직접 Link 되고 있는 객체의 리스트로 객체는 전체 이름으로 표현됨

Constant Pool : Class나 Interface의 모든 Constant 정보를 가지고 있는 부분이며, Constant라 함은 Literal Constant는 물론이고, Type, Field(Member Variable, Class Variable), Method로의 모든 Symbolic Reference까지 확장한 개념으로 받아들여야 한다.
 Symbolic Reference가 저장되는 공간이 Constant Pool이다.

Class Variable :  클래스 멤버는 static으로 선언된 변수이며, Class에서는 하나의 값으로 유지가 된다. 이 변수는 모든 Instance에 공유되기 때문에 이를 이용하는데 있어 동기화 이슈가 발생한다. 이 변수는 Class와 관련이 있기 때문에 Instance가 없어도 접근이 가능하다. Class Data의 논리적 부분으로 기능을 하는 Class Variable은 Class를 사용하기 전부터 Method Area에 미리 메모리를 할당 받는다.
 Class Variable을 final로 선언할 경우 상수로 취급하기 때문에 Constant Pool에 Literal Constant로 저장된다.

Field Information
Field의 이름
Field의 데이터 타입, 선언된 순서
public, private, protected, static, final, volatile, transient와 같은 Field의 Modifier

Method Information
Type(Class, Interface)에서 선언된 모든 Method의 정보를 의미한다.
 Method의 이름
 Method의 반환값의 Data Type 또는 void
 Method Parameter의 수와 Data Tpye, 선언된 순서
 Method Modifier
만약 Method가 native나 abstract 가 아니라면 다음의 정보가 추가된다.
 Method의 Bytecode
 Method Stack Frame의 Operand Stack 및 Local Variable Section의 크기
 Exception Table

Reference to class ClassLoader
Type이 JVM에 Load 될 때 항상 이 Type은 어떤 ClassLoader를 경유하여 Loading 되었는지를 추적하게 된다. Type이 다른 Type을 참조할 경우 ClassLoader를 사용하도록 되어 있기 때문이다.

Reference to class Class
 Type이 JVM에 Load되면 항상 java.lang.class class 의 Instance가 하나 생성된다. 그래서 Method Area에는 Type 정보의 일부로 이 Instance의 Reference를 저장하고 있다. getName()을 통해 Class의 이름을 알아오거나 isIntetface()를 통해 interface인지의 여부등을 알수 있는것도 class Instance의 Reference를 통하기 때문임

※ Method Table
Method Area에서 원하는 정보를 찾는 속도는 성능의 중요한 이슈가 될 수도 있다. 이를 위해 JVM을 설계할 때 Method Table이라는 데이터 구조를 사용하기도 한다. Method Table이 Class의 하위 자료이기 때문에 Class가 Loading 될 때 함께 생성된다.



■ Heap
Java Heap은 Instance( 또는 Object ) 와 Array 객체 두 가지 종류만 저장되는 공간이다.
모든 Thread들에 의해 공유되며, 같은 Application을 사용하는 Thread 사이에서는 공유된 Heap Data를 이용할 때 동기화 이슈가 발생할 수 있다. 
 JVM은 Java Heap에 Memory를 할당하는 Instruction(Bytecode로 new, newarray, anewarray, multianewarray) 만 존재하고 메모리 해제를 위한 어떤 Java Code나 Byte Code도 존재하지 않는다. Heap의 메모리 해제는 오로지 Garbage Collection 을 통해서만 수행된다.

Hotspot JVM의 Object Layout

첫번째 헤더는 Mark Word라고 불리며 Garbage Collector와 Synchronization 작업을 위해 사용한다. 위와 같은 레이아웃을 가지고 있다.

 Mark Word는 Hash Code 또는 Thread ID, Age, Biased Bit, Tag Flag로 구성되어 있다.
하위 3bits는 Biased Lock의 사용여부와 Tag Flag를 나타낸다. Biased Bit가 1이면 Biased Lock을 사용한다는 것으로 Synchronization 작업을 수행할 때 Lock을 획득하기 위한 연산을 수행핮 않고 가볍게 Lock을 획득함을 의미한다. Biased Lock을 사용하게 되면 Biased Lock Mode로 Synchronization을 수행하는지에 대한 상태값만이 존재한다. 그 밖에 Biased Lock을 사용하지 않으면 Biased Bit는 0이 되고 Tag 별로 4가지 상태를 나타낸다.

 Hashcode 또는 Thread ID는 23Bits 를 할당받고 있으며 Biased Bit와 Tag에 따라 값이 결정된다. Based Bit가 1이면 Biased Lock을 획득한 Thread의 ID가 기록된다.
Biased Bit가 0이고, 이전 Synchronization 작업 때 산출된 Hash Code가 있다면 Tag에 따라 각각 다른 값이 저장된다.

 Light-Weight Locked 상태에서는 Lock Record Address, Heavy-Weight Locked 상태에서는 Monitor Address 등의 Lock과 관련한 Pointer 정보, 그리고 Marked fo GC에서는 Forwarding Address의 정보를 Hash Code로 가지고 있게 된다. Age 정보는 6Bits로 Young Generation의 Object가 Eden과 Survivor를 넘나든 횟수를 기록한다.

두번째 Header에는 Method Area의 Class 정보를 가리키는 Reference 정보가 저장된다. 이 두개의 Header는 Object와 Array 모두 동일하다. 그러나 Array의 경우는 Array Size를 위한 Header가 하나더 추가된다.

 Data 영역에는 Object나 Array에 따라 적절한 구조와 크기를 가진다

Runtime Data Areas - PC Registers, Stack

■ 정의 : Runtime Data Areas는 Process로서의 JVM이 프로그램을 수행하기 위해 OS로 부터 할당받는 메모리 영역


Runtime Data Areas의 5개의 영역

PC Registers 
Method Area
Heap
Native Method Stacks
Java Virtual Machine Stack




  •  PC Registers


- 프로그램의 실행은 CPU에서 명령어, 즉 인스트럭션(Instruction)을 수행하는 과정으로 이루어진다. CPU는 이러한 Instruction을 수행하는 동안 필요한 정보를 레지스터(Register) 라고 하는 CPU내의 기억장치를 사용한다.
 1 + 2    1 과 2 처럼 명령실행에 사용되는 데이터를 Operand 라고 한다. 그리고 결과인 3 이라는 Operand 도 메모리로 전달하기 전에 CPU어딘가에 잠시 머무른다. 이 공간이 레지스터.

 - Runtime Data Area의 메모리 영역인 PC Register는 이것과는 다르다. Java는 Register-Base로 구동되는 방식이 아니라, Stack-Base방식으로 작동한다. JVM은 CPU에 직접 Instruction을 수행하지 않고 Stack에서 Operand를 뽑아내어 이를 별도의 메모리 공간에 저장하는 방식을 취하고 있다. 이러한 메모리 공간을 PC Registers 라고 한다.

- Java는 플랫폼에 있어서는 독립적이다(CPU아키텍처 같은 Instruction Set) 하지만 JVM 도 OS나 CPU의 입장에서 보면 머신에서 동작하는 하나의 프로세스에 지나지 않기 때문에 머신의 리소스를 사용해야 하는 것도 너무도 당연, 그렇기 때문에 Java도 현재 작업하는 내용을 CPU에 Instruction으로 제공해야 하며, 이를 위한 버퍼공간으로서 PC Registers 라는 메모리 영역을 생성한것

- PC Registers는 각 Thread마다 하나씩 존재하며 Thread가 시작할 때 생성된다. 만약 Thread가 Java Method를 수행하고 있으면 이 PC Registers에는 현재 수행중인 Java Virtual Machine Instruction의 주소를 가지고 있게 된다.


  •  Java Virtual Machine Stack


- Java Virtual Machine Stack 은 Thread의 수행정보를 기록하는 Frame을 저장하는 메모리 영역이다.

- Thread마다 하나씩 존재하며 Thread가 시작할 때 생성된다. Java Virtual Machine Stack에 있는 모든 데이터는 각 Thread가 소유하며, 다른 Thread는 접근이 불가능하다. 그렇기 때문에 Java Virtual Machine Stacks에서는 동기화에 대한 이슈가 발생하지 않는다.
대표적인 예로 로컬변수(Local Variable)를 들 수 있다.

- Java Virtual Machine Stack은 Stack Frame들로 구성이 되는데 JVM은 Stack Frame을 Java Virtual Machine Stacks에 넣고(Push) 빼는(Pop or Pull) 작업만 수행한다.
 Stack Trace, Stack Dump를 얻어내어 분석을 하는것도 이 곳이다.

- 여러 Stack Frame중 현재 수행하고 있는 Method 의 정보를 저장하는 것을 Current Frame이라고 한다. 그리고 현재 수행하고 있는 Method의 Class를 Current Class라고 한다.
JVM이 현재 Method를 수행하고 있다면 Current Frame을 통해 Current Class와 같은 Current 정보를 계속해서 주시하게 된다. 이 Stack Frame에는 Method의 파라메터 변수, 지역변수, 연산의 결과 등과 같은 데이터들을 저장한다.

- Thread가 Java Method를 하나 수행하게 되면, JVM은 Stack Frame을 하나 생성하여 Java Virtual Machine Stacks에 Push한다. 그렇게 새로 들어간 Stack Frame은 Current Frame이 된다. 만약 Method가 비정상 종료하게 되면 Exception 처리를 하는 작업을 수행한 후 Java Virtual Machine Stacks에서 사라지게 된다.

 Stack Frame
Stack Frame은 Thread가 수행하고 있는 Application을 Method 단위로 기록하는 곳,
Local Variable Section, Operand Stack, Frame Data의 세부분으로 구성,
Stack Frame은 Method를 실행하게 되면 Class의 메타정보를 이용하여 적절한 크기로 생성되는데, 그 크기는 Compile Time에 결정된다.

  - LocalVariable Section
Method의 Paramether Variable과 Local Variable 들을 저장한다. 0부터 시작하는 인덱스를 가진 Array들로 구성되어 있고, 이 Array의 인덱스를 통해 데이터에 접근,
 Method 파라메터는 선언된 순서로 인덱스가 할당되며, 로컬변수는 Compiler가 알아서 인덱스를 할당한다. 선언만 해놓고 사용하지 않는 로컬변수는 인덱스를 할당하지 않을 수도 있다. 인덱스 할당은 Local Variable Section에 저장공간을 마련한다는 것을 의미한다.
 원시타입(primitive Type) 변수와 객체가 들어갈때 Local Variable Section에 저장되는 값은 다르다.
 원시타입은 값 그대로 Local Variable Section에 들어가지만, 객체는 Reference형태로 참조가 된다. Java에서 모든 객체가 저장되는 곳은 바로 Heap이라는 메모리 영역이다.
객체정보는 Java Virtual Machine Stack 에 직접 저장되는 것이 아니라 해당 객체가 존재하고 있는 Heap의 위치를 말해주는 Reference를 저장한다. 그리고 그 Reference를 통해 실제 객체가 저장되어 있는 Heap을 찾아가는 것이다.
 0번 인덱스의 Reference는 Heap에 있는 Class의 Instance데이터 참조를 위한것, Local Method가 아닌 Class Method, static으로 선언한 Method의 경우 Reference정보가 존재하지 않는다. 이것은 Class Variable과 마찬가지로 Instance에 속한것이 아니라, Class 자체에 속해있는 것이다.

 - Operand Stack
JVM의 작업공간, JVM이 프로그램을 수행하면서 연산을 위해 사용되는 데이터 및 그 결과를 Operand Stack에 집어넣고 처리를 한다.

class JvmInternal{
 public void operandStack(){
 int a, b , c;
 a = 5;
 b = 6;
 c = a + b;
}
}

위와 같은 Java 파일을 컴파일 한뒤 'javap -c' 를 사용하여 Method의 Bytecode를 추출한 결과는 아래와 같다

Compiled from "JvmInternal.java"
class JvmInternal {
  JvmInternal();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void operandStack();
    Code:
       0: iconst_5
       1: istore_1
       2: bipush        6
       4: istore_2
       5: iload_1
       6: iload_2
       7: iadd
       8: istore_3
       9: return
}

iconst_5는 상수5를 push 하라는 것을 의미하는 Bytecode이다.
istore_1은 Local Variable Section의 1번 인덱스에 값을 저장하는 것을 의미한다.


istore_1istore_2iload_1iload_2iaddistore_3
Operand Stack6
5511
Localint c311
Variableint b266666
Sectionint a1555555
hidden thisref0refrefrefrefrefref

Operand Stack Local Variable Section에서의 연산과정


-Frame Data
Stack Frame을 구성하고 있는 또 하나의 영역, 이곳에는 Constant Pool Resolution 정보와 Method 가 정상 종료했을 때의 정보들, 그리고 비정상 정료를 했을 시에 발생하는 Exception 관련 정보들을 저장하고 있다.
 Resolution => Java는 모든 참조정보를 Symbolic Reference로 가지고 있는데, 이 Symbolic Reference는 JVM에서 실제로 접근 할 수 있는 실질적인 Direct Reference로 변경이 되는데 이러한 것을 Resolution이라고 힌다.
 Class의 모든 Symbolic Reference는 Method Area의 Constant Pool이라는 곳에 저장되어 있기 때문에 Resolution을 Constant Pool Resolution이라고 부르는 것이다.

 Frame Data에 저장된 Constant Pool Resolution 정보는 , 관련 Constant Pool의 Pointer 정보이다. JVM은 이 Pointer를 이용하여 필요할 때 마다 Constant Pool을 찾아간다.

보통 상수를 가져올때 Constant Pool의 Entry 정보를 참조하기도 하지만 다른 Class를 참조하거나, Method를 수행하거나 아니면 특정 변수들을 접근할 때에도 Constant Pool을 참조해야 한다. Java의 모든 Reference는 Symbolic Reference이기 때문에 Class나 Method, 그리고 변수나 상수에 접근할 때에도 이러한 Resolution이 수행된다. 또한 특정 Object가 특정 Class나 Interface에 의존관계가 있는지 확인하기 위해서도 Constant Pool의 Entry를 참조한다.

 Frame Data에는 자신을 호출한 Stack Frame의 Instruction Pointer가 들어가 있다. Method가 종료되면 JVM은 이 정보를 PC Register에 설정하고 Stack Frame을 빠져 나간다. 만약 이 Method가 반환값이 있다면 이 반환 값을 다음 번 Current Frame, 즉 자신을 호출한 Method의 Stack Frame의 Operand Stack에 Push 하는 작업도 병행한다. 또한 Exception Handling 정보 또한 Frame Data영역에 저장하고 있다.

2017년 6월 13일 화요일

제네릭스( Generics )

Generics ?
제네릭스, 에너테이션, 열거형

제네릭스는 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시의 타입체크(compile-time type check)를 해주는 기능이다. 객체의 타입을 컴파일시에 체크하기 때문에 객체의 타입 안정성을 높이고 형변환의 번거로움이 줄어든다.

■ 제네릭스의 장점
1. 타입 안정성을 제공한다.
2. 타입체크와 형변환을 생략할 수 있으므로 코드가 간결해 진다.

 - 간단히 말하면 다룰 객체의 타입을 미리 명시해줌으로써 번거로운 형변환을 줄여준다는 이야기다

◇ 제네릭 클래스의 선언
제네릭 타입은 클래스와 메서드에 선언할 수 있다.  [ 제네릭 클래스 ]
45 lines (35 sloc)  1.45 KB
package common.fundament.generics;
/*
class Box {
Object item;
void setItem(Object item){this.item=item;}
Object getItem(){return item;}
}
* 위의 클래스를 제네릭 클래스로 변경하면 아래와 같이 클래스 옆에 '<T>'를 붙이면 된다.
* 그리고 'Object' 를 모두 'T'로 바꾼다.
* */
class Box<T>{ // 제네릭 타입 T를 선언
T item; // T를 '타입변수(Type variable)' 라고 하며, 'Type'의 첫 글자에서 따온것이다.
void setItem(T item){
this.item=item;
}
T getItem(){ return item;}
}
// T는 임의의 참조형 타입을 의미한다.
// 제네릭 클래스가 된 Box클래스의 객체를 생성할 때는 다음과 같이 참조변수와 생성자에 타입 T대신에
// 사용될 실제 타입을 지정해주어야 한다.
// Box<String> b = new Box<String>(); //타입 T 대신, 실제 타입을 지정
// b.setItem(new Object()); //에러. String 이외의 타입은 지정불가
// b.setItem("ABC"); //OK. String 타입이므로 가능
// String item = (String) b.getItem();//형변환 필요없음
// 위의 코드에서 타입 T 대신에 String타입을 지정해 주었으므로, 제네릭 클래스 Box<T>는 다음과 같이
// 정의된 것과 같다
class Box2<String> { // 제네릭 타입을 String으로 지정
String item;
void setItem(String item){
this.item=item;
}
String getItem(){
return item;
}
}

만일 Box클래스에 String만 담을거라면, 타입 변수를 선언하지 않고 위와 같이 직접 타입을 적어주는 것도 가능하다. 단, Box<String> 클래스는 String타입만 담을 수 있다. 반면에 Box<T>클래스는 어떤 타입이든 한 가지 타입을 정해서 담을 수 있다.

제네릭이 도입되기 이전의 코드와 호환을 위해, 제네릭 클래스인데도 예전의 방식으로 객체를 생성하는 것이 허용된다. 다만 제네릭 타입을 지정하지 않아서 안전하지 않다는 경고가 발생한다.

Box b = new Box();            // OK. T는 Objcet로 간주된다.
b.setItem("ABC");              // 경고, unchecked of unsafe operation
b.setItem(new Object());   // 경고, unchecked of unsafe operation


■ 제네릭스의 용어
 class Box<T> { }
Box<T>  제네릭 클래스, 'T의 Box' 또는 'T Box' 라고 읽는다.
T           타입변수, 또는 타입 매개변수. (T는 타입 문자)
Box        원시 타입 ( raw type )

타입문자 T는 제네릭 클래스 Box<T>의 타입변수 또는 타입 매개변수라고 하는데, 메서드의 매개변수와 유사한 면이 있기 때문이다. 

             대입된 타입(매개변수화된 타입, parameterized type)
Box<String> b = new Box<String>();
제네릭 타입 호출

위와 같이 타입 매개변수에 타입을 지정하는 것을 '제네릭 타입 호출' 이라고 하고, 지정된 타입 'String'을 '매개변수화 된 타입(parameterized type)' 이라고 한다. ( 대입된 타입 )
예를 들어, Box<String> 과 Box<Integer>는 제네릭 클래스 Box<T>에 서로 다른 타입을 대입하여 호출한것, 이 둘이 별개의 클래스를 의미하는 것은 아니다.

■ 제네릭스의 제한
 모든 객체에 대해 동일하게 동작해야 하는 static 멤버에 타입 변수 T를 사용 할 수 없다.
T는 인스턴스 변수로 간주되기 때문이다.
 static멤버는 인스턴스 변수를 참조할 수 없다.

class Box<T>{
      static T item; //에러
      static int compare(T t1, T t2){ } // 에러

static멤버는 타입 변수에 지정된 타입, 즉 대입된 타입의 종류에 관계없이 동일한 것이어야 하기 때문이다. 즉, 'Box<Apple>.item'과 'Box<Grape>.item'이 다른 것이어서는 안된다는 뜻이다.

 제네릭 타입의 배열을 생성하는것도 허용되지 않는다.
제네릭 배열 타입의 참조변수를 선언하는 것은 가능하지만, 'new T[10]' 과 같이 배열을 생성하는 것은 안된다는 뜻이다.
class Box<T>{
    T[] itemArr; //OK, T타입의 배열을 위한 참조변수
    T[] toArray() {
              T[] tmpArr = new T[itemArr.length];   // 에러, 제네릭 배열 생성불가
              return tmpArr;

제네릭 배열을 생성할 수 없는 것은 new 연산자 때문인데, 이 연산자는 컴파일 시점에 타입 T가 뭔지 정확히 알아야 한다. Box<T> 클래스를 컴파일 하는 시점에서는 T가 어떤 타입이 될지 전혀 알 수 없다.

JavaScript_함수의 역할

◎ 자바스크립트 함수의 역할 ■ 자바스크립트 함수와 메서드는 전혀 다른 개념이다. - 자바스크립트 함수는 메서드 역할을 할 수 있지만, 객체지향 프로그래밍의 메서드는 자바스크립트의 함수 역할을 할 수 없다. - 메서드와는 다르게 ...