Java 기술 면접 분석
기술 면접 대비 핵심 질문
카테고리: Java
Q. JVM의 구조와 메모리 영역에 대해 설명해주세요.
핵심 답변
JVM은 자바 애플리케이션을 OS에 구애받지 않고 실행할 수 있게 해주는 가상 머신입니다. 구조는 크게 클래스 로더, 실행 엔진, 가비지 컬렉터, 런타임 데이터 영역으로 나뉩니다. 런타임 데이터 영역 중 공유되는 공간으로는 언제나 접근 가능한 Method Area와 객체들이 할당되는 Heap Area가 있고, 스레드마다 개별적으로 생성되는 공간으로는 Stack, PC Register, Native Method Stack이 있습니다.
꼬리 질문
- Q. 가비지 컬렉션(GC)의 원리와 자주 쓰이는 GC 알고리즘에 대해 설명해주세요.
- A: 동적으로 할당된 메모리 영역 중 사용되지 않는 영역(unreachable)을 탐지하여 해제합니다. 현대 자바버전에서는 보통 G1 GC나 ZGC가 많이 쓰이며 레거시는 CMS, Parallel GC를 씁니다.
- Q. Java 8 이후 메모리 구조에서 달라진 점이 있다면 무엇인가요?
- A: Method Area에 존재하던 PermGen 영역이 완전히 제거되고 Native 메모리를 사용하는 Metaspace로 대체되었습니다.
Q. JVM의 구조를 설명해주세요.
핵심 답변
JVM(Java Virtual Machine)은 자바 바이트코드를 OS에 맞게 해석해 실행하는 가상 머신입니다. 크게 3가지 요소로 구성됩니다. 첫째, .class 파일을 읽는 클래스 로더(Class Loader), 둘째, 읽은 명령어를 컴파일 및 해석해 실행하는 실행 엔진(Execution Engine - JIT 등), 셋째, 런타임 시에 프로그램 데이터를 저장하는 메모리인 **런타임 데이터 영역(Runtime Data Area)**입니다.
꼬리 질문
- Q. JIT(Just-In-Time) 컴파일러의 역할은 무엇인가요?
- A: 기존 자바의 인터프리터 방식(한 줄씩 읽어 실행)이 느리다는 단점을 극복하기 위해, 자주 실행되는 바이트 코드를 통째로 기계어로 변환해 캐싱해두고 직접 CPU에서 실행되도록 속도를 비약적으로 끌어올리는 엔진입니다.
Q. volatile 키워드는 무엇인가요?
핵심 답변
자바에서 스레드들은 변수값을 읽을 때 CPU 캐시(Cache)에 먼저 접근해 성능을 확보합니다. 그러나 volatile로 선언된 변수는 모든 읽기와 쓰기 작업을 CPU 캐시 메모리가 아니라 공유 메인 메모리(RAM)에 직접 읽고 쓰겠다고 선언하는 키워드입니다. 이는 스레드 간 '가시성(Visibility - 내 변경 결과를 당장 전부 볼 수 있음)' 문제를 해결해줍니다.
꼬리 질문
- Q. 그렇다면 volatile 하나만으로 원자성(Atomicity, 무결한 동기화)도 완벽히 보장되나요?
- A: 불가능합니다.
volatile은 메모리 접근 가시성만 맞춰줄 뿐이지 스레드 다수가 동시에 동일한 변수에 계산(연산 증가)을 일으키면 연산 충돌(Race Condition)을 방어해주지 않습니다. 무결한 연산을 보장하려면synchronized나 Atomic(java.util.concurrent.atomic) 객체를 필수적으로 활용해야 합니다.
- A: 불가능합니다.
Q. Thread와 Runnable 차이는?
핵심 답변
Thread는 자바에서 스레드를 구동하기 위해 상속받아야 하는 독립적인 **클래스(Class)**이고, Runnable은 단 하나의 run() 메서드 뼈대만 가지는 **인터페이스(Interface)**입니다. 자바는 다중 상속이 불가능하므로, 만일 내 클래스가 이미 다른 특성 클래스를 상속받고 있다면 단일 상속 제약에 걸리는 Thread 대신 Runnable '인터페이스 구현' 방식을 채택해 무리 없이 다른 기능과 다중 호환될 수 있는 유연성을 챙깁니다.
꼬리 질문
- Q. Thread의 start() 대신 run() 메서드를 직접 호출하면 어떻게 되나요?
- A: 새로운 병렬 스레드가 OS로부터 생성되지 않고, 단순히 메인 스레드가 일반 메서드를 동기로 실행하는 것과 똑같이 끝나버리기 때문에 멀티스레딩이 무의미해집니다.
start()를 반드시 호출해야 JVM이 네이티브 스레드를 할당해run을 백그라운드로 진입시킵니다.
- A: 새로운 병렬 스레드가 OS로부터 생성되지 않고, 단순히 메인 스레드가 일반 메서드를 동기로 실행하는 것과 똑같이 끝나버리기 때문에 멀티스레딩이 무의미해집니다.
Q. 멀티스레드 환경에서의 문제점은?
핵심 답변
가장 큰 문제는 동일한 변수(자원)를 여러 스레드가 무분별하게 조작하며 내부 메모리가 뒤섞여 데이터가 논리적으로 심하게 깨지는 **가시성 단절 및 경쟁 상태(Race Condition)**입니다. 이를 막기 위해 락 동기화를 무분별하게 걸다 보면 서로 키를 내주지 않고 멈춰버리는 **데드락(교착 상태)**에 직면하는 고착 문제에 빠지기가 매우 쉽습니다.
꼬리 질문
- Q. 어떻게 해결하나요?
- A:
synchronized나ReentrantLock으로 무결한 임계 영역을 차단 구축하거나, 락 대기가 없는Atomic연산 클래스 및ConcurrentHashMap과 같은 스레드 동시성 전용 자료구조를 적극적으로 사용하는 것이 가장 이상적입니다.
- A:
Q. JDK, JRE, JVM의 차이는?
핵심 답변
세 가지는 포함 관계를 가집니다. JVM은 단순히 바이트코드를 실행하는 엔진의 규격입니다. **JRE(Java Runtime Environment)**는 JVM에 더해 자바 구동에 필요한 기본 코어 클래스 라이브러리와 환경을 합친 '자바 실행 환경'입니다. **JDK(Java Development Kit)**는 JRE에 더해 Javac(컴파일러), jdb 등의 '개발 도구'까지 모두 포함된 풀 패키지입니다.
꼬리 질문
- Q. 자바 프로그램을 실행만 한다면 JDK가 필요한가요?
- A: 아닙니다. 코드를 빌드하고 컴파일 할 게 아니라 단순히 컴파일된
.class나.jar파일을 실행만 하는 서버(운영 환경)라면 JRE 환경만으로도 충분합니다.
- A: 아닙니다. 코드를 빌드하고 컴파일 할 게 아니라 단순히 컴파일된
Q. Java의 메모리 구조는 어떻게 구성되나요?
핵심 답변
JVM 런타임 데이터 영역은 5 파트로 나뉩니다. 모든 스레드가 공유하는 영역은 클래스의 메타데이터와 정적(static) 변수가 저장되는 **메서드 영역(Method Area)**과 동적 생성 객체가 저장되는 힙 영역(Heap Area) 두 가지입니다. 스레드마다 개별 할당되는 영역은 메서드 호출 스택인 JVM 스택(Stack Area), 그리고 PC 레지스터와 네이티브 메서드 스택 영역이 있습니다.
꼬리 질문
- Q. 상수 풀(Constant Pool)은 메모리 구조 중 어디에 위치하나요?
- A: 메서드 영역(Method Area) 내에 런타임 상수 풀 영역으로 존재하여 클래스와 인터페이스의 모든 상수 정보를 보관합니다.
Q. GC는 어떻게 동작하나요?
핵심 답변
가비지 컬렉터(GC)는 Heap 영역 내에서 참조를 잃은 Unreachable 객체들을 판별해 메모리를 스스로 회수합니다. 보통 JVM Heap은 물리적으로 Young Generation과 Old 세대로 나뉘며, 새로 생성된 객체는 Young에서 빠르게 Minor GC로 해소시키고, 오랫동안 살아남은 객체는 Old로 승격(Promotion)시켜 덜 자주 일어나는 Major GC로 일괄 스윕(Mark-and-Sweep)하는 방식으로 오버헤드를 최적화합니다.
꼬리 질문
- Q. Stop-the-world란 무엇인가요?
- A: GC를 실행하기 위해 JVM이 애플리케이션의 실행을 즉시 모든 스레드에서 멈추는(Stop) 상태를 뜻합니다. GC 성능 튜닝의 핵심은 이 중단 시간을 최소화하는 것입니다.
Q. String, StringBuilder, StringBuffer 차이는?
핵심 답변
가장 큰 차이는 '불변성(Immutability)'과 '동기화' 유무에 있습니다. String은 불변성을 가져한 번 생성되면 수정이 불가능해 + 연산시마다 새로운 주소의 객체를 계속 버리며 만들기 때문에 성능상 좋지 못합니다.
반면 StringBuilder와 StringBuffer는 가변성 객체이므로 문자열 변경 시 자체 버퍼만을 수정하므로 빠릅니다. StringBuffer는 멀티 스레드에 안전(동기화 보장)하지만 단일 스레드에서는 무겁고, StringBuilder는 동기화 되지 않아 단일 스레드에서는 제일 빠릅니다.
꼬리 질문
- Q. 최근 Java 버전에선
String s = "a" + "b"같은 작업이 어떻게 최적화되나요?- A: Java 1.5 이후부터는 컴파일러가 단순한 String의
+더하기 연산을 내부적으로StringBuilder의.append()메서드로 변환해 처리해주도록 자동 최적화되었습니다.
- A: Java 1.5 이후부터는 컴파일러가 단순한 String의
Q. equals()와 hashCode()의 관계는?
핵심 답변
두 메서드 모두 객체의 동등성 비교를 위해 쓰입니다. equals()가 두 객체를 논리적으로 동일하다고 판단했다면(true 반환), 두 객체의 hashCode() 반환값은 무조건 똑같아야 한다는 자바의 규약이 있습니다. 반대로 해시코드가 같더라도 equals()는 둘을 다르게 판별할 수 있습니다(해시 충돌 현상).
꼬리 질문
- Q. 왜 equals()를 재정의(Override)할 때는 hashCode()도 항상 같이 재정의해야 하나요?
- A:
HashMap이나HashSet같이 해시 기반 컬렉션은 객체를 비교할 때 먼저hashCode()를 비교한 뒤equals()를 이용해 2차 대조하기 때문입니다.hashCode()를 오버라이딩 하지 않으면 동등한 객체여도 컬렉션 입장에서는 아예 완전히 다른 버킷 주소 취급을 하게 되어 키를 잃어버리는 치명적 버그가 발생합니다.
- A:
Q. 컬렉션 프레임워크 구조를 설명해주세요.
핵심 답변
자바 컬렉션 프레임워크는 대량의 데이터를 효율적으로 관리할 수 있도록 스탠다드 API 자료구조들을 제공합니다. 최상위에는 데이터 추가와 탐색을 위한 Collection 인터페이스가 있고, 그 아래에 값의 순서를 보장하는 List(ArrayList 등), 값의 중복을 쳐내는 Set(HashSet 등) 인터페이스가 상속됩니다.
참고로 Map(HashMap 등)은 Key-value 쌍을 위한 독립 인터페이스로 Collection 계열을 상속받지는 않지만 프레임워크에 통칭 포함시킵니다.
꼬리 질문
- Q. Vector와 ArrayList의 차이는?
- A: 두 기능은 거의 동일하지만
Vector는 레거시 클래스로, 모든 메서드에synchronized가 걸려있어 단일 스레드에서는 성능이 매우 느립니다. 이를 최신화하여 스레드 동기화를 빼 속도를 올린 것이ArrayList입니다.
- A: 두 기능은 거의 동일하지만
Q. List, Set, Map 차이는?
핵심 답변
- List: 순서 유지 보장, 중복 값 허용 O (배열, 연결 리스트 등)
- Set: 순서 유지 불가, 중복 값 허용 X (해시 셋, 트리 셋)
- Map: Key와 Value 쌍 지어 저장. 입력 순서 보장 불가, 다만 Key의 중복은 절대 불용납하고 Value 값만 중복 허용 O (해시 맵 등)
꼬리 질문
- Q. Set에서 요소 순서를 입력한 순서 그대로 보장받고 싶다면 어떤 클래스를 쓰나요?
- A:
LinkedHashSet을 사용하면 내부적으로 링크드 리스트가 추가로 체이닝을 구성하므로 입력된 순서대로의 삽입 및 반복(Iteration)이 보장됩니다.
- A:
Q. synchronized 키워드는 어떻게 동작하나요?
핵심 답변
synchronized는 멀티 스레드 환경에서 한 스레드가 임계 구역(메서드나 동기화 블록)에서 공유 데이터를 수정하는 동안, 다른 훼방꾼 스레드들이 접근하지 못하도록 해당 객체 단위에 락(Lock) 메커니즘인 '모니터(Monitor) 락'을 투척해 직렬성을 보장하는 키워드입니다.
꼬리 질문
- Q. synchronized로 도배할 때 일어날 수 있는 성능 저하 원인은?
- A: 모든 공유 자원 접근이 철저하게 줄을 서서 1명씩 처리되므로 병목 병목현상이 일어나 런타임 속도가 대폭 하락하게 되며, 스레드 간 서로 락을 잡고 놓아주지 않게 되는 데드락(Deadlock)에 걸릴 확률이 커지게 됩니다.