CS 전공지식

24.02.07 Java 관련 2

김용글 2024. 2. 7. 15:46

1. GC

    - 가비지 컬렉터란, 힙 메모리 관리를 위해 참조되고 있지 않은 객체들을 메모리에서 삭제하는 역할

    - 객체는 힙영역에 저정되고 스택 영역에 이를 가르키는 주소값이 저장되는데 힙영역에서 자신을 가르키는

      주소값이 없으면, 참조되고 있지 않는다고 판단

 

  1) GC 동작 과정

       - 객체가 생성되면 메모리를 young 영역에 저장

       - 객체를 최초 생성하면 Young 영역에 Eden 영역에 위치

       - Eden 영역에서 Minor GC 가 발생하면 참조 중인 객체는 1번 Survivor 영역으로 이동

       - 1번 Survivor 영역에서 Minor GC 가 발생하면 참조 중인 객체는 2번 Survivor 영역으로 이동하고

         1번 Survivor 영역은 비게 됨

       - Young 영역에서 오래 살아남은 객체는 Old 영역으로 이동

       - Old 영역에 있는 객체는 Major GC 가 발생했을 때 참조 여부에 따라 유지되거나 제거됨

  2) GC 알고리즘 종류

     (1) Serial GC

           - 가장 단순한 방식

           - 싱글 스레드로 동작하고 그만큼 느리다

           - Mark - sweep - compact 알고리즘 (mark 하고, sweep(지우고)하고, compact(빈공간을 채워넣음))

           - 적은 메모리와 CPU 코어 갯수가 적을 때 적합

     (2) Paraller GC

           - Java8 의 기본 GC

           - Serial GC 와 알고리즘은 같지만 GC 를 처리하는 스레드가 여러개

           - 메모리와 코어가 충분할 때 적합

     (3) Paraller Old GC

           - Paraller GC 는 Young 영역에서만 멀티스레드를 사용하지만 이건 Old 영역까지 멀티쓰레드를 사용

           - Paraller GC 에서 Old GC 알고리즘을 개선한 버전

  3) GC 장단점

     (1) 장점

           - 메모리 누수를 막을 수 있다

           - 해제된 메모리에 접근하는 오류와 해제된 메모리를 한번 더 해제하는 이중 해제를 막을 수 있다

     (2) 단점

           - GC 메모리 해제 타이밍을 개발자가 정확하게 알기 힘들다

           - 실시간성이 강조되는 프로그램의 경우 GC 에게 메모리를 맡기는 것은 알맞지 않을 수 있다

 

2. 정적 타입, 동작타입 언어의 장단점

  1) 정적 타입 언어

       - 컴파일 때 미리 타입을 결정하기 때문에 실행속도가 빠르고 타입 에러로 인한 문제점을 초기에

         발견할 수 있어 안전성이 높다

       - 매번 코드 작성시 변수형을 결정해줘야 하는 번거로움이 있다

  2) 동적 타입 언어

       - 런타임까지 타입에 대한 결정을 끌고 갈 수 있기 때문에 유연성이 높고, 컴파일시 타입을 명시해주지

         않아도 되기 때문에 빠르게 코드를 작성할 수 있다

       - 실행 도중에 변수에 예상치 못한 타입이 들어와 타입에러가 발생할 수 있다

       - 런타임시 확인할 수 밖에 없기 때문에 코드가 길고 복잡해질 경우 타입 에러를 찾기가 어려워짐

 

3. 스크립트 언어와 컴파일 언어 차이

  1) 스크립트 언어

       - Javascript, Pytion 이 대표적

       - 인터프리터가 한 라인 한라인 기계어로 번역하며 실행

       - 컴파일 에러라고 부르는 문법 오류를 사전에 방지하지 못하기 때문에 주의

       - 운영체제를 신경 쓸 필요 없이 한번만 적성하면 됨

  2) 컴파일 언어

       - C, C++ 이 대표적

       - 실행 전 소스코드를 컴파일하여 기계어로 변환 후 해당파일 실행

       - 이미 기계어로 변환된 것을 실행하므로 비교적 빠르다

       - 컴파일 과정을 거쳐 기계어 코드로 번역이 되기 때문에 사전에 검증을 할 수 있고 최적화를 해줄 수 있다

       - 운영체제에 따라 다르게 작업해야 함

 

4. Java 코드 컴파일 과정

    - 개발자가 자바 소스 코드를 작성하면 자바 컴파일러가 자바 소스파일을 컴파일

      이때 나오는 파일은 자바 바이트 코드 파일로, 아직 컴퓨터가 읽을 수 없는 자바 가상 머신이 이해할 수 있는 코드

    - 컴파일된 바이트 코드를 JVM 의 클래스 로더에 전달

    - 클래스 로더는 동적로딩을 통해 필요한 클래스들을 로딩 및 링크하여 JVM 의 런타임 데이터 영역에 올림

    - 실행 엔진은 JVM 메모리에 올라온 바이트 코드들을 명령어 단위로 하나씩 가져와서 실행

      이때, 실행엔진은 바이트 코드를 각 OS 가 실행할 수 있는 기계어로 변환

    * 바이트 코드 : 특정 하드웨어가 아닌 가상 머신에서 돌아가는 실행 프로그램을 위한 이진 표현법

                             대부분 명령어는 0 개 이상의 매개변수를 갖는 1바이트 크기를 가지고 있다

 

5. 클래스 로더 동작 과정

    - 클래스 파일을 가져와서 JVM 의 메모리에 로드

    - 자바 언어 명세 및 JVM 명세에 명시된 대로 구성되어있는지 검사

    - 필드, 메서드, 인터페이스 등 메서드 클래스가 필요로 하는 메모리 할당

    - 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경

    - 클래스 변수들을 적절한 값으로 조기화

    * 심볼릭 레퍼런스 : 참조할 때, 클래스의 특정 메모리 주소를 참조 관계로 구성한 것이 아닌 참조하는 대상의

                                    이름을 지칭

                                    Class 파일이 JVM 에 올라가게 되면 심볼릭 레퍼런스는 그 이름에 맞는 객체의 주소를 찾아서

                                    연결하는 작업을 수행하므로 실제 메모리 주소가 아니라 이름만을 가짐

    * 다이렉트 레퍼런스로 변경이라는 의미? : 실제 메모리 주소 값으로 변경해 주는 작업을 의미

 

6. 동적 로딩?

    - 프로그램을 실행할 때 필요할 때마다 동적으로 메모리를 생성하고 필요없는 메모리는 자동으로 소멸시키는 것

    - 일부 클래스가 변경되어도 전체 어플리케이션을 다시 컴파일 하지 않아도 되기에, 변경사항이 발생해도

      비교적 적은 작업만으로 처리 할 수 있다

 

7. 실행 엔진이 바이트코드를 실행하는 과정

  1) 인터프리터 방식

       - 바이트코드 프로그램은 보통 한번에 하나의 명령어를 읽은 후 실행

       - 한줄 씩 수행하기 때문에 느림

  2) JIT 컴파일러

       - 인터프리터 방식을 보완하기 위해 도입

       - 인터프리터 방식으로 실행하다가 적절한 시점에서 바이트 코드 전체를 컴파일하여 네이티브 코드로

         변경하고 이후에는 더 이상 인터프리팅 하지 않고 네이티브 코드를 직접 실행

       - JIT 컴파일러를 사용하는 JVM 은 내부적으로 해당 메서드가 얼마나 자주 수행되는지 체크하고,

         일정 정도를 넘었을 때 컴파일 수행

       - JIT 컴파일러로 인해, 자바는 네이티브 언어와 유사한 수준의 퍼포먼스를 낼 수 있게 됨

 

8 .기본타입 VS 참조타입

  1) 기본타입 (primitive type)

       - 종류는 byte, short, char, int, float, double, bollean 이 있다

       - 실제 데이터 값 자체를 저장한다 데이터의 크기가 작고 고정적이기 때문에 메모리의 Stack 영역에 저장됨

       - Null 을 다루지도 못하고 Generics 에 담기지도 못함

       - 참조타입에 비교해서 갖는 장점은 성능과 메모리 이점이 있다

  2) 참조타입 (reference type)

       - 기본타입을 제외하고는 모두 참조타입임

       - 4 byte 크기의 주소값이 들어감

       - array 와 class 와 같은 참조 데이터 타입은 객체가 메모리 상에 위치한 주소를 저장, 데이터의 크기가

         동적이기 때문에 동적으로 관리하는 Heap 영역에 저장

       - 더이상 참조하는 변수가 없을 때 가비지 컬렉션에 의해 파괴 됨

       - 데이터가 동적이지 않으면 성능과 메모리에 장점이 있는 기본타입을 먼저 고려해야함

         참조타입은 Stack 메모리에는 참조 값만 있고 실제 값은 Heap 메모리에 존재하기 때문에 값이

         필요할 때마다 접근 속도가 느려지기 때문

       - 만약 Null 을 다뤄야 허거나 Generic 타입에서 사용되어야 하는 경우 사용

 

9. Java ThreadLocal

    - Thread 내부에서 사용하는 지역변수

    - 일반 변수 수명은 특정 코드 블록 범위내에서만 유효하지만 ThreadLocal 을 이용하면 쓰레드 영역에

      변수를 설정할 수 있기 때문에 특정 쓰레드가 실행하는 모든 코드에서 그 쓰레드에 설정된 변수 값을

      사용할 수 있게 됨

    - 멀티 쓰레드 환경에서 각 쓰레드마다 get( ), set( ) 메서드를 통해 독립적으로 변수에 접근할 수 있다

 

  1) 활용방법

       - 한 쓰레드에서 실행되는 코드가 동일한 객체를 사용할 수 있도록 해준다

         따라서, 쓰레드와 관련된 코드에서 변수를 공유할 때 파리미터 또는 리턴 값으로 정보를 제공해주지 않아도 된다

       - 대표적인 활용예시는 Spring Security 에서 사용자 인증 정보를 사용하는 것

         서버에서 클라이언트 요청들에 대해 각 쓰레드에서 처리하게 될 경우, 해당 유저의 인증 및 세션 정보나

        참조 데이터를 저장하는데  ThreadLocal 이 사용됨

  2) 내부 구조

       - thread 정보를 key 로 하여 값을 저장해두는 Map 구조 이고 기본적 사용에는 get, set 메서드 사용

  3) 사용시 주의 사항

       - Thread 의 정보를 key 로 하여 Map 의 형식으로 데이터를 저장한 후 사용할 수 있는 자료구조이므로

         TreadPool 을 사용해 thread 를 재활용 한다면 동일한 이전에 세팅했단 ThreadLocal 의 정보가

         남아있어 원치 않은 동작을 할 수 있다

       - ThreadPool 을 사용하는 경우 반드시 모두 사용후 ThreadLocal 값을 remove 메서드를 사용하여 값을

         제거해주는것이 필요