2025-11-15

오늘 배운 것

  • JVM(Java Virtual Machine)
    • 개념
      • 자바 바이트코드를 OS/하드웨어 상관없이 실행시키는 가상 머신
      • 자바 프로그램은 다른 언어와 다르게 코드 → 바이트 코드 → JVM → 기계어 이 과정을 거치기 때문에 OS 독립성이 생긴다.
      • 따라서 자바 파일은 운영체제가 아닌 JVM 위에서 실행되는 프로그램이다
    • 전체 구조
      1. Class Loader
      2. Runtime Data Area
      3. Excution Engine
    • JVM 메모리 구조
      1. 메서드 영역
        • 클래스 정보(필드, 메서드, static), 상수 풀(Constant Pool)
        • JVM 시작 시 생성, JVM 전체가 공유
        • 객체를 저장하는 영역
        • new 로 만든 객체가 여기에 올라간다
        • GC가 동작하는 영역
        • Young/Old generation으로 나뉜다
      2. 스택
        1. 스레드마다 각각 존재
        2. 메서드 호출 시 생성되는 스택 프레임 저장
        3. 지역 변수, 매개변수 저장
        4. 메서드 종료 시 해당 프레임 삭제
      3. PC 레지스터
        1. 현재 실행 중인 JVM 명령어의 주소 저장 (스레드마다 1개)
      4. 네이티브 메서드 스택
        1. C/C++로 작성된 네이티브 코드 실행을 위한 공간
    • 클래스 로더
      • JVM이 .class 바이트 코드를 읽어들이는 역할
      • 작동 순서
        1. Loading
          • .class 파일을 읽어 메서드 영역에 저장
        2. Linking
          • Verify : 바이트 코드 검증
          • Prepare: static 변수 기본값 준비
          • Resolve : 참조를 실제 주소로 연결
        3. Initialization
          • static 초기화 코드 실행
          • static 블록 실행
      • 클래스 로더 3단계
        • Bootstrap ClassLoader
        • Extension ClassLoader
        • Application ClassLoader
    • 실행 엔진
      • 바이트 코드를 실제 기계어 수준에서 실행하는 장치
      • 구성
        1. 인터프리터
          • 바이트코드를 한 줄씩 해석 / 실행
          • 시작은 빠르지만 반복 실행 시 느림
        2. JIT(Just-In-Time) 컴파일러
          • 자주 실행되는 코드를 기계어로 변환해 캐싱
          • 한 번 기계어로 바뀌면 매우 빠르게 실행됨
          • 인터프리터 + JIT 하이브리드 방식
        3. Garbage Collector(GC)
          • 힙 영역의 불필요한 객체를 자동으로 정리
          • mark → sweep → compact 과정으로 이루어짐
    • JVM의 힙 구조(Young/Old)
      • 힙은 GC 효율을 높이기 위해 세 부분으로 나뉜다.

          Young  Eden + Survivor(S1,S2)
          Old  오래 살아남은 객체
        
      • Young Generation
        • 대부분의 객체는 금방 생성 후 사라짐 → Minor GC에서 빠르게 정리됨
      • Old Generation
        • survivor 영역에서 여러 번 살아남은 객체 → Major GC / Full GC 대상
    • Stop-The-World(STW)
      • GC가 실행될 때 모든 스레드가 멈추는 시간을 의미
      • GC는 불가피하게 STW를 발생시키기 때문에 GC튜닝을 통해 STW 최소화가 핵심 목표
  • Java 실행과정(JDK/JRE/JVM)
    • JDK /JRE / JVM 관계
      • 자바 개발 환경은 계층 구조

          [JDK]
           └─ [JRE]
                └─ [JVM]
        
      • JDK(Java Development Kit)
        • 개발용 패키지
        • 컴파일러(javac), 디버거, 빌드 도구 등 포함
        • 개발 시 반드시 필요
      • JRE (Java Runtime Environment)
        • 실행 환경 패키지
        • JVM + 표준 라이브러리(rt.jar 등)
        • 실행만 필요할 때 사용(개발엔 부족)
      • JVM (Java Virtual Machine)
        • 바이트코드를 실제로 실행하는 엔진
        • OS/CPU에 맞춰 구현되며 플랫폼 독립성 제공
    • 자바 실행 과정

        소스(.java)
               (javac 컴파일)
        바이트코드(.class)
               (Class Loader 로딩)
        JVM 내부 메모리(Method Area)
               (Interpreter + JIT)
        실행
      
      1. .java 파일 작성
        • 개발자가 자바 소스 코드를 작성
      2. 컴파일 : javac → .class 생성
        • 명령 : javac Main.java
        • 결과
          • 바이트코드(.class) 파일 생성
          • JVM이 이해할 수 있는 중간 언어
          • 어떤 OS에서도 동일
        • 이 과정은 JDK에 포함된 컴파일러가 수행
      3. 클래스 로딩
        • 클래스 로더가 .class 파일을 읽어 JVM 메모리(메서드 영역)에 저장
        • 과정
          1. Loading : .class 읽기
          2. Linking
            • Verify(검증)
            • Prepare(static 기본값 확보)
            • Resolve(참조 주소 연결)
          3. Initialization
            1. static 변수 초기화
            2. static 블록 실행
        • JRE 내부의 JVM이 담당
      4. JVM 메모리 적재
        • 클래스 로더로 읽은 데이터들은 JVM 내부의 메모리 영역에 배치
          • 메서드 영역 : 클래스/메서드 정보, static 저장
          • 힙 : new 객체 저장
          • 스택 : 메서드 호출 시 프레임 저장
      5. 실행 엔진에 의해 실제 실행
        • 실행 엔진이 바이트 코드를 실제 기계어로 전환하며 실행
        • 구성요소
          • 인터프리터 : 한줄씩 해석 (빠른 시작, 반복 시 느림)
          • JIT 컴파일러 : 자주 실행되는 코드 전체를 머신 코드로 변환
        • 실행 흐름

            바이트코드
              (인터프리터)  줄씩 실행
              반복 많으면
              (JIT 컴파일러) 전체 메서드를 기계어로 변환
              매우 빠른 실행
          
      6. Garbage Collector 동작
        • 힙에 더 이상 참조되지 않는 객체를 수집해 정리
          • Young → Minor GC
          • Old → Major GC / Full GC
          • 이때 STW 발생 가능
  • OOP 핵심 개념
    • 객체 지향이란?
      • 프로그램을 객체 단위로 나누어 각 객체가 데이터(속성) + 기능(메서드)를 갖도록 구성하는 프로그래밍 패러다임
    • 객체와 클래스
      • 클래스(Class)
        • 객체를 만들기 위한 설계도
        • 필드(속성), 메서드(행동)를 정의
      • 객체(Object)
        • 클래스의 실제 인스턴스
        • 메모리에 로딩된 실체
    • 객체지향의 4대 특징
      • 캡슐화(Encapsulation)
        • 데이터(필드)와 기능(메서드)을 하나로 묶는 것
        • 외부에서 직접 필드에 접근하지 못하도록 보호(private)
        • 필요한 API만 공개(getter/setter)
        • 효과
          • 보안성 향상
          • 코드 응집도 증가
          • 데이터 무결성 유지
      • 상속(Inheritance)
        • 부모 클래스의 속성과 메서드를 자식 클래스가 물려받음
        • 코드 재사용성과 확장성 증가
        • 주의점
          • 과한 상속으로 결합도 증가로 유지보수 어려워짐
      • 다형성(Polymorphism)
        • 같은 메서드 호출이 객체 타입에 따라 다르게 동작하는 성질
        • 정적 다형성 (컴파일 시) → 오버로딩
          • 같은 이름의 메서드를 매개변수 형태로 구분(개수, 타입, 순서)
        • 동적 다형성 (런타임) → 오버라이딩
          • 부모 메서드를 자식이 재정의
          • 접근제어자 완화는 가능(부모보다 더 큰 범위로)
            • public > protected > default > private
            • 애초에 private는 상속이 안됌
          • 동적 바인딩(dynammic dispatch) 기반으로 동작
            • 실행 중에 어떤 객체인지에 따라 메서드 결정
      • 추상화(Abstraction)
        • 불필요한 내부 구현을 감추고 객체가 가진 핵심적인 기능만 노출
        • 방법
          • 인터페이스
          • 추상클래스
        • 효과
          • 복잡도 감소
          • 실제 동작이 아닌 무엇을 할 수 있는지에 집중
    • 인터페이스 vs 추상 클래스
      • 인터페이스
        • 행동의 규칙 정의
        • 다중 구현 가능
        • default, static 메서드 제공 가능
      • 추상 클래스
        • 공통 속성 + 공통 메서드 + 추상 메서드 혼합 가능
        • 단일 상속만 가능
    • 객체지향의 중요성(장점)
      • 코드 재사용성 : 공통 구조 재활용
      • 유지보수성 : 변경에 강한 구조
      • 확장성 : 기능 추가 쉬움
      • 모듈화 : 관리 책임 분리 가능
    • 객체지향 관련 개념
      • 메시지 : 객체간의 메서드 호출
      • 책임 : 객체가 맡아야 할 역할
      • 의존성 : 객체 간 관계
      • 응집도 : 객체가 한 책임에 집중하는 정도
      • 결합도 : 객체 간 의존성 정도
  • SOLID
    1. S - 단일 책임 원칙 (Single Responsibility Principle)
      • 정의
        • 클래스는 오직 하나의 책임만 가져야 한다.
      • 이유
        • 여러 책임을 함께 가지면 변경 시 연쇄적으로 영향을 받음
      • 체크 포인트
        • 클래스가 바뀌는 이유가 2개 이상인가(예: 클래스 이름이 2단어 이상?) → SRP 위반
    2. O - 개방-폐쇄 원칙 (Open-Closed Principle)
      • 정의
        • 확장에는 열려있고 변경에는 닫혀있어야한다
        • 기존 코드를 수정하지 않고 새로운 기능 추가 가능 해야 한다
      • 방법
        • 인터페이스를 통해 다형성 사용
        • 전략 패턴, 템플릿 메서드 패턴 등으로 확장 포인트 분리
        • 조건문으로 계속 이러면 이거 저러면 저거 하면 OCP 위반
    3. L - 리스코프 치환 원칙 (Liskov Substitution Principle)
      • 정의
        • 부모 타입 객체를 사용하는 곳에 자식 타입 객체를 넣어도 정상적으로 동작해야 한다
        • 대체 가능성
      • 의미
        • 메서드 오버라이딩에서 부모가 보장한 행동을 무너뜨리면 LSP 위반
      • LSP 위반 예
        • 부모 양수 반환, 자식 음수 반환
        • 부모는 예외 안던짐, 자식은 예외 새로 던짐
        • 부모는 null 허용, 자식은 null 안 허용
    4. I - 인터페이스 분리 원칙 (Inteface Segregation Principle)
      • 정의
        • 클라이언트는 사용하지 않는 메서드가 있는 인터페이스에 의존하면 안된다.
      • 의미
        • 큰 인터페이스를 여러 개로 분리해야 한다
        • 불필요한 기능이 섞여 있는 큰 인터페이스는 ISP 위반
        • Animal 인터페이스에 walk, fly, swim을 모두 넣는 것은 문제
        • 새는 날 수 있지만, 고래는 못남 → 불필요한 의존 발생
    5. D - 의존 역전 원칙 (Dependency Inversion Principle)
      • 정의
        • 상위 모듈은 하위 모듈에 의존하면 안된다
        • 둘 다 추상화(인터페이스)에 의존해야 한다
        • 추상화는 구현체에 의존하면 안된다.
      • 의미
        • 구현체가 바뀌어도 상위 레이어는 영향을 받아선 안됨
        • 스프링 DI/IoC 설계의 핵심 원리
  • Thread-safe
    • 정의
      • 여러 스레드가 동시에 접근해도 결과가 항상 정상이고 데이터 무결성이 보장되는 상태
    • 구현방법
      • Synchronized
        • 자바의 기본 동기화 키워드
        • synchronized void add() {}
        • 한 스레드만 접근 가능(뮤텍스)
        • 객체/메서드/블록 단위 락을 획득
        • 단점 : 성능 저하 (락 경쟁, 블로킹)
      • volatile
        • 공유 변수의 가시성 보장
        • CPU 캐시가 아닌 메인 메모리에서 직접 읽고 씀
        • 재배치 방지
        • 하지만 원자성 보장 x
          • count++ 같은 연산은 volatile로는 안전해지지 않음
      • Atomic Classes
        • java.util.concurrent.atomic.*
        • CAS(Compare-And-Swap) 기반
        • Lock-free 방식으로 원자적 연산 제공
          • AtomicInteger
          • AtomicLong
          • AtomicReference
      • Concurrent Collections
        • 멀티스레드 안전한 자료구조
        • ConcurrentHashMap
        • CopyOnWriteArrayList
        • ConcurrentLinkedQueue
        • 특징
          • 부분 락 혹은 Lock-free 구조
          • Hashtable 같은 “전체 락” 보다 훨씬 효율적
      • Locks
        • ReentrantLock, ReadWriteLock, StampedLock
        • 장점
          • synchronized 보다 세밀한 제어
          • 타임아웃, 조건 변수(Condition) 사용 가능
          • 공정(fair) 모드 지원
        • 단점
          • finally에서 unlock 꼭 필요 (실수할 수 있음)
      • Immutable(불변 객체) 구조 활용
        • 공유 자원을 애초에 변경 불가능하게 만들어 경쟁 자체를 제거
        • Thread-safe 구현 가장 쉬운 방법
        • 예 : String 불변
    • 자바의 Thread 구현방법
      • Thread 클래스 상속

          class MyThread extends Thread {
              public void run() {}
          }
        
      • Runnable 구현

          class MyTask implements Runnable {
              public void run() {}
          }
          new Thread(new MyTask()).start();
        
      • ExecutorService(실무 표준)

          ExecutorService exec = Executors.newFixedThreadPool(10);
          exec.submit(() -> doWork());
        
    • 동시성 vs 병렬성
      • 동시성 : 여러 작업이 겉보기에 동시에 진행됨 (실제로는 빠르게 전환)
      • 병렬성 : 여러 작업이 실제로 동시에 실행됨 (멀티코어에서만 가능)
    • 실무 스레딩에서 중요한점
      • 가능하면 공유 상태 최소화
      • synchronized 최소 사용
      • 상태 변화가 필요하면 Atomic 또는 Concurrent 구조 사용
      • ExecutorService 사용이 표준
      • 락을 사용할 때는 반드시 unlock 보장 필요
  • Java Memory Model(JMM)
    • 개념
      • 멀티스레드 환경에서 메모리 읽기/쓰기를 어떻게 보장할지 정한 규칙
      • 정의
        • 어떤 값이 언제 다른 스레드에게 보이는지 (가시성)
        • 명령이 어떻게 재배치 되는지
        • 어떤 연산이 원자성이 있는지
        • 락/volatile 등이 어떤 방식으로 동작하는지
      • 멀티스레드에서 발생하는 이상 동작의 대부분은 JMM 때문
    • 필요한 이유
      • 자바는 JVM 위에서 실행되고, JVM은 CPU의 캐시/파이프라인 최적화 때문에 여러 문제가 발생할 수 있다
      • 문제
        1. CPU 캐시로 인해 최신 값이 보이지 않음 (가시성)
          • 스레드 A는 x = 10을 캐시에 저장
          • 스레드 B는 같은 데이터의 옛값 x = 0을 보고 있을 수 있으므
        2. 명령어 재배치
          • 컴파일러와 CPU는 성능 때문에 코드 순서를 최적화함

              initialized = true;
              data = 100;
              // 아래로 재배치 될 수 있는데 이것이 멀티 스레드 환경에서 큰 문제가 될 수 있음
              data = 100;
              initialized = true;
            
    • JMM이 해결하려는 3대 문제
      1. 원자성(Atomicity)
        • 한 번에 일어나는 연산을 보장 → 중간 상태를 못보게 함
        • 원자성 보장되는 기본 연산
          • 읽기,쓰기,참조 변경
          • 32bit 정수 연산
        • 원자성 보장 x
          • count++
          • a = a+1
          • long, double(64bit) 일 JVM
      2. 가시성
        • 한 스레드의 쓰기가 다른 스레드에게 언제 보이는지 정의
        •   boolean flag = false;
                          
            Thread A:
              flag = true;
                          
            Thread B:
              while (!flag) { }  // 무한 루프 돌 수도 있음 (캐시 때문)
          
      3. 순서성
        • 코드 순서가 실제 실행 순서를 보장하는가?
        • JMM이 없으면 코드순서 != 실행순서
    • 제공하는 키워드/메커니즘
      • volatile
        • 가시성,순서성 보장
        • 원자성 x
      • synchronized
        • 락 기반
        • 원자성, 가시성, 순서성 보장
        • 완전한 Thread-safe 보장 키워드 → 단 성능 비용 있음
      • final
        • final 필드는 생성자 완료 후 값이 다른 스레드에 항상 보인다
      • Happens-before 규칙
        • X, Y사이에 happens-before 관계가 있으면 X의 연산 결과는 반드시 Y에서 관측 됨
          • 동일 스레드 내 코드 순서 → 보장됨
          • 락 해제 → 락 획득
          • volatile write → volatile read
          • 스레드 start() 이전 → start된 스레드의 실행
          • 스레드 종료 → join() 이후
    • JMM에서 발생하는 오류
      • double-checked locking (JDK 1.5 이전)
        • 재배치 때문에 싱글통이 깨질 수 있음 → volatile로 해결
      • visibility 문제
        • 다른 스레드가 값을 안읽어감
      • atomicity 문제
        • count++ 경쟁 상태
  • 비동기(Async) / 병렬 프로그래밍
    • 동기/비동기/블로킹/논블로킹
      • 동기
        • 작업이 순서대로 실행
        • 하나가 끝나야 다음 작업 시작
      • 비동기
        • 요청 후 즉시 다음 작업 진행
        • 결과는 나중에 콜백/이벤트로 받음
      • 블로킹
        • 작업이 완료될 때까지 스레드 멈춤
      • 논블로킹
        • 작업 시작후 스레드가 멈추지 않고 계속 진행
      • 조합
        • 동기 + 블로킹 : 전통방식, 순서대로 다기다림
        • 동기 + 논블로킹 : 요청은 즉시 끝나지만 결과는 기다려야 함
        • 비동기 + 블로킹 : 거의 없음
        • 비동기 + 논블로킹 : 가장 현대적인 방식
    • 자바에서 비동기 구현
      • Thread/Runnable
        • 직접 스레드를 만들어 비동기 실행
        • 단순하지만 실무에서 잘 사용 안함
      • ExecutorService(스레드 풀 기반)
        • 장점
          • 스레드 재사용
          • 관리 쉬움
          • 대규모 서버 구현 가능
      • Future
        • 비동기 결과를 객체로 관리
        • 단점
          • 완료되지 않으면 get() 시 블로킹
          • 콜백 없음
          • 함수형 프로그래밍 불가능
      • CompletableFuture (Java 8+ 핵심)
        • 비동기 + 논블로킹을 가장 깔끔하게 구현하는 도구
        • 특징
          • 콜백 기반 비동기
          • 체이닝 가능
          • 내부적으로 ForkJoinPoll 사용(기본)
    • 비동기 vs 병렬
      • 비동기
        • 스레드 안멈춤
        • 결과를 나중에 받음
        • 콜백 / 이벤트 기반
      • 병렬
        • 여러 스레드가 실제로 동시에 실행
        • 멀티코어에서만 가능
    • Java 비동기 개념
      • 콜백 : 결과를 나중에 전달 받는 함수

          future.thenAccept(result -> print(result));
        
      • Promise(Future/CompletableFuture) : 비동기 결과 역할을 하는 객체
      • Event Loop : 단일 스레드로 이벤트 처리하는 구조
        • 자바에서 java.nio , Netty, Reactor에서 사용
      • Non-bloking I/O(NIO)
        • 자바 NIO는 논블로킹 소켓을 지원
        • 특징
          • 스레드 하나가 여러 소켓을 감시
          • 대규모 연결 처리 가능 → 대규모 채팅 서버에서 사용되는 방식
        • Selector 기반
    • 스프링에서 비동기 (Spring @Async)
      • 원리
        • 내부적으로 TaskExecutor(스레드 풀) 사용
        • 반환 타입을 CompletableFuture로 둘 수 있음
      • 주의
        • 같은 클래스 내부 메서드 호출은 프록시 적용 안됨
    • 비동기/병렬이 필요한 이유
      • I/O 작업이 많은 서버에서 높은 처리량 확보
      • 스레드를 블로킹 시키지 않음
      • CPU-bound/IO-bound 작업 분리
      • 대규모 트래픽 처리 가능
  • JCF(Java Collection Framework) 핵심 자료구조

      Collection
       ├── List
            ├── ArrayList
            ├── LinkedList
            └── Vector (거의  )
       ├── Set
            ├── HashSet
            ├── LinkedHashSet
            └── TreeSet
       ├── Queue
            ├── PriorityQueue
            └── ArrayDeque
       └── Deque
             └── ArrayDeque
        
      Map
       ├── HashMap
       ├── LinkedHashMap
       └── TreeMap
    
    • List
      • ArrayList
        • 내부 구조
          • 배열기반 동적 배열
          • Index 접근 매우 빠름
        • 시간 복잡도
          • get: O(1)
          • add : 평균 O(1), resize O(n)
          • remove : O(n) (중간 삭제 시 뒤요소들 당김)
        • 특징
          • 랜덤 액세스 빠름
          • 많은 추가/삭제에는 비효율적
          • 스레드 안전 X
      • LinkedList
        • 내부 구조
          • 이중 연결 리스트
        • 시간 복잡도
          • get : O(n)
          • add/remove : 위치만 알면 O(1)
        • 특징
          • 삽입/삭제 많을 때 유리
          • 메모리 사용량 많음
          • 스레드 안전 X
    • Set
      • HashSet
        • 내부구조
          • HashMap 기반, key만 사용
        • 특징
          • 중복 허용 X
          • 순서 보장 X
          • contains 매우 빠름 (O(1))
      • LinkedHashSet
        • HashSet + 삽입 순서 유지
        • 내부적으로 순서 유지용 이중 연결 리스트 사용
      • TreeSet
        • Red-Black Tree기반
        • 자동 정렬
        • 범위 탐색 가능
        • 시간 복잡도
          • add/remove/contains : O(log n)
    • Map
      • HashMap
        • 내부 구조
          • 배열 + 연결 리스트 + 트리(레드-블랙 트리) 혼합
          • 자바 8부터 버킷 충돌이 많아지면 체인이 트리로 변환
        • 시간 복잡도
          • 평균 : O(1)
          • 최악 : O(log n) → 트리 변환 시
        • 특징
          • 순서 보장 X
          • null key 1개 가능, null value 여러개 가능
      • LinkedHashMap
        • HashMap + 삽입 순서 유지
        • LRU 캐시 구현 시 많이 사용
      • TreeMap
        • Red-Black Tree 기반 Map
        • 자동 정렬됨
        • 시간복잡도 : 모든 연산 O(log n)
        • 특징
          • key 범위 탐색 용이
          • 정렬 기반 맵이 필요할 때 사용
    • Queue/Deque
      • PriorityQueue
        • 내부 구조
          • 이진 힙 기반
          • 기본은 min-heap
        • 시간 복잡도
          • 삽입/삭제 : O(log n)
      • ArrayDeque
        • 내부 구조
          • 배열 기반 원형 큐
        • 특징
          • LinkedList보다 훨씩 빠른 큐/스택
          • 스택으로 쓰면 Stack 보단 안전하고 빠름
          • null 저장 불가(API 안정성)
    • Thread-safe 컬렉션
      • Vector(List)
        • 전체 메서드 synchronized
        • 성능 매우 낮아 거의 사용 x
      • Hashtable (Map)
        • 전체 락 → 성능 낮음
        • HashMap + 동기화 필요 시 사용 x (비추천)
      • Concurrent Collections
        • ConcurrentHashMap
          • 부분 락(시그먼트 락) → 고성능
          • 실무에서 thread-safe Map의 표준
        • CopyOnWriteArrayList
          • 쓰기 시 마다 전체 복사 → 일기 많은 경우 최적
        • ConcurrentLinkedQueue
          • Lock-free 큐

results matching ""

    No results matching ""