[CS/JAVA] 스터디 week1

2rlokr·2025년 3월 16일

cs-knowledge

목록 보기
1/12
post-thumbnail

JAVA의 실행과정을 설명해주세요.

  1. 자바 컴파일러(javac)가 소스코드를 바이트코드(*.class)로 변환한다.
  2. 클래스 로더 (Class Loader)가 바이트코드를 JVM의 Runtime Data Area에 올린다.
  3. Execution Engine 의 Interpreter와 JIT compiler을 통해 코드를 해석한다.

JVM의 구조를 설명해주세요.

자바의 구조

JDK (Java Development Kit)

  • 자바의 개발 환경으로, 자바 어플리케이션을 개발하기 위해 필요한 도구
  • 자바 언어를 바이트코드로 컴파일해주는 자바 컴파일러(javac), 자바 클래스를 해석해주는 어셈블리어(javap) 등이 있다.

JRE (Java Runtime Environment)

  • 자바 실행 환경
  • JVM, 자바 클래스 라이브러리, 기타 자바 어플리케이션 실행에 필요한 파일들을 포함한다.

JVM

  • 자바 가상 머신, 자바 어플리케이션을 실행하는 가상머신
  • 실제 컴퓨터로부터 JAVA 어플리케이션 실행을 위한 메모리를 할당받아 Runtime Data Are를 구성
  • 인터프리터(Interpreter)와 JIT 컴파일러를 통해 바이트코드를 각 운영체제에 맞는 기계어로 해석시켜 실행시키고, 가비지 컬렉터를 통해 어플리케이션의 동적 메모리를 관리한다.

자바 소스 파일을 어떤 동작으로 코드를 읽나요?

  1. 자바 프로그램 실행 -> JVM은 OS로부터 메모리를 할당받는다.
  2. 자바 컴파일러(javac)가 자바 소스코드(.java)를 자바 바이트코드(.class)로 컴파일한다.
  3. Class Loader는 동적 로딩을 통해 필요한 클래스들을 로딩 및 링크하여 Runtime Data Area(실질적인 메모리를 할당받아 관리하는 영역)에 올린다.
  4. Runtime Data Area에 로딩된 바이트코드는 Execution Engine을 통해 해석된다.
  5. 이 과정에서 Execution Engine에 의해 Garbage Collector의 작동과 Thread 동기화가 이루어진다.

JVM의 구조

클래스 로더 (Class Loader)

JVM 내로 클래스 파일 (*.class)을 동적으로 로드하고, 링크를 통해 배치하는 작업을 수행하는 모듈이다. 즉, 로드된 바이트 코드(.class)를 엮어서 JVM의 메모리 영역인 Runtime Data Area에 배치한다.

로딩 순서

  • Loading -> Linking -> Initialization

Loading : 클래스 파일을 가져와 JVM의 메모리에 로드한다.
Linking : 클래스 파일을 사용하기위해 검증하는 과정

  • Verifying : 읽어들인 클래스가 JVM 명세서에 명시된대로 구성되어 있는지 검사한다.
  • Preparing : 클래스가 필요로하는 메모리를 할당한다.
  • Resolving : 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경한다.

Initializing : 클래스 변수들을 적절한 값으로 초기화한다. (static 필드들을 설정된 값으로 초기화 등)

실행 엔진 (Execution Engine)

클래스 로더를 통해 런타임 영역에 배치된 바이트코드를 명령어 단위로 읽어서 실행한다. 자바 바이트 코드 (*.class)는 기계가 바로 실행할 수 있는 언어가 아니라 가상머신이 이해할 수 있는 중간 레벨로 컴파일된 코드이다. 그래서 실행엔진은 이와 같은 바이트코드를 실제로 JVM 내부에서 기계가 실행할 수 있는 형태로 변경한다. 인터프리터 + JIT 컴파일러 두 방식을 혼합하여 바이트코드를 실행한다.

인터프리터 (Interpreter)

바이트 코드 명령어를 하나씩 읽어서 해석하고 바로 실행한다.

  • JVM 안에서 바이트코드는 기본적으로 Interpreter 방식으로 동작한다. 다만, 같은 메소드라도 여러 번 호출이 되면, 매번 해석하고 수행해야하기에 전체적인 속도가 느려진다.

JIT 컴파일러 (Just-In-Time compiler)

Interpreter의 단점을 보완하기위한 방법으로, 바이트 코드 전체를 컴파일하여 Native Code로 변경하고 이후에는 더이상 인터프리팅하여 실행하지 않고 캐싱해 두었다가 네이티브 코드로 직접 실행하는 것이다.

  • 하나씩 인터프리팅하여 실행하지 않고, 컴파일된 네이티브 코드를 실행하는 것 => 전체적인 속도는 인터프리팅 방식보다 빠르다.

  • 바이트코드에서 Native Code로 바꾸는 비용도 발생하기에 모든 코드를 JIT 방식으로 해석하지 않는다. 사용하다가 일정 기준을 넘어가면 JIT 방식을 사용한다.

  • 일정 기준 : Client 모드랑 Server 모드가 있는데, Client 모드에서는 150번, Server 모드에서는 10000번이라고 한다. 그리고 이 기준은 변경할 수 있다고 한다.

Native Code : Java에서 부모가 되는 C언어나, C++, 어셈블리어로 구성된 코드

가비지 콜렉터 (Garbage Collector, GC)

Heap 메모리 영역에서 더는 사용하지 않는 메모리를 자동으로 회수해준다.

  • C에서는 개발자가 직접 메모리를 해제해야 한다. 반면에, JAVA는 GC를 이용해 자동으로 메모리를 실시간 최적화 시켜준다.
  • 일반적으로 자동으로 실행되지만, GC가 실행되는 시간은 정해져있지 않다.
  • 특히, Full GC가 발생하는 경우, GC를 제외한 모드 스레드가 중지되기 때문에 장애가 발생할 수 있다. (수동으로 GC를 실행하기 위해 System.gc()라는 메소드를 사용할 수 있지만, 함수 실제 실행은 보장되지 않는다.

런타임 데이터 영역 (Runtime Data Area)

JVM의 데이터 영역으로 자바 어플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역

메서드 영역 (Method Area)

JVM이 시작될 때 생성되는 공간으로, 바이트 코드(.class)를 처음 메모리 공간에 올릴 때 초기화하는 대상을 저장하기 위한 메모리 공간으로 Class AreaStatic Area로도 불리운다.

  • JVM이 동작하고 클래스가 로드될 때 적재돼서 프로그램이 종료될 때까지 저장된다.
  • 모든 스레드가 공유하는 영역으로 다음과 같이 초기화 코드 정보들이 저장되게 한다.
    • Field Info : 멤버 변수의 이름, 데이터 타입, 접근 제어자 정보
    • Method Info : 메소드 이름, return type, 함수 매개변수, 접근 제어자 정도
    • Type Info : Class인지, Inteface인지 여부 저장, Type 속성 이름, Super class의 이름

간단히 말하자면, 메서드 영역에는 정적 필드클래스 구조만 갖고 있다.

런타임 상수풀 (Runtime Constant Pool)

  • 메서드 영역에 존재하는 별도의 관리 영역
  • 각 클래스/인터페이스마다 별도의 Constant Pool 테이블이 존재하는데, 클래스를 생성할 때 참조해야할 정보들을 상수로 가지고 있는 영역이다.
  • JVM은 이 상수풀을 통해 해당 메소드나 필드의 실제 메모리상 주소를 찾아 참조한다.
    정리하면, 상수 자료형을 저장하여 참조하고 중복을 막는 역할을 한다.

메소드 영역 / 런타임 상수풀의 사용기간 & 스레드 공유 범위

  • JVM 시작 시 생성
  • 명시적으로 null 선언 시
  • 프로그램 종료 시까지

힙 영역 (Heap Area)

메서드 영역과 함께 모든 스레드가 공유하며, JVM이 관리하는 프로그램 상에서 데이터를 저장하기위해 런타임 시 동적으로 할당하여 사용하는 영역이다. 즉, new 연산자로 생성되는 클래스와 인스턴스 변수, 배열 타입 등 Reference Type이 저장되는 곳

힙 영역에 생성된 객체와 배열은 Reference Type, JVM의 스택 영역의 변수나 다른 객체의 필드에서 참조된다.
즉, 힙의 참조 주소는 "스택"이 가지고 있고, 해당 객체를 통해서만 힙 영역에 있는 인스턴스를 핸들링할 수 있는 것이다.
만약, 참조하는 변수나 필드가 없다면 의미없는 객체가 되기 때문에 쓰레기로 취급한다. JVM은 GC를 실행시켜 쓰레기 객체를 힙 영역에서 자동으로 제거한다. 즉, 힙 영역은 GC의 대상이 되는 공간이다.

힙 영역의 사용기간 / 스레드 공유 범위
  • 객체가 더 이상 사용되지 않거나 명시적으로 null 선언 시
  • GC의 대상

스택 영역 (Stack Area)

int, boolean, long 등과 같은 기본 참조형을 생성할 때 저장하는 공간으로 사용되는 변수나 정보들이 저장되는 영역이다.

메서드 호출 시마다 각각의 스택 프레임 (그 메서드를 위한 공간)이 생성되고, 메서드 안에서 사용되는 값들을 저장하고 호출된 메서드의 매개변수, 지역변수, 리턴 값 및 연산 시 일어나는 값들을 임시로 저장한다. 그리고 메서드 수행이 끝나면 자동으로 삭제된다.

스택 프레임

메서드가 호출될 때마다 프레임이 만들어지며, 현재 실행중인 메서드 상태 정보를 저장하는 곳이다. 메서드 호출 범위가 종료되면 스택에서 삭제된다. 스택 프레임에 쌓이는 데이터는 메서드의 매개변수, 지역변수, 리턴 값 ,연산 시 결과값등이 있다.

각 스레드마다 하나씩 존재하며 스레드가 존재될 때 할당된다. 프로세스가 메모리에 로드될 때, 스택 사이즈가 고정되어있어 런타임 시에 스택 사이즈를 바꿀 수 없다. 메모리의 크기가 충분하지 않다면, StackOverFlowError 가 발생한다. 스레드 종료 시 런타임 스택도 사라진다.

데이터 타입에 따라 스택과 힙에 저장되는 방식이 다르다.
  1. 기본(원시) 타입 변수는 스택 영역에 직접 값을 가진다.
  2. 참조 타입 변수는 힙 영역이나 메소드 영역의 객체 주소를 가진다.
Person p = new Person();
  • p : 생성된 클래스의 참조, Stack에 저장
  • new Person() : Heap에 저장

PC 레지스터 (Program Counter Register)

스레드가 시작될 때 생성되며, 현재 수행중인 JVM 명령어 주소를 저장하는 공간이다. JVM 명령어 주소는 스레드가 어떤 부분을 무슨 명령으로 실행해야할지에 대한 기록을 가지고 있다.

일반적인 레지스터는 CPU내의 기억장치로, JAVA의 PC Register과 다르다. 자바는 OS나 CPU 입장에서는 하나의 프로세스이기 때문에 가상머신인 JVM의 리소스를 이용해야 한다. 그래서 자바는 CPU에 직접 연산을 수행하도록 하는 것이 아닌, 현재 작업하는 내용을 CPU에게 연산으로 제공해야 하며, 이를 위한 버퍼 공간으로 PC Register라는 메모리 영역을 만들게 된 것이다.
따라서, JVM 스택에서 비연산값 Operand를 뽑아 별도의 메모리 공간인 PC Register에 저장하는 방식을 취한다.

만약, 스레드가 자바 메소드를 실행 중일 땐, JVM 명령어 주소를 PC Register에 저장한다.
만약, 자바가 아닌 다른 언어 (C, 어셈블리어)의 메소드를 실행 중일 땐, undefined로 남긴다.
이는 두 경우를 따로 처리하기 때문이다.

네이티브 메서드 스택 (Native Method Stack)

바이트 코드가 아닌 기계어로 작성된 프로그램을 실행시키는 영역이다. 또한, 자바 이외의 (C, C++, 어셈블리 등) 언어로 작성된 네이티브 코드를 실행하기 위한 공간이다.

사용되는 메모리 영역으로는 일반적인 C스택을 사용한다.

  • JIT 컴파일러에 의해 변환된 Native Code 역시 여기에서 실행된다고 보면 된다.
  • 일반적으로 메소드를 실행하는 경우, JVM 스택에 쌓이다가 해당 메소드 내부에 네이티브 방식을 사용하는 메소드가 있다면, 해당 메소드는 네이티브 스택에 쌓인다. 메소드가 끝나면 다시 자바 스택으로 돌아와 다시 작업을 수행한다. 그래서 네이티브 코드로 되어있는 함수의 호출을 자바 프로그램 내에서도 직접 수행할 수 있고, 그 결과를 받아올 수도 있는 것이다.

JNI (Java Native Interface)

자바가 다른 언어로 만들어진 어플리케이션과 상호 작용할 수 있는 인터페이스를 제공하는 프로그램

  • JVM이 Native Method를 적재하고 수행할 수 있도록 한다.

Native Method Library

  • C / C++로 작성된 라이브러리
    만약, 라이브러리가 필요하다면, JNI는 이 라이브러리를 로딩해 실행한다.

GC가 무엇인지, 필요한 이유는 무엇인지, 동작방식에 대해 설명해주세요.

GC는 가비지 콜렉터 (Garbage Collector)로, 자바의 메모리 관리 기법 중 하나이다. JVM의 Heap 영역에서 동적으로 할당했던 메모리 중 필요없게된 객체(Garbage)를 모아 주기적으로 제거하는 프로세스를 말한다.

GC가 필요한 이유
C/C++ 언어에서는 GC가 없어 개발자가 수동으로 메모리 할당과 해제를 일일이 해야 한다. 반면, JAVA는 GC가 메모리 관리를 대행해주기 때문에 Java 프로세스가 한정된 메모리를 효율적으로 사용할 수 있게 한다. 개발자 입장에서 메모리 관리, 메모리 누수 문제에 대해 관리하지 않아도 되며, 개발에만 집중할 수 있게 해준다.

단점
1. 자동으로 처리한다해도 메모리가 언제 해제되는지 정확하게 알 수 없어 제어하기 힘들다.
2. GC가 동작하는 동안에는 다른 동작을 멈추기 때문에 오버헤드가 발생되는 문제점이 있다.

STM (Stop the World)

GC가 수행되는동안 JVM이 프로그램 실행을 멈추는 현상을 의미

GC가 작동하는동안 GC 관련 Thread를 제외한 모든 Thread는 멈추게 되어 서비스 이용에 차질이 생길 수 있다. 따라서, 이 시간을 최소화하는 것이 중요하다.

  • GC가 너무 자주 실행되면 소프트웨어 성능 하락의 문제가 되기도 한다. 이러한 특성에 따라 실시간성이 매우 강조되는 프로그램일 경우, GC에 프로그램을 맡기는 것은 맞지 않을 수 있다.

가비지 컬렉션의 대상

가비지 컬렉션은 특정 객체가 garbage인지 아닌지 판단하기 위해 도달성, 도달능력(Reachability)의 개념을 적용한다.

객체에 레퍼런스가 있다면 Reachable로 구분되고, 객체에 유효한 레퍼런스가 없다면 Unreachable로 구분해버리고 수거해버린다.

Reachable : 객체가 참조되고 있는 상태
Unreachable : 객체가 참조되고 있지 않은 상태 (GC의 대상이 됨)
- Heap Area 객체의 메모리 주소를 가지고 있는 참조 변수가 삭제되는 현상이 발생하면, Heap 영역에서 어디서든 참조하고 있지 않는 객체(Unreachable)들이 발생하게 된다.

GC 청소 방식

Mark and Sweep

다양한 GC에서 사용되는 객체를 솎아내는 내부 알고리즘이다. 가비지 컬렉션이 동작하는 아주 기초적인 청소 과정이다.

가비지 컬렉션이 될 대상 객체를 Mark(식별)하고 Sweep(제거)하며 객체가 제거되어 파편화된 메모리 영역을 앞에서부터 채워나가는 작업(Compaction)을 진행하게 된다.

Mark 과정 : 먼저 Root Space로부터 그래프 순회를 통해 연결된 객체들을 찾아내어 각각 어떤 객체를 참조하고 있는지 찾아서 마킹한다.
Sweep 과정 : 참조하고 있지 않은 객체 즉, Unreachable 객체들을 Heap에서 제거한다.
Compact 과정 : Sweep 후에 분산된 객체들을 Heap의 시작 주소로 모아 메모리가 할당된 부분과 그렇지 않은 부분으로 압축한다.(가비지 컬렉터 종류에 따라 하지 않는 경우도 있다.)

이렇게 Mark-Sweep 방식을 사용하면 루트로부터 연결이 끊긴 순환 참조되는 객체들을 모두 지울 수 있다.

Heap의 구조

객체는 대부분 일회성이며, 메모리에 오랫동안 남아있는 경우가 드물기 때문에 보다 효율적인 메머리 관리를 위해 객체의 생존 기간에 따라 물리적인 Heap 영역을 Young과 Old 총 2가지 영역으로 설계하였다.

Heap 영역이 설계될 때의 2가지 전제
1. 대부분의 객체는 금방 접근 불가능한 상태(Unreachable)가 된다.
2. 오래된 객체에서 새로운 객체로의 참조는 아주 적게 존재한다.

Young 영역 (Young Generation)

  • 새롭게 생성된 객체가 할당되는 영역
  • 대부분의 객체가 금방 Unreachable 상태가 되기 때문에, 많은 객체가 Young 영역에 생성되었다가 사라진다.
  • Young 영역에 대한 가비지 컬렉션을 Minor GC라고 부른다.

또 다시 힙 영역은 효율적인 GC를 위해 Young 영역을 3가지 영역(Eden, survivor0, survivor1)으로 나눈다.

Eden

  • new를 통해 새로 생성된 객체가 위치
  • 정기적인 쓰레기 수집 후 살아남은 객체들은 Survivor 영역으로 보낸다.

Survivor 0 / Survivor 1

  • 최소 1번 이상의 GC를 살아남은 객체가 존재하는 영역
  • Survivor 영역에는 특별한 규칙이 있는데, Survivor 0 또는 Survivor 1 둘 중 하나에는 꼭 비어 있어야 하는 것이다.
  • Survivor 영역의 제한 조건으로 Survivor 영역 중 반드시 1개는 사용되어야 하고, 나머지는 비어 있어야 한다. 만약 두 Survivor 영역에 모두 데이터가 존재하거나, 모든 사용량이 0이라면 현재 시스템이 정상적인 상황이 아니라는 반증이 된다.

Old 영역 (Old Generation)

  • Young 영역에서 Reachable 상태를 유지하여 살아남은 객체가 복사되는 영역
  • Young 영역보다 크게 할당되며, 영역의 크기가 큰만큼 가비지는 적게 발생한다.
  • Old 영역에 대한 가비지 컬렉션을 Major GC 또는 Full GC라고 부른다.

Minor GC

Young Generation 영역에서 발생되는 GC를 Minor GC라 부른다. Young Generation의 공간은 Old Generation에 비해 상대적으로 작기 때문에 메모리 상의 객체를 찾아 제거하는 데에는 적은 시간이 걸린다.

  1. 처음 생성된 객체는 Young Generation 영역의 일부인 Eden 영역에 위치한다.
  2. 객체가 계속 생성되어 Eden 영역이 꽉차게 되고 Minor GC가 실행된다.
  3. Mark 동작을 통해 reachable 객체를 탐색한다.
  4. Eden 영역에서 살아남은 객체의 1개의 Survivor 영역으로 이동한다.
  5. Eden 영역에서 사용되는 객체(unreachable)의 메모리를 해제(sweep)
  6. 살아남은 모든 객체들은 age 값이 1씩 증가
  7. 또 다시 Eden 영역에 신규 객체들로 가득 차게 되면 다시 한 번 Minor GC가 발생하고 mark한다.
  8. Marking 한 객체들은 비어있는 Survivor 1로 이동하고 Sweep
  9. 다시 살아남은 모든 객체들은 age가 1씩 증가
  10. 반복

age

Survivor 영역에서 객체가 살아남은 횟수를 의미하는 값이며, Object Header에 기록된다. 만일 age 값이 임계값에 다다르면 Promotion(Old 영역으로 이동) 여부를 결정한다.

Major GC

Major GC는 객체들이 계속 Promotion 되어 Old 영역의 메모리가 부족해지면 발생하게 된다.

  1. Survivor 영역의 객체의 age가 임계값에 도달하게 된다.
  2. 이 객체들은 Old Generation으로 이동된다. => promotion
  3. 위의 과정이 반복되어 Old Generation 영역의 공간(메모리)가 부족하게 되면, Old 영역에 있는 모든 객체들을 검사하여 참조되지 않는 객체들을 한꺼번에 삭제하는 Major GC가 발생하게 된다.

Minor GC vs Major GC

Old Generation은 Young Generation에 비해 상대적으로 큰 공간을 가지고 있어, 이 공간에서 메모리상의 객체 제거에 많은 시간이 걸리게 된다.

예를 들어 Young 영역은 일반적으로 Old 영역보다 크기가 작기 때문에 보통 0.5초에서 1초 사이에 끝난다. 그렇기 때문에 Minor GC는 애플리케이션에 크게 영향을 주지 않는다.
하지만, Old 영역의 Major GC는 일반적으로 Minor GC보다 시간이 오래걸리며, 10배 이상의 시간을 사용하고, 여기서 Stop-The-World 문제가 발생하게 된다.

객체 지향 프로그래밍의 특징 4가지를 설명해주세요

추상화, 캡슐화, 상속, 다형성

추상화

  • 객체들이 공통적으로 필요한 속성이나 동작을 하나로 추출하는 작업
  • 불필요한 부분은 숨기고, 중요한 부분에 중점을 두어 개략화하는 것

구현
추상 클래스 (abstract class) 와 interface로 구현할 수 있다.

기능
공통된 기능을 다시 정의할 필요가 없기에 코드 중복이 줄고, 코드 재사용이 용이해진다.

상속

  • 자식 클래스와 부모 클래스를 두어 자식 클래스가 부모 클래스의 속성과 메서드를 물려받는 것
  • 기존 클래스를 재활용하여 새로운 클래스를 작성하는 것

구현
1. extends 키워드를 통하여 부모 클래스를 상속받을 수 있다.
2. super 을 통해 부모 클래스를 참조하거나 호출할 수 있다.

기능

  • 클래스들 간 공유하는 속성과 기능들을 반복적으로 정의할 필요없이 딱 한 번만 정의해두고 간편하게 재사용할 수 있어 반복적인 코드를 최소화하고, 공유하는 속성과 기능에 간편하게 접근할 수 있다.

캡슐화

  • 서로 연관있는 속성과 기능들을 하나의 캡슐로 만들어 외부로부터 데이터를 보호하는 것
  • 외부로부터 데이터를 보호하기위해 접근에 제한을 둔다.

구현
1. 접근제어자로 구현할 수 있다.
2. Getter/Setter : 접근 경로를 제한한다는 점에 캡슐화의 방법, 개발자가 여러 명일 수 있는데, 그냥 public으로 풀어져있으면 다른 개발자가 그냥 접근할 수도 있다. 그걸 방지할 수 있기에 캡슐화의 방법 !
또, Getter/Setter를 어디서 함수 호출을 하는지 호출할 수 있다. 그럼 값이 잘못 수정되거나 접근 됐을 때 추적도 가능하다. => 유지/보수에 도움이 된다.

기능
1. 데이터 보호 : 외부로부터 클래스에 정의된 속성과 기능들을 보호
2. 데이터 은닉 : 내부의 동작을 감추고 외부에는 필요한 부분 노출

다형성

  • 어떤 객체의 속성이나 기능이 상황에 따라 여러 가지 형태를 가질 수 있는 성질
  • 그 맥락에 따라 다른 역할을 수행할 수 있는 특징

구현
1. 메서드 오버로딩 (Method Overloading) : 동일한 함수 이름으로 다른 매개변수 타입, 개수를 받으며 구현할 수 있다.
2. 메서드 오버라이딩 (Method Overriding) : 함수의 구현체를 상속클래스나 구현체에서 재정의할 수 있도록 하는 것이다.

문자열을 리터럴(string = "abcd")로 할당하는 것과 객체(string = new String("abcd"))로 할당하는 방식의 차이가 무엇인가요?

저장 공간의 차이

1. String a = "abcd"; :

저장 공간

  • 리터럴(literal)방식으로 선언할 경우, String constant pool에 저장된다. (String constant pool은 Method 영역에 있다.)

같은 값의 객체가 존재하는 경우

  • 만약 String literal로 생성한 객체가 이미 "String constant pool"에 존재한다면, 해당 객체는 이미 생성되어 있는 String constant pool의 Reference를 참조한다.

2. String b = new String("abcd");

저장공간
new라는 생성자를 통해 생성했기 때문에 Heap 영역에 저장된다.

같은 값의 객체가 존재하는 경우

  • new 연산자로 생성한 String 객체는 같은 값이 String pool에 존재하더라도, Heap영역에 별도로 객체를 생성한다.

성능 비교
문자열 리터럴을 사용하는 것이 new String()을 사용하는 것보다 성능이 더 좋다.

스터디를 하면서 알게 된 지식 보충

객체로 할당한 문자열은 문자열 상수 풀에 저장될 수 없나?

String str = new String("Hello World!").intern(); // String pool에 강제로 넣음.
  • intern()은 문자열 상수 풀에 해당 문자열이 있으면 기존 상수풀에 있는 레퍼런스 값을 반환하고, 없으면 새롭게 문자열 상수 풀에 저장한 후 레퍼런스를 반환한다.
  • 특정 문자열을 강제로 문자열 상수 풀에 넣기 때문에 문자열 상수풀이 사용하는 메모리가 계속해서 커집니다.
  • intern()을 사용하는 경우 문자열 상수 풀의 크기가 계속 커지며, 메모리가 부족해질 수 있습니다. 문자열 상수 풀은 보통 GC 대상이 되지 않고, 애플리케이션이 종료될 때까지 메모리에 남아있습니다. 즉, 메모리 누수 가능성을 높이는 메서드이므로 사용하지 않는 것이 좋습니다.

String constant pool vs constant pool

0개의 댓글