아놔. 요즘 배그에 빠져서는 블로그는 나몰라라 하고 있다 ㅠㅠ

매번 쓸때마다 오랜만이긴 한데.

역시나 오랜만이니까 기초지식 다뤄보겠다.



이번에는 자바의 클래스 패스와 그것을 시스템 환경변수에 설정하는 목적에 대해 포스팅 해보겠다.


클래스 패스(Class Path)란?

자바 가상머신이 프로그램을 실행할 때, 클래스 파일을 찾는 데에 기준이 되는 파일 경로라고 할 수 있다.
즉, 자바 가상머신이 클래스 파일을 찾는 경로다.

물론, 이 클래스 패스는 개발자인 너, 나, 우리가 지정해준다.
지정해주지 않으면 자바 가상머신은 현재 디렉토리에서 필요한 클래스들을 가져와서 쓴다.

현재 디렉토리는 현재 명령 프롬프트가 가리키고 있는, 위치하고 있는 그 경로를 말한다.

>> java 파일명.class
이러한 명령어를 명령 프롬프트 창에다가 입력해서 실행시킬텐데, 이 상황에서의 디렉토리를 말한다.

다시 돌아와서.

A 클래스에서 B 클래스를 사용하고 있다고 하자. 
그리고 A, B 클래스가 같은 폴더에 있다고 하자.

이럴 땐 그냥 두 클래스가 있는 디렉토리로 가서

 >> java A.class

이렇게 해주면 아주 잘 돌아간다.

그치만 만약, B 클래스가 'bbb'라는 폴더 안에 있다면 약간의 문제가 발생한다.
위와 같이 실행하면, 중간에 오류를 뱉어낸다.
A 클래스에서 B 클래스를 사용하고 있는데, 해당 디렉토리에는 B 클래스가 없기 때문에 그러하다.

그래서 우리는 다른 디렉토리에 있는 B 클래스를 사용하기 위해, 
자바 가상머신이 클래스를 탐색하는 경로에 B클래스가 들어 있는 경로를 지정해주면 된다.




클래스 패스 지정하기

클래스 패스는 우리가 직접 지정해줄 수가 있다. 

>> set classpath=경로

이렇게 지정해주면 된다. 참 쉽다.

경로는 전체 경로가 싹 다 들어가는데, 예를 들면 'C:\workspace\bbb' 이런게 들어갈 수 있겠다.
C드라이브 밑에, workspace폴더 밑에, bbb라는 디렉토리를  클래스 패스로 지정하겠단 뜻이다.

근데 방금 말했듯이, 저렇게 명령어를 넣게 되면, 
저렇게 클래스 패스를 지정하게 되고, 그러면 자바 가상머신은 실행할 때, 저 경로만 탐색하게 된다.
즉, 클래스 패스를 추가하는게 아니라, 말 그대로 다시 설정해주는 명령어다.

A클래스가 있는 workspace 디렉토리와, B클래스가 있는 workspace/bbb 디렉토리를 둘 다 지정해주려면,  
경로 부분에 두 개의 경로를 모두 지정해주면 된다.
두 경로를 구분해주는 것은 세미콜론(;) 되시겠다.

>> set classpath=경로1;경로2
Ex)
>> set classpath=C:\workspace;C:\workspace\bbb

근데, 디렉토리 세계에서 마침표(.)는 현재 디렉토리를 의미한다.
그래서 내가 지금 명령 프롬프트 상에서 위치한 디렉토리를 클래스 패스로 지정하고싶다 하면

>> set classpath=.

일케 입력해주면 된다.


이렇게 입력해주는 클래스 패스는 언제 어디서나, 평생펑생 유효할까?

그렇지 않다.

이것을 지정해준 명령 프롬프트 창 내에서만 유효하다.
그래서 새로운 명령 프롬프트 창을 띄우면, 또 지정해줘야 한다.


이거 너무 불편하니까, 미리 클래스패스를 고정해놓곤 하는데
우리 윈도우 유저분들. 징그럽게 많이 해서 이제는 눈 감고도 하는 바로 그거.

환경변수 셋팅. 두둥-.

바로 여기에서 클래스 패스를 지정해놓으면, 알아서 자바 가상머신은 그 경로에서 클래스 파일을 탐색하게 된다.
새로운 명령 프롬프트를 띄우더라도.



클래스 패스 고정하기


나의 컴은 맥북이라 이걸 캡쳐할 수 없는 안타까움이 있지만. 대충 말로 떼워보자면
윈도우에서 '새 사용자 변수' 창을 띄워서 환경 변수를 등록하는게 있는데
여기서 변수 이름에 'classpath'를 넣고, 변수 값에 경로를 주면 된다.

자바 처음 배울 때
아무것도 모르는데 일단 JDK를 설치하고, 환경변수를 설정하라고 하는데...
하라는대로 하긴 하겠는데... 
이랬었던 기억이 난다. 새록새록.


경로 집어 넣을 때, 

C:\Program Files\java\jdk0.0.0\bin;

이렇게 집어 넣었었는데, 꼭 마지막에 세미콜론(;) 빼먹거나, 'bin' 빼먹고서는 안된다고 하는 애들 있었다.

'bin'은 자바 소스(.java 파일)가 컴파일 되어 새롭게 생성되는 .class 파일이 모여있는 곳이다.
실제로 자바 가상머신이 실행할 수 있는 형태의 파일은 .java 파일이 아니라, .class 파일이고,
그것들이 모여있는 위치가 'bin'폴더이기 때문에 꼭 여기까지 써주어야 한다는 사실을 기억하자.





-----


사실 패키지에 대한 것까지 오늘 쓸라그랬는데..
넘나 졸린 것 ㅠㅠ 
낼 출근 똥망. 


  1. ㅁㄴㅇㄹ 2018.12.18 09:53

    감사합니다 이해잘되는거같아요 잘봣습니다

  2. 초보개발자 2020.12.29 12:40

    감사합니다~ Programming/Java 관련 글을 다봤습니다! 매일 코딩은 하지만정확히 어떻게 구동되는지 기본적인 지식이 없었는데 간단하게 설명해주셔서 잘 보고갑니다!

자바 메모리 구조


이전 포스트에서 자바의 메모리 구조는 크게 메소드 영역, 힙 영역, 스택 영역으로 나뉘어 있다는 것과, 메소드 영역이 뭔지 살짝 알아봤다.


이번에는 힙 영역과 스택 영역은 뭔지, 각각 어떤 역할을 하는지,


좀 헷갈리니까 친절히 코드로 비교도 해볼까 한다.



2. 스택 영역(Stack Area)

스택 영역에는 지역변수와 매개변수가 저장된다.


그렇담 지역변수는 뭐고, 매개변수는 뭔가여.

한글로 접하니까 오히려 헷갈리는데 지역변수 = 로컬변수(local variable), 매개변수 = 파라미터(parameter)라고 들으면 아~~ 뭐야. 할 것임.

이래도 모르겠으면 로컬변수는 메소드 내에서 선언된 변수들, 매개변수는 메소드에 아규먼트로 넘겨주는 값들로 이해하고 넘어가자.


로컬변수와 매개변수의 특징은 이 아이들이 선언된 그 블록 안에서만 유효한 변수들이라는 점이다.

이런 친구들이 스택 영역에 저장되는 것임.


쉽게 말해 스택 영역에는 프로그램의 실행 과정에서 '임시로 할당'되고, 그게 끝나면 바로 소멸되는 것들이 저장된다.

 즉 => 메소드가 호출될 때마다 그 메소드의 로컬 변수를 준비하고, 메소드 호출이 끝나면 그 메소드를 위해 준비했던 모든 변수가 스택에서 제거된다.



 

<참고> 

   밑에서 설명하겠지만, 빠른 이해를 위해 덧붙이자면 

참조변수에 저장되는 메모리주소는 스택영역에 저장되지만, 그 주소가 가리키는 메모리는 모두 힙 영역에 저장된다.




코드로 보자.


<스택 영역>

public class StackMemoryTest {
  public static void m1(int a) {
    m2(++a);
    System.out.printf("m1():%d\n", a);
  }
  
  public static void m2(int a) {
    m3(++a);
    System.out.printf("m2():%d\n", a);
  }
  
  public static void m3(int a) {
    ++a;
    System.out.printf("m3():%d\n", a);
  }
  
  public static void main(String[] args) {
    int a = 20;
    m1(a);
    System.out.printf("main():%d\n", a);
  }
}


결과:


m3(): 23

m2(): 22

m1(): 21

main(): 20 



여기서 이런 결과가 나오는 이유는, 먼저 들어간 것이 나중에 나오는 스택 구조의 특성 때문이다.


위 코드의 흐름을 보기 좋게 표현하면 대략 이렇다.


 실행 코드

스택 영역 메모리

 출력


main() {

int a = 20; 

m1(a);

...

}


 

 main() : a = 20


 


m1(a) { m2(++a);

... }


/* a 자리에 a + 1이 놓임 */



 m1() : a = 21

 main() : a = 20

 

 

m2 (a) {

m3(++a);

...

}

 

 m2() : a = 22

 m1() : a = 21

 main() : a = 20

 

 

m3 (a) {

++a;

...

}


 m3() : a = 23

 m2() : a = 22

 m1() : a = 21

 main()  : a = 20

 



m3 (a) {

...

  System.out.printf("m3():%d\n", a);

}

 

 m3() : a = 23

 m2() : a = 22

 m1() : a = 21

 main()  : a = 20

 

m3(): 23

  m2(a) { 

...

 System.out.printf("m2():%d\n", a); 

 }

 

 m2() : a = 22

 m1() : a = 21

 main()  : a = 20


m3(): 23

m2(): 22

 m1(a) { 

...

 System.out.printf("m1():%d\n", a); 

 }


 m1() : a = 21

 main()  : a = 20

m3(): 23

m2(): 22

 m1(): 21 

 main() {

...

System.out.printf("main():%d\n", a);

}

 

 main()  : a = 20

m3(): 23

m2(): 22

 m1(): 21 

main(): 20



이렇게 지역변수의 값은 해당 메소드가 실행되면, 그 메소드의 스택번지에 값이 저장된다.

그리고 해당 메소드가 모두 실행되고나면, 스택에서 해당 메소드의 스택번지가 제거되는 구조이다.


아래의 힙 영역과 어떻게 다른지도 밑에서 설명하도록 하겠다.



.



3. 힙 영역(Heap Area)

힙 영역에는 흔히 코드에서 'new'명령을 통해 생성된 인스턴스 변수가 놓인다.


어떤 메소드인지는 상관이 없다.

걍 new 명령으로 만드는 메모리는 모조리 힙 영역에 보관된다고 보면 된다.


스택영역에 저장되는 로컬변수, 매게변수와 달리, 힙 영역에 보관되는 메모리는 메소드 호출이 끝나도 사라지지 않고 유지된다.


언제까지??


주소를 잃어버려 가비지가 되어 가비지 컬렉터에 의해서 지워질 때까지. 

아니면 JVM이 종료될때까지.


역시 코드로 살펴보겠다.


<힙 영역>

public class HeapMemoryTest {
 
  public static int[] m1(int a) {
    int[] arr = m2(a + 1);
    arr[2] = a;
    return arr;
  }
  
  public static int[] m2(int a) {
    int[] arr = m3(a + 1);
    arr[1] = a;
    return arr;
  }
  
  public static int[] m3(int a) {
    int[] arr = new int[3];
    arr[0] = a;
    return arr;
  }
  
  
  public static void main(String[] args) {
    int[] arr = m1(100);
    for (int i = 0; i < arr.length; i++) {
      System.out.printf("%d=%d\n", i, arr[i]);
    }
  }
}


결과:

0=102

1=101

2=100








 실행 코드

스택 영역 메모리

 힙 영역 메모리

 출력

 main() {
...
}

  

 main()


  


main() {

int[] arr = m1(100);

...

}



 m1() : a = 100

main()

 

 


m1(a) { int[] arr = m2(a + 1); ... }



 m2() : a = 101

 m1() : a = 100

main()

 

  

m2(a) { int[] arr = m3(a + 1); ... }


 m3() : a = 102

 m2() : a = 101

 m1() : a = 100

main()

  

  

m3(a) { int[] arr = new int[3]; arr[0] = a; return arr; }


 m3() : a = 102,

arr = 7000번지

 m2() : a = 101

 m1() : a = 100

main()

 

 <7000번지>

 

  



 m3(a) {

int[] arr = new int[3]; arr[0] = a; return arr; }

 

 m3() : a = 102,

arr = 7000번지

 m2() : a = 101

 m1() : a = 100

main()

 

 <7000번지>

102

  

 
 

  

m3(a) { 

int[] arr = new int[3]; arr[0] = a; return arr; }

m2(a) {
    int[] arr = m3(a + 1); arr[1] = a; return arr;

}



 m2() : a = 101,

arr = 7000번지

 m1() : a = 100

main()

 

 <7000번지>

102

  

  
 

  

m2(a) { 
    int[] arr = m3(a + 1);  arr[1] = a; return arr;

}

 

 m2() : a = 101,

arr = 7000번지

 m1() : a = 100

main()

 

 <7000번지>

102

 101

 

   
 

  

m2(a) { 
    int[] arr = m3(a + 1);  arr[1] = a; return arr;

}

m1(a) { int[] arr = m2(a + 1); arr[2] = a; return arr; }

 
  

 m1() : a = 100,

arr = 7000번지

main()

 

 <7000번지>

102

 101

 

    
 

m1(a) { int[] arr = m2(a + 1); arr[2] = a; return arr; } 

 

 m1() : a = 100,

arr = 7000번지

main()

 
  <7000번지>

102

 101

 100

    
 

  

m1(a) { int[] arr = m2(a + 1); arr[2] = a; return arr; } 

main() {

int[] arr = m1(100);

...

}

  
 

main() : arr = 7000번지

 
   <7000번지>

102

 101

 100

    
 


main() {

...

for (int i = 0; i < arr.length; i++) {

System.out.printf("%d=%d\n", i, arr[i]); }

}


main() : arr = 7000번지

 

   <7000번지>

102

 101

 100

     

0=102

1=101

2=100



이미 눈치가 빠른 사람은 알아차렸겠지만

이렇게 인스턴스를 별도의 힙 영역에 할당하는 이유


인스턴스의 소멸방법과 소멸시점이 지역변수(스택영역에 할당되는 애들)와는 다르기 때문이다.


(이전 포스트에서 굳이 메모리 영역을 몇 개로 나눠서 관리하는 이유가, 일상생활에서도 용도에 따라 물건을 다른 장소에 관리하는게 편한것과 같은 맥락이라고 설명했다.)





 

<참고2> 


이미 알고 있으리라 믿지만.


8가지 원시타입(byte, short, int, long, float, double, char, boolean)을 제외한 그외의 타입으로 정의된 변수들은 

모조리 레퍼런스 변수, 즉 참조변수이다.


이런 참조변수들은 실행될 때마다 많은 데이터들을 스택메모리 영역에 뒀다 뺐다 하는게 매우 비효율적이므로,

힙 영역에 그 내용(진짜 값)이 저장되고, 스택 메모리에는 간단하게 그 주소만 저장이 되는 것이다.


그리고 힙 영역에는 실제 그 변수가 가리키고 있는 값들이 저장되어 있다.





정리

스택 영역의 지역변수들은 메소드가 호출되고 끝이 나는 프로그램의 실행 흐름에 관여하는 것들이고

힙 영역의 인스턴스들은지역변수가 참조하고 있는 실제의 값들을 가지고 있다.






---


  1. dijeodo 2017.06.02 17:09

    애매하게 알고 있던 부분을 확실히 잡고가네요 감사합니다 ^^

  2. ANAHEIM 2018.03.08 14:37 신고

    가끔 헷갈릴 때 마다 계속 와서 보고 있습니다
    감사합니다

  3. 다슬기 2018.08.27 11:10

    자세한 설명 감사합니다.

  4. hihi 2018.10.19 17:29

    자세한 설명 감사합니다
    힙 영역 표 8번째 줄에 오타있네요(arr[2] -> arr[1])

    • WANZ 2018.11.16 19:37 신고

      앗 그러네요 ! 수정하겠습니다 ㅎㅎ 감사합니다

  5. ㅇㅇ 2018.11.16 18:21

    아래글이랑 이 글이 초보자인 제가 가장 이해하기 쉬운 메모리 구조 글이었습니다 :) 구글검색 파도타다가 계속 이해안되서 이것저것 찾아봤는데 이글 한방에 해결됬네요. 감사합니다 :)

  6. ㅇㅇ 2018.12.09 16:03

    잘보고가여^

  7. asdfas 2019.01.03 10:32

    heap 영역에서 static 영역값을 참고할때는 heap영역에도 주소값이 들어갈 수 있나요?

    • WANZ 2019.02.13 11:37 신고

      음. 우선 static영역(메소드 영역)은 논리적으로는 힙 영역의 일부로 보고 있습니다.(https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html 참고)
      즉, 이 질문은 "힙 영역 값을 참조할 때는 힙 영역에도 주소 값이 들어갈 수 있나요?" 와 같은 질문으로 바꾸어볼 수 있는데요.
      역으로 어떤 경우에 힙 영역에 주소 값이 저장되어야 하는건지 묻고 싶습니다. 모든 프로그램의 실행은 스택영역에서 일어난다고 해도 무방한데, 스택영역에서는 진짜 값에 접근하기 위해 주소 값을 가지고 있습니다. 그리고 이 값을 다른 힙 영역에서 사용하여야 한다면, 그때 그때 주소값을 통해 진짜 값에 접근해서 가져옵니다.
      즉, 힙 영역에 주소 값이 들어갈 필요가 없습니다.
      가비지 컬렉터가 있는 한 그럴 필요도 없고, 그게 된다고 해도... 에러를 유발하지 않으면 다행이 아닐까 싶네요.

    • 차람쥐코드 2019.11.11 23:40 신고

      힙영역의 값을 static영역에서 참고 하는거 아닌가요?

    • WANZ 2019.11.12 00:41 신고

      이 글을 읽어보시면 도움이 될 것 같습니다. (참고로 static area == method area)

      --------------------------
      2.5.4. Method Area
      The Java Virtual Machine has a method area that is shared among all Java Virtual Machine threads. The method area is analogous to the storage area for compiled code of a conventional language or analogous to the "text" segment in an operating system process. It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods (§2.9) used in class and instance initialization and interface initialization.

      The method area is created on virtual machine start-up. Although the method area is logically part of the heap, simple implementations may choose not to either garbage collect or compact it. This specification does not mandate the location of the method area or the policies used to manage compiled code. The method area may be of a fixed size or may be expanded as required by the computation and may be contracted if a larger method area becomes unnecessary. The memory for the method area does not need to be contiguous.

      A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of the method area, as well as, in the case of a varying-size method area, control over the maximum and minimum method area size.

  8. javavoa 2019.07.01 14:03

    그렇다면
    public String test123() throw Exception{
    List<String> list = new ArrayList<String>();
    }
    과 같은 함수에서 지역변수로 ArrayList를 생성한다면
    list변수는 heap영역에 실제 값이 들어가는건가요?
    아니면
    stack영역에 저장되는건가요?

    • WANZ 2019.07.01 15:28 신고

      해당 포스트의 <참고2> 를 읽어보시면 좋을 것 같습니다.

      -------- 참고 2 내용 --------
      8가지 원시타입을 제외한 그 외의 타입으로 정의된 변수들은 모조리 레퍼런스 변수, 즉 참조변수이다.

      이런 참조변수들은 실행될 때마다 많은 데이터들을 스택메모리 영역에 뒀다 뺐다 하는게 매우 비효율적이므로, 힙 영역에 그 내용(진짜 값)이 저장되고, 스택 메모리에는 간단하게 그 주소만 저장이 되는 것이다.

      그리고 힙 영역에는 실제 그 변수가 가리키고 있는 값들이 저장되어 있다.

  9. 초보개발자 2019.10.23 10:56

    안녕하세요. 초보개발자입니다. 좋은 글 너무 잘봤습니다. 다름이 아니라 읽으면서 제가 모호한 개념이 있어서 뭐 좀 여쭤보고 싶습니다.
    main(){
    List<String,String> testList = new ArrayList<>();
    for(int i =0; i <1000; i++){
    Map<String,String> testMap = new HashMap<>();
    testMap.put(""+i,""+i);
    testList.add(testMap);
    }
    }
    위와 같은 코드의 경우 for문안에 있는 testMap의 메모리를 생성하는 경우도 다 heap 메모리에 값이 저장되는 건가요?
    for문이 끝나면 메모리가 사라지기 때문에, 지역변수로써 stack 메모리에 할당되는 줄 알았는데 그게 아닌가 보네요..

    혹시 죄송하지만, 시간이 되시면 위 코드에 대한 스택과 힙에 대한 메모리 생성 해석 좀 부탁드리겠습니다.

    • ChickenCola 2019.11.04 21:32

      안녕하세요 지나가다가 글 잘 보고, 제 생각이 도움이 될 것 같아 답글 남깁니다.

      testMap, testList 모두 heap에 메모리가 생성 되고 그 주소들을 함수(여기서는 main)가 종료될 때까지 stack 메모리에 올라가 있습니다.

      testMap 먼저 살펴 보면 for문 안에서 생성(heap)되고 있고 그 주소값이 stack에 올라갑니다. 그리고 for문이 한 번 끝나면 바로 stack에서 사라집니다.
      하지만 testList에 add를 하고 있고 이때 실제 add 되는 부분은 stack에 올라가 있는 주소값을 add 하기 때문에 stack에서 사라져도 상관 없겠죠?
      반복문이 끝나면 testMap에 해당하는 heap 영역이 1,000개가 생성이 되어 있을 거고, 그 1,000개의 메모리 주소를 가진 testList 하나가 heap에 생성 되어 있게 되는 겁니다.

      그리고 main 함수가 종료되면 힙 메모리도 사라지겠죠?

      만약 main 함수가 아니라 다른 함수였다고 가정을 해보겠습니다.

      testList의 주소값이 stack에 올라가 있었을텐데 함수가 끝나면서 stack이 사라지게 됩니다.

      하지만 아직 힙 영역에 1000개의 testMap과 그 주소를 가지고 있는 1개의 testList는 아직 안사라지고 남아 있게 됩니다.

      그리고 함수가 종료 되면 아무도 testList의 주소와 testMap의 주소를 저장하고 있지 않게 됩니다.

      개발자로써도 다시 해당 메모리의 주소를 모르기 때문에 접근하는 것도 불가능 하죠.

      그럼 사실상 불필요한 메모리를 heap에서 사용 중인거기 때문에 JVM에서 이런 부분을 주기적으로 확인 하고 비우게 됩니다.

      이걸 하는 놈이 가비지 컬렉터입니다.

    • WANZ 2019.11.12 01:13 신고

      ChickenCola님이 상세하게 설명 잘해주셨네요 ㅎㅎ 감사합니다

    • 초보개발자 2019.12.16 16:11

      답변 너무 잘봤습니다.
      답변 보고 한가지 딱 의문사항이 있는데요,
      for문안에서 testList.add(testMap) 구간에서 testMap의 stack영역에 있는 주소값을 복사하는 건가요? 아니면 testMap의 주소값을 바라보는 건가요?
      for문이 끝나면 testMap의 주소값이 stack영역에서 사라질텐데, testList에선 주소값이 남아있는것이면 testList.add는 주소값을 복사하는 걸로 봐도 되겠죠? 즉, for문안에서 stack 영역에서는 2개의 중복되는 메모리값이 있는건가요?

  10. TakeKnowledge 2019.12.11 02:19 신고

    메모리 단위로 이해하니 명확해지네요 좋은 포스팅 감사합니다!

  11. 2020.05.05 05:35

    비밀댓글입니다

다시 힘을내어 포스팅을 해보려고 한다. 아자아자! 비록 구글 너가 애드센스 승인을 빨리 안해주지만 나는 힘을내어 포스팅할거야.


오늘 다루려고 하는 것은 자바의 메모리 구조이다.


메모리 구조가 어떻게 되어 있고, (메모리 구조가 여러 영역으로 나누어져 있는데 그 영역이) 어떻게 다른지를 아주 짧고 간단하게 스쳐지나가듯 짚도록 하겠다.




자바의 메모리 구조

이전 포스트였던가. 자바 가비지 컬렉터를 이야기하면서 JVM이 실행되면 OS가 JVM에게 필요한 메모리를 할당해준다고 했다.


컴퓨터 프로그램이 실행되기 위해 필요한 메모리를 우리는 '메인 메모리'라고 한다. 우리말로는 주기억장치쯤 되시겠다.

물리적으로는 우리가 램(RAM)이라고 부르는 아이다. 

이 메인메모리를 관리하는게 OS이고, JVM(자바 버추얼 머신!)은 OS로부터 필요한만큼 할당받는거다.


이놈 JVM은 OS로부터 받은 메모리를  나누어 관리한다.


전문용어로 OS로부터 받은 메모리 공간(?)을 "Runtime Data Area"라고 하는데. 이 에뤼아를 5개 영역으로 쪼개서 관리한다.


왜 굳이 나눠서 관리 하느냐고?


우리가 보통 물건을 둘 때에도 용도에 맞게 나름의 위치가 있듯이... 샴푸는 욕실, 필기도구는 서재.. 얘네들도 똑같다고 이해하면 된다.


자 그럼 용도에 맞게, 어떻게 나누어 관리하느냐 하면.


- Class Area(=Method Area)

- Stack Area

- Heap Area

- Native Method Stack Area

- PC Register


이렇게 나눠서 관리한다. 


크게는 3개 영역(메소드 영역, 스택 영역, 힙 영역)으로 분류한다.


그림으로 한번 봐야, 기억에 오래 남는다. 서비스루다가.


Class Area

 = Method Area 

 = Static ARea 

Stack Area 

Heap Area 

Native Method Stack Area 

PC Register 


1. 메소드 영역(Class Area, Method Area)

이 영역은 중요해서 그런가.. 이름이 여러 개다. 

그 중 나는 처음 배울 때 '메소드 영역'이라는 이름으로 배워가지고.. 주로 이 이름을 사용하도록 하겠다.


메소드 영역, 여기는 클래스 파일의 바이트 코드가 로드되는 곳이다.

엥? 이게 무슨말이냐고?


JVM이 뭔가를 실행하려면 먼저 그 바이트 코드들이 메모리 공간에 저장이 되어 있어야 한다.

JVM은 메인 메소드를 호출하는 것으로 시작을 한다. 거기서부터 프로그램이 시작된다고 볼 수 있다.

우리가 메인메소드에서 사용하는 클래스와 static변수가 있을텐데, 바로 그것들이 이 메소드 영역에 올라온다.


즉, 메소드 영역에 저장되는 바이트코드는 '프로그램의 흐름을 구성하는 바이트코드'이다.

이 바이트코드는 자바 컴파일러에의해서 컴파일된 친구들이기 때문에, 사실상 전체 바이트코드가 이 영역에 올라간다고 봐도 무방하다.




여기서 잠깐. 


이렇게 메소드 에뤼아에 바이트코드가 올라가는것을 "클래스 로딩" 이라고 한다.

이는 클래스를 사용하는 시점에 해당 클래스의 바이트코드가 들어있는 파일을 찾아서 메모리에 로딩하는 것을 말한다.


클래스 로딩이 이뤄지는 이유는 다시정리하자면!


=> 어떤 메소드가 호출되려면, 먼저 그 메소드를 갖고 있는 클래스 파일이 메모리에 로딩되어있어야 한다.

     그래서 클래스를 실행할 때 *.class 파일을 찾아서 메모리에 로딩하는 것이다.


클래스 로딩을 위한 JVM의 로딩 절차는 다음과 같다.

1. 어떤 메소드를 호출하는 문장을 만났는데, 그 메소드를 가진 클래스 바이트코드가 아직 로딩된 적이없다면, 곧바로 JVM은 JRE라이브러리 폴더에서 클래스를 찾음.

2. 없으면, CLASSPATH 환경변수에 지정된 폴더에서 클래스를 찾음.

3. 찾았으면 그 클래스 파일이 올바른지 바이트코드를 검증한다.

4. 올바른 바이트코드라면 메소드영역으로 파일을 로딩한다.

5. 클래스 변수를 만들라는 명령어가 있으면 메소드 영역에 그 변수를 준비한다.

6. 클래스 블록이 있으면 순서대로 그 블록을 실행한다.

7. 이렇게 한번 클래스의 바이트코드가 로딩되면 JVM이 종료될때까지 유지된다.






----

자 오늘은 여기까지.

  1. 악오 2017.06.01 00:11

    구수한 말투 잘 보고 갑니다

  2. 엄선생의수제자 2017.06.01 11:46

    굉장히 좋은 정보네요 ^^

  3. while(alive) {eat(); sleep(); code();} 2018.05.19 15:01

    이해하기 쉽게 설명이 되어 있네요~ 감사합니다~

  4. David 2020.01.07 16:10

    좋은자료 감사합니다~~ 블로그로 공유해갈게요!!

  5. David 2020.01.07 16:10

    좋은자료 감사합니다~~ 블로그로 공유해갈게요!!

  6. 일용직노동자 2020.12.26 22:25

    매우 좋은 글 감사합니다!! 출처남기고 부분인용을 해가도 될까요?

자바의 가비지 컬렉터(Garbage Collector) 즉, 쓰레기 수집 기능을 이해하려면 메모리에 대한 이해가 먼저다.

1. 자바 실행 프로그램인 JVM과 메모리

메모리는 OS가 관리하는데, 모든 프로그램들은 OS 위에서 돌아간다.

그리고 프로그램이 돌아가려면 당연 메모리가 있어야 한다.

때문에 프로그램들은 OS에게 "나 메모리좀 줘" 하고 요청을 한다.


자바의 실행 프로그램인 JVM도 예외는 아니다. 이 친구도 메모리가 필요하면 OS에게 요청해야한다.

메모리좀 달라고.


그런데 OS가 처음부터 자기가 가진 메모리 전부를 줘버리면, 다른 프로그램들에게 줄 메모리가 없게 된다.

때문에 각 프로그램에게 메모리의 일정부분만 빌려주는(?) 방식으로 관리가 된다.


<OS 메모리>

 

 

 

 

A 프로그램 

JVM 

B 프로그램 

-



이렇게 OS가 메모리를 각각의 프로그램에게 빌려주는 방식이다.


자. 그런데 JVM이 메모리를 쓰다가, OS가 준 메모리 용량이 턱없이 부족하다면?


JVM은 프로그램을 실행하다가, 메모리가 부족하면 OS에게 메모리를 더 달라고 요청한다.

그러면 OS가 또 JVM에게 메모리를 더 빌려준다.


 

 

 

 

 

A 프로그램 

JVM 

B 프로그램 

JVM

 -



여기서 참고적으로 알아둘 것이 있다.


JVM은 또다시 그 안에서 변수, 함수 등 값이 저장될 메모리 주소를 할당해야 한다. OS로부터 받은 메모리들 중, 어디에다가 저장할지 그 주소를 할당해야 한다.

이 때,

JVM은 자기가 받은 메모리 안에서, 절대주소가 아니라 거기에 대한 '상대주소'를 할당한다. 이걸 offset 주소라고 한다.



0 ~ 100 

101 ~ 200 

201 ~ 300 

301 ~ 400 

 

A 프로그램 

JVM 

B 프로그램 

JVM

 -



JVM이 받은 메모리의 절대 주소는 101 ~ 200, 301 ~ 400이지만, 이들의 상대적 주소는 0 ~ 200이라는 뜻이다.


이 말은 즉,

프로그램이 OS로부터 부여받은 메모리는 물리적으로는 분리되어 있을 수 있지만, 논리적으로는 하나의 메모리처럼 동작한다.




흠흠. 

여기까지가 메모리에 대한 간단한 설명이었다.


자, 그럼 다시 돌아가서 Java의 가비지 컬렉터(Garbage Collector)의 '가비지'를 정의하고 가겠다.


2. 가비지(Garbage)란?


프로그램을 실행하다보면 '가비지'가 발생하게 된다. 

요놈, 가비지는 '정리되지 않은 메모리', '유효하지 않은 메모리 주소'를 말한다.


대체 어느 경우인지 아래 코드로 가 봅시다.



int[] array = new int[3];

array[0] = 0;
array[1] = 10;
array[2] = 20;

...

array = new String[] {'하하', '호호', '히히', '후후', '헤헤'};

System.out.println(array[0]);



1번째 라인에서 만들어진 array는 int타입의 3칸짜리 배열을 가리키는 주소이다. 

3번째 코드부터 계속해서 이 array를 사용하다가

9번째 라인에서 새로운 String 타입의 배열을 만들어서 array가 새로 만든 배열을 가리키게 한다.


글탐, 기존에 array가 가리키고 있던 int타입의 3칸짜리 배열을 또 쓰려고 하면 어찌될까?


안타깝지만 방법이 없다 ㅠㅠ

주소를 잃어버려서 사용할 방법이 없다.

그리고 잃어버린 주소를 다시 찾을 방법도 없다.


요렇게 주소를 잃어버려서 사용할 수 없는 메모리가 바로 '정리되지 않은 메모리'이다.

이걸 프로그래밍 언어로는 "Dangling Object"라고 하고,

자바에서는 쓰레기, 즉 "가비지(Garbage)"라고 부른다.


 *참고: 주소가 가리키는 메모리가 정리된 채로 있을 때, 그 주소는 더이상 유효하지 않게 된다.(없는 메모리 가리키는 주소라고)
            이런 유효하지 않은 주소"Dangling Pointer"라고 부른다.(이건 C언어에 있음)


3. 가비지 컬렉터(Garbage Collector)란?

가비지에 대한 정의를 마쳤기 때문에, 가비지 컬렉터가 무엇인지는 쉽게 짐작할 수 있을 것이다.

가비지 컬렉터는 메모리가 부족할 때 쓰레기(가비지)를 정리해주는 프로그램을 말한다.


프로그램을 실행하다보면 가비지가 발생하게 되는데, 이놈들은 유효한 메모리가 아니다.

그렇게 되면 정리되지 않은 채로 남겨져있는 메모리들은 사용되지도 않으면서 자리를 차지하고 있게된다.

때문에 JVM의 가비지 컬렉터는 가비지를 다른 용도로 사용할 수 있게 '메모리 해제'를 시키는 프로그램이다.


4. 가비지 컬렉터는 언제 실행되는가?

JVM은 메모리를 부여받고 열심히 프로그램들을 실행하다가 메모리가 부족해지는 순간이 오면 OS에게 추가로 메모리를 더 요청하게 된다.

바로 이 메모리를 더 달라고 요청하는 때에 가비지 컬렉터(Garbage Collector)가 실행된다.


또, 서버 프로그램인 경우에는 24시간 내내 돌아가는데, 이 때에는 JVM이 한가할 때(idle time) 가비지 컬렉터가 실행된다.

(JVM이 종료되면, 당연히 사용하던 모든 메모리는 OS에게 반납된다.)







여기까지가 가비지 컬렉터에 대한 설명이었다.


자바 공부할 때, 가비지 컬렉터를 왜그렇게 열심히 설명하시나.. 그냥 자동으로 메모리 정리해주는 친구가 있다고 하면 될걸.

하고 생각했었는데,

C언어에서는 개발자가 강제로 메모리를 해제시키는 문법이 있다는걸 알게되면서 가비지 컬렉터의 위대함을 깨달았다.


 * 참고: C언어에서는 개발자가 메모리 해제시키는 문법이 있지만, Java에는 없다.


  1. 가비지 2017.11.13 23:23

    가비지컬렉터 잘 알고 갑니다!ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

  2. 컬렉터 2018.01.04 09:38

    좋아요!! 쏙쏙들어오네요~

  3. 청소부 2018.08.03 21:12

    감사합니다. 이해가 정말 쉽게 되네요 ㅋㅋ

  4. GCC 2019.05.21 01:36

    으엌ㅋㅋㅋ 바로 이해되네요!!! 좋은글 감사합니다

자바에서 패키지를 사용하지 않고, 다른 클래스에서 정의한 메소드를 사용한다고 해보자.


<a.java> 

경로: src/a.java

public a {
    public static void wow() {
        System.out.println("a.class 실행결과로 A가 나왔습니다!");
    }
} 


<b.java> 

경로: src/haha/b.java

public b { 
    public static void wow() {
        System.out.println("b.class 실행결과로 B가 나왔습니다!");
    }
}


이렇게 정의된 자바파일이 있다고 해보자. 

a.java파일은 src 폴더 바로 밑에 있고, b.java파일은 src폴더 밑에 있는 haha폴더 밑에 있다.

그런데 이 두 파일은 모두 'wow'라는 이름의 메소드를 정의하고 있다.


명령창에서 각각 이 두 파일을 컴파일하여 a.class파일은 bin폴더 밑에, b.class파일은 bin폴더 밑에 있는 haha 폴더 밑에 둬 보겠다.
(이 때 폴더가 없으면 에러나니까 haha 폴더를 bin폴더 아래에 미리 만들어놔야 함)

 => javac -d bin src/a.java

=> javac -d bin/haha src/haha/b.java


욜케 하면 a.class파일과 b.class파일이 각각 아래의 경로로 생성된다.

- bin/a.class

- bin/haha/b.class


이 두 클래스에는 이름이 같은 wow 메소드가 각각 정의되 있는데, 

a.class에서 정의한 wow메소드를 사용하는 ax.java파일과
b.class에서 정의한 wow메소드를 사용하는 bx.java파일이 있다고 하자.

- ax.java: src/a.class에 정의된 wow메소드 사용
- bx.java: src/haha/b.class에 정의된 b.wow메소드 사용


<ax.java>

경로: src/ax.java

public ax {
    public static void main(String[] args) {
        a.wow();
    }
} 


<bx.java>

경로: src/bx.java

public bx {
    public static void main(String[] args) {
        b.wow();
    }
} 

각각 이렇게 정의가 되어있을 것이다.


이놈들을 컴파일 할 때에는 각각 wow메소드가 정의되어 있는 클래스파일을 읽어들이면서 컴파일을 해야하는데,
명령창에 아래와 같이 실행한다.

=> javac -d bin -cp bin src/ax.java

=> javac -d bin -cp bin/haha src/bx.java


ax.java파일은 a.class파일에 정의된 메소드를 사용하므로 -cp 뒤에 a.class파일이 위치한 경로를 지정해주었고,
bx.java파일은 b.class파일에 정의된 메소드를 사용하므로 -cp뒤에 b.class파일이 위치한 경로를 지정해준거다.


ax.class, bx.class 두 파일을 실행해주면 아래와 같은 결과가 나온다.


<ax.class파일을 실행 결과>

-> a.class 실행결과로 A가 나왔습니다!

<bx.class파일을 실행 결과>

-> b.class 실행결과로 B가 나왔습니다!


여기까진 아무 문제가 없다.

그런데 x.java라는 파일에서 a.class와 b.class에 정의된 wow메소드 모두를 사용하려고 한다고 해보자.


그러면 아래와 같은 소스코드를 기대할 것이다.


<x.java>

경로: src/x.java

public x {
    public static void main(String[] args) {
        a.wow();
        b.wow();
    }
} 


여기까진 그렇다고 치는데..

문제는 이걸 어떻게 컴파일 하느냐는 거다.


a.class는 bin에 있고, b.class는 bin/haha에 있는데..


- javac -d bin -cp bin src/x.java

  => 이걸 실행하면 b.wow() 라인에서 컴파일 에러가 날 것이고,

- javac -d bin -cp bin/haha src/bx.java

  => 이걸 실행하면 a.wow() 라인에서 에러가 날 것이니.



글탐, 이걸 해결하려면???


아쉽게도 방법은 없다. 

package를 이용하는 것 뿐.


아, 누군가는 

public x {
    public static void main(String[] args) {
        a.wow();
        haha.b.wow();
    }
} 


뭐 이런 상상을 할 수 있겠으나, 안타까비하게도 자바에서 이런 문법은 없다.



결론은 

자바에서 package이용 없이 서로 다른 경로의 클래스에서 같은 이름으로 정의한 메소드를 모두 사용할 수 있는 방법은 없.다

는거다.


욜케 자바에 package가 있는 배경을 알고 넘어가자능. 헿

java 파일과 class 파일 구분하여 관리하기


왜 구분해야 하지?

source code 파일(.java)과 byte code 파일(.class)이 한 폴더에 함께 있는 것은 바람직하지 못하다.
배포할 때에는 실행파일인 바이트코드 파일, 즉 class파일만 배포하는데 여기에 소스코드 파일인 java 파일이 섞여 있으면 관리하기가 불편하기 때문이다.

따라서 두 종류의 파일을 서로 다른 폴더로 분리시켜야 한다.


어떻게 구분하지?

구분하는 방법은 쉽다.
class파일은 컴파일이 완료되면 생성된다. 
그 말은 컴파일 할 때 옵션을 줘서 어느폴더에 저장될지 지정해주면 된다는 말씀이다.

폴더를 지정해주기 전에, 그 폴더를 만들어줘야 한다.
있지도 않은 폴더에 무언가를 담을 수는 없으니까.

보통은. 일반적으로는. 대부분은.
소스코드(자바 파일)는 source의 약자로 'src'라는 이름으로, 클래스 파일은 binary의 약자로 'bin'이라는 이름으로 폴더를 지정해준다.
(이걸 몰랐을 때는 '통' 이라는 의미로 bin을 사용하는줄 알았다능ㅋㅋㅋㅋ)


폴더 지정 옵션으로 컴파일하는 방법

=> javac -d bin src/파일명.java

이렇게 하면 bin이라는 폴더 안에 src폴더 안에 있는 자바 파일을 컴파일한 클래스 파일이 생성된다.

참고로 'javac'라는 명령어는 컴파일 명령어다. 
위의 명령어를 풀이하면 다음과 같다.

'bin 폴더 안에다가 컴파일한 결과물을 둘거야. (그 결과물은 .class파일)
글탐 뭐를 컴파일 할거냐하면, src폴더 밑에 있는 파일명.java파일 있지? 
그거를 컴파일 할거야.'
라는 뜻이다.

이렇게 클래스파일과 자바파일 폴더가 구분되어 있으니 실행할 때에도 어느 폴더 안에 있는 클래스파일을 실행할건지 알려줘야 한다.

폴더 지정 옵션으로 실행하는 방법

=> java -classpath bin 파일명

=> java -cp bin 파일명

(두가지 모두 똑같음)


이걸 해석하면
'클래스 파일을 하나 실행할거야.
근데 뭘 실행할거냐면, bin폴더 밑에 있는 그 파일 있지?
고 놈을 실행할거야.'
라는 말이다.


<Java 컴파일/실행 시 폴더지정 옵션 정리>

* -d 옵션: 컴파일한 결과물(클래스파일)을 어디에 둘지 폴더를 지정한다.
                (해당 폴더가 실제로 있어야 함)
                javac -d 폴더명 파일명.java

* -cp 옵션: 클래스파일을 읽을 때(실행할 때) 해당 파일이 어디에있는지 위치를 지정한다.
                 java -cp 폴더명 파일명

 

-----------------------------------

이클립스와 같은 개발 툴을 이용한다면, 명령창에 javac 명령어로 컴파일하고, java 명령어로 실행하는 그 자체가 낯설테지만...

요정도는 기본 배경 날리지로 좀 알아두는 것도 나쁘지 않겠다.



컴파일(Compile)

자바 소스코드(source code)를 기계가 이해하기 쉬운 바이트코드(byte code)로 변환하는 것을 컴파일이라고 한다.
이런 일을 해주는 놈을 우리는 컴파일러(compiler)라고 부름.

컴파일할 때에는 단순히 바이트코드로 변환만 하는 것이 아니라, 소스코드의 문법을 분석하고, 문법이 유효한지 유효성 검사가 이뤄진다.

문제가 없으면 byte code로 이뤄진 .class 파일이 생성되는데, 문자들이 어떤 방식으로 인코딩이 되냐하면 
컴파일할때 사용하는 그 OS의 기본 문자열집합으로 인코딩이 된다.

윈도 운영체제 사용하는 사람들 중에, 소스코드에 한글이 포함된 파일을 컴파일 돌렸을 때 컴파일 에러를 만나본 적이 있을 것이다.

왜 컴파일할 때 문자가 깨지고 에러가 날까?

그것은 Windows 운영체제가 기본적으로 컴파일할 때 문자열들을 MS-949 문자열 집합(character set)으로 인코딩하기 때문이다.
Mac OS는 기본이 UTF-8인데, Windows 운영체제는 자기네들이 만든 MS-949 캐릭터 셋으로 기본 설정이 되어 있어서, 
소스코드에 작성한 문자들을 byte code로 변환할 때 모조리 MS-949로 인코딩해버린다.
따라서 지금 내가 컴파일을 돌리고 있는 컴퓨터 운영체제가 윈도우 운영체제다, 라고 하는 사람들은 
컴파일할 때 국제표준인 UTF-8로 인코딩한다고 지정해주는 것이 바람직하겠다.

 * Eclipse와 같은 개발 툴을 이용한다면 설정만 해주면 자동으로 해준다. 그러나 명령창에서 컴파일 돌리는 경우에는 자동으로 안해주니까 신경쓸 것.



컴파일하는 방법

=> javac 파일명.java 


컴파일할 때 문자열집합(캐릭터) 인코딩 하는 방법

=> javac -encoding 문자집합 소스파일명

     Ex) javac -encoding utf8 파일명.java


대체 MS-949는 뭐고, UTF-8은 뭔가?

하고 궁금할 것이다.

'가', '나', '다', ... 이런 문자들은 사실 하나의 '그림'이다.
컴퓨터는 0과 1만 읽어들일 수 있는, 사실은 아주 멍청한 놈이라 '[1010 1100 0000 0000] 이런 비트 조합으로 입력이 들어오면 '가' 라는 문자를 뿌리겠다' 라는게 미리 정의되어있지 않으면 우리한테 문자들을 보여줄 수 없다.
즉, 컴퓨터에 일일이 가, 각, 곽, 꽋 등 표현할 수 있는 모든 문자 그림이 비트조합으로 저장이 되어있다 이 말이다.

문제는 MS949와 UTF8은 서로 정의된 비트조합이 다르기 때문에 문자가 깨지는 일이 발생한다는 거다.
MS949로 입력한걸 UTF8로 보려고 하면, 해당 비트조합에는 전혀 다른 문자가 정의되어 있기 때문에, 우리가 보기에는 문자열이 꼭 깨진 것 처럼 보이는 것이다.
이 때문에 어떤 문자열집합으로 인코딩했는지가 중요하다.


왜 영어는 안깨지냐구?

영어는 MS949나 UTF8이나 똑같은 비트조합을 사용하고 있기 때문이다.
한글은 다른거고.

그니까 앞으로 한글이 소스코드에 포함되어 있는데 하필이면 윈도 운영체제에서 작업을 하고 있다, 라고 한다면 문자열 인코딩을 한번쯤은 확인하길 바란다. 

끄읏.


.java 파일

확장자가 'java'인 파일은 인간이 작성한 source code 파일이다.

.class 파일

확장자가 'class'인 파일은 source code 인 java파일을 자바 컴파일러를 통해 byte code(target code)로 변환한 byte code 파일이다.

왜 굳이 class파일로 변환한 다음, class파일을 실행시켜야 할까?

자바는 먼저 소스코드를 자바 컴파일러를 통해 기계가 쉽게 이해할 수 있는 bytecode로 이뤄진 class파일로 변환한다.
*문법: javac 파일명.java
참고로, 컴파일 과정에서는 소스코드를 분석하고, 문법의 유효성을 검사를 한다.
이렇게 모든 검열(?)을 마치고서야 class파일을 우리는 얻을 수가 있다.

이렇게 컴파일이라는 것을 하게되면 class파일이 생긴다.
그리고 이 class파일을 실행시킨다.
*문법: java 파일명

이렇듯 자바는 굳~이 컴파일을 한 후, 컴파일 된 byte code를 실행시키는 번거로운 과정을 거친다.
걍 컴파일이랑 실행을 합쳐가지고 한번에 하면 될 것이지, 굳이 나눠서 개발자들 번거롭게 하는 이유는 무얼까?
하고 궁금할 것이다. (나는 궁금했따.)

이유는 이렇다.
VM(자바 실행 프로그램)이 이렇게 얻어진 byte code(.class 파일)를 실행할 때에는 컴파일 과정에서 진행한 일들을 진행하지 않고 말 그대로 실행만 한다.
때문에 소스 코드보다 이해가 쉽기 때문에 속도가 더 빠를 뿐만 아니라, 매번 소스코드 문법을 검사하는 등 불필요한 작업을 생략할 수 있어 효율적이다.
이런 이유 외에도, 사람들이 이해할 수 있는 소스코드를 외부 사람들로부터 보호(?)하고자 하는 의도도 있다.


그렇다고 무조건 컴파일한 bytecode를 실행하는 방식이 좋다고만 할 수는 없다.

이 방식은 소스코드를 변경하게되면 그 때마다 컴파일 -> 실행의 과정을 거쳐야 한다는 번거로움이 따른다.
즉, 소스코드를 바로 실행하게 되면 프로그램 배포가 쉽다.
프로그램을 변경할 때마다 컴파일 할 필요 없이 바로 배포하면 되니까.

대표적인 예로 NodeJS를 들 수 있다. 
자바스크립트는 컴파일이 필요 없고, 바로 실행할 수 있다.

이렇게 장/단점이 있다는거.

참고로 이클립스를 쓰면, 소스코드 파일을 저장할때마다 알아서 자동으로 컴파일을 돌려서 클래스 파일을 만들어준다.
그래서 이런 번거로움을 느끼지 못할 것이다.


자바 컴파일러(Java Compiler)

자바 소스 코드를 실행시키려면 먼저 기계가 잘 알아들을 수 있는 bytecode로 변환을 시켜야 한다.
변환하기 전의 원본 코드인 source code를, 기계에서 실행할 수 있도록 bytecode로 변환하는 일을 하는 프로그램이 바로 자바 컴파일러다.

* 요렇게 특정 프로그램에서 실행할 수 있도록 변환한 코드를 'target code'라고 부름. 특정 기계가 없으면 실행할 수가 없다.
    이 놈은 Native code를 실행하는 것에 비해 실행 속도가 느린 대신, OS 독립적이라서 어떤 OS에서라도 VM만 설치되어 있으면 실행할 수 있다.
  

JVM(Java Virtual Machine)

자바 컴파일러가 변환한 bytecode를 실행시키는 프로그램이다.
(bytecode는 p-code라고도 부름)

이 프로그램은 bytecode를 읽어서 OS의 코드를 호출하는 일을 한다.

실행되는 과정을 설명하면 이렇다.
1. JVM이 target code를 로딩한다.
2. 필요한 기능을 OS에게 호출하면,
3. OS가 HW를 제어함.

한마디로, 자바 컴파일러가 컴파일한 target code는 OS에게 바로 접근할 수 없다.
반드시 JVM을 거쳐야 한다.

JVM의 기능을 호출하는 target code를 JVM이 로딩하면,
JVM은 다시 OS의 기능을 호출하고,
다시 OS는 HW를 제어하는 것이다.

우리가 잘 알고 있는 OS 프로그램인 '메모장'과 비교해보면 이해가 빠르다.
메모장은 윈도우 운영체제의 native app이므로, native code로 되어 있다.
* native code는 특정 OS의 기능을 바로 호출하는 코드로 '기계어 코드'라고 부름.
이 프로그램은 곧장 OS기능을 호출하고, OS는 HW를 제어한다.
즉, JVM을 거칠 필요가 없다.

 

Target code(=bytecode)와 그것을 실행시켜주는 프로그램(VM)의 관계를 잘 이해하면 JVM에 대한 이해가 쉽다.
좋은 예로, Adobe Flash Player가 있다.

종종 어떤 웹 사이트에서 동영상을 보려고 하는데, 컴퓨터에 Flash Player가 설치되어 있지 않으면 동영상을 볼 수 없는 일을 경험해본 적이 있을 것이다.
해당 동영상은 Flash Player라는 실행 프로그램만 읽어들일 수 있는 targetcode로 되어 있기 때문에, Flash Player가 없이는 실행되지 않는 것임.


<코드 종류 별 특징 정리>

* source code(소스코드)
    : 원본 소스 코드. 인간이 작성한 바로 그것.

* byte code
    : 특정 프로그램이 이해하도록 소스코드가 변환된 형태의 코드. 가상 기계를 위한 코드라고도 부른다.
      해당 프로그램(VM)이 없이는 실행할 수 없는 대신, VM만 있으면 어느 OS에서도 실행시킬 수 있다.

* native code
     : 기계어 코드로 OS에 종속되는, 특정 OS에서만 실행할 수 있는 코드를 말한다.
       다른 OS에서 실행하려면 일부 코드를 다시 짜고나서 컴파일을 다시 해야 한다. 





JRE(Java Runtime Environment)

Java bytecode를 실행할 때 필요한 프로그램들로, 자바 프로그램을 실행하는 일반 사용자를 위한 SW이다.

JRE는

- Java Virtual Machine(JVM)
   * JVM은 바이트코드를 실행하는 프로그램이다.

- 필수 자바 라이브러리

로 구성되어 있다.


JDK(Java Development Kit)

자바 프로그램을 만드는 개발자가 사용하는 SW이다.

JDK에는 자바프로그램을 개발하는데 필요한 프로그램들이 들어 있는데, 당연히 JVM과 필수 자바 라이브러리를 포함한다.
즉, JDK는 JRE를 포함하고 있다.

JDK > JRE !!!

프로그램을 개발했으면 실행시키고 테스트도 해봐야 하니까... JVM이 있는 것은 당연한거다.

다시 정리해서, JDK는

- JRE

- 자바 컴파일러, 프로파일러, 문서생성기 등 개발도구들

로 구성되어 있다.


개발하려고 자바 다운로드 하려고 할때마다 JRE받아야 했던가... JDK 받아야 했던가.... 헷갈렸었다.
애초에 Fullname을 적어주면 참 좋으련만, 이 세계는 줄임말이 난무하는지라 ㅜㅜ


Server-JRE

서버 운영자가 서버에 설치하는 SW로, 일반인들을 위한 SW가 아니다.

기존의 JRE에서 Desktop Application 관련 기능을 빼버리고, 그 대신 Server Application 관리에 필요한 모니터링 기능을 추가한 것이 Server-JRE이다.



----------------------------------------------

자바를 설치할 때마다 뭘 받아야 하는지 헷갈리는데, 

일반적인 개발자는 JDK를 설치하면 된다.  


이제 다시는 헷갈리지 않으리.

----------------------------------------------

  1. 악오 2017.05.31 20:16

    블로그 비교중!!! 두둥!

+ Recent posts