구직활동을 하느라 블로그에 너무 소홀했다.
기술 면접은 준비하면 할수록 끝이 없지만 그만큼 또 배우는 것도 많은 것 같다.
(특히 꼼꼼하게, 디테일하게 질문해주시는 면접관님들 감사합니다!)
나도 많은 블로그/깃허브의 도움을 받았기에 내가 정리한 부분도 공유해본다.
Q. 프로그램을 메모리에 로드할 때, 사용되는 메모리 내부 영역을 크게 코드영역, 데이터영역, 스택영역, 힙영역으로 나뉨
코드 영역
프로그램의 실행 코드가 저장되는 영역
CPU는 코드 영역에 있는 명령들을 하나씩 가져와 실행
일반적으로 읽기 전용이기 때문에 프로그램이 실행되는 동안 그 내용이 바뀌지 않음
데이터 영역
초기화된 전역 변수와 정적 변수가 이 영역에 저장
예를 들어, int global_var = 10;과 같은 전역 변수
프로그램의 시작과 동시에 할당되며, 프로그램이 종료될 때까지 유지
스택 영역
지역 변수, 매개변수, 리턴 주소 등이 저장되는 영역
함수 호출 시 함수의 반환 주소와 매개변수가 스택에 푸시되고, 함수가 종료될 때 팝되면서 메모리에서 해제
스택은 LIFO (Last-In-First-Out) 방식으로 동작
힙 영역
동적 메모리 할당을 위한 영역
malloc(), calloc(), new 등의 함수를 통해 동적으로 할당받은 메모리는 힙 영역에 위치
스레드 간 공유가 가능 -> 할당된 메모리를 다른 스레드가 참조하거나 사용
Q. 스레드 공유시 동기화를 통해 데이터 경쟁 상황을 피해야 함
-> 어떻게?
-> 뮤텍스: 한 번에 하나의 스레드만 공유 자원에 접근할 수 있도록 하는 잠금 메커니즘, 스레드가 뮤텍스를 획득하면 해당 스레드만 해당 공유 자원에 접근할 수 있으며, 다른 스레드는 대기 상태
-> 세마포어: 뮤텍스와 유사하지만, 동시에 여러 스레드가 자원에 접근할 수 있도록 허용하는 일반화된 메커니즘, 카운터를 사용하여 몇 개의 스레드가 동시에 자원에 접근할 수 있는지를 제한
Q. 데드락은 왜 발생?
여러 스레드나 프로세스가 서로를 대기하면서 아무 것도 진행되지 않는 상태
네가지 조건이 동시에 만족될 때 발생한다.
- 상호 배제 : 한 번에 한 스레드만이 자원을 사용할 수 있습니다. 다른 스레드는 그 자원을 사용하려면 대기해야 합니다.
- 점유와 대기: 스레드는 이미 할당된 자원을 가지고 있으면서 다른 자원을 기다리는 상황이 발생
- 비선점: 스레드가 일단 자원을 점유하면, 그 스레드가 자원을 자발적으로만 반납할 수 있으며, 다른 스레드나 운영 체제가 강제로 그 자원을 빼앗을 수 없습니다.
- 순환대기: 두 개 이상의 스레드나 프로세스가 자원을 순환적으로 기다리는 상황이 발생합니다. 예를 들어, 스레드 A는 자원 1을 보유하고 있으면서 자원 2를 기다리고, 스레드 B는 자원 2를 보유하고 있으면서 자원 1을 기다리는 상황입니다.
Q. 스택 프레임
프로그램이 함수를 호출할 때 생성되는 메모리 블록을 의미합니다. 각 함수 호출마다 자신만의 스택 프레임이 생성되며, 이는 호출한 함수가 반환될 때까지 스택에 유지됩니다. 스택 프레임은 주로 스택 영역에 위치하고, 여러 정보를 포함할 수 있습니다.
스택 프레임은 일반적으로 다음과 같은 섹션으로 구성됩니다:
- 로컬 변수 (Local Variables): 함수 내부에서 선언된 지역 변수가 저장되는 영역입니다.
- 리턴 주소 (Return Address): 함수가 종료된 후에 실행을 계속할 프로그램의 주소입니다.
- 이전 스택 프레임의 베이스 포인터 (Old Base Pointer): 이전 함수의 스택 프레임의 베이스 주소를 가리킵니다. 이 정보는 현재 함수가 종료되고 나면 스택 포인터를 원래 위치로 되돌리는 데 사용됩니다.
- 매개변수 (Arguments or Parameters): 함수에 전달된 인자값이 저장되는 영역입니다.
이러한 정보들이 스택 프레임에 저장되는 방식은 언어와 컴파일러, 운영체제에 따라 다를 수 있습니다. 일반적으로 함수가 호출될 때 스택 포인터가 이동하며 새로운 스택 프레임이 생성되고, 함수가 반환되면 해당 스택 프레임은 해제됩니다.
스택 프레임은 프로그램이 실행되는 동안 다음과 같은 작업에 중요하게 사용됩니다:
- 함수 호출과 복귀: 각 함수 호출은 자신의 스택 프레임을 갖게 되며, 함수가 종료되면 해당 스택 프레임을 통해 복귀 주소를 찾아 원래 위치로 돌아갑니다.
- 변수 스코프 및 지속성: 지역 변수는 해당 함수의 스택 프레임 내에서만 유효하며, 함수가 종료되면 이 변수들은 메모리에서 해제됩니다.
- 재귀 호출: 재귀 함수도 각 호출마다 새로운 스택 프레임을 생성합니다. 이를 통해 이전 호출의 상태를 유지할 수 있습니다.
스택 프레임은 함수 호출과 관련된 모든 정보를 캡슐화하므로, 프로그램의 실행 흐름을 이해하고 디버깅하는 데 매우 중요한 역할을 합니다.
Q. 스택 오버플로우
스택 메모리 영역에 할당된 공간을 초과하여 데이터를 쓸 때 발생하는 실행 시간 오류
만약 프로그램이 스택에 너무 많은 데이터를 푸시하려고 하면, 스택의 용량을 초과하게 되어 스택 오버플로우가 발생합니다.
주요 원인:
- 과도한 재귀 호출: 재귀 함수가 종료 조건 없이 무한히 자신을 호출하거나, 종료 조건이 너무 늦게 도달되는 경우에 스택 오버플로우가 발생할 수 있습니다. 각 함수 호출마다 스택에 새로운 스택 프레임이 추가되기 때문입니다.
- 지역 변수의 크기가 너무 큰 경우: 예를 들어, 매우 큰 배열을 함수 내의 지역 변수로 선언하는 경우 스택의 할당된 공간을 초과할 수 있습니다.
-> 재귀 함수의 종료 조건을 적절히 설정하고, 함수 내부에서 큰 메모리를 할당할 때는 힙 영역을 사용하는 것이 좋습니다.
Q. 실제 숫자 0.3+0.6 =0.9 인데 컴퓨터에서는 그렇지 않다. 왜?
컴퓨터가 소수를 이진(binary) 형태로 표현하기 때문
예를 들어, 0.1은 이진수로 정확하게 표현될 수 없습니다. 따라서 근사값으로 표현되게 되며, 이로 인해 오차가 발생
여러 소수 연산을 수행할 때마다 오차가 누적될 수 있습니다. 예를 들어, 두 소수를 더하면 결과가 더욱 정확하지 않을 수 있습니다.
Q. 부동소수점
실수를 컴퓨터에서 근사적으로 표현하는 방식
소수점의 위치가 고정되어있지 않고 "부동"하는 방식으로 숫자를 표현
큰 범위의 실수 값을 표현할 수 있습니다
구성요소:
- 부호(Sign): 숫자가 양수인지 음수인지를 나타냅니다.
- 가수(Mantissa 또는 Significand): 숫자의 실제 값을 표현합니다.
- 지수(Exponent): 소수점의 위치를 조정합니다.
Q. 64비트 - 32비트 차이점
CPU의 레지스터 크기는 해당 CPU가 한 번에 처리할 수 있는 데이터의 양을 결정합니다.
64비트 CPU는 64비트 레지스터를 사용하여 한 번에 64비트의 데이터를 처리할 수 있습니다.
반면 32비트 CPU는 32비트 레지스터를 사용하여 한 번에 32비트의 데이터만 처리할 수 있습니다.
Q. string a = “hello”, string b= “world”, c = a+b, JVM 내에서 내부 메모리 할당과 해제에 관련해서 어떻게 동작하는지?
c = a + b 연산을 수행할 때, a와 b의 문자열이 연결되어 새로운 문자열 객체가 생성되며, 이는 힙 메모리에 저장됩니다. 이후 해당 문자열에 대한 참조가 없어지면, 가비지 컬렉터에 의해 메모리가 해제됩니다.
Q. Java의 String 타입이 immutable한 이유는?
- 문자열이 불변하면, 한 번 생성된 문자열을 캐시하여 재사용할 수 있습니다. 예를 들어, String 리터럴은 자바에서 풀(pool)로 관리되어 메모리 사용을 최적화합니다.
- 여러 참조가 동일한 문자열 객체를 공유하고 있을 때, 문자열이 변경되면 예기치 않은 부작용이 발생합니다. immutable 하기 때문에 무결성이 보장됩니다.
- 문자열이 불변하면 한 번 계산된 해시코드를 캐시하여 재사용할 수 있습니다. 이로 인해 String의 hashCode() 메서드 호출은 매우 빠르게 작동합니다.
Q. string a = “hello”는 어느 영역에 저장?
- Java에서 문자열 리터럴은 String Pool이라는 특별한 영역에 저장됩니다. 만약 동일한 문자열 리터럴이 이미 String Pool에 존재한다면, 새로운 객체를 만들지 않고 기존의 참조를 재사용
- a는 참조 변수로 힙 영역에 있는 String 객체를 가리킴, a는 String 객체의 참조를 저장, 실제 문자열 데이터는 String pool에
- a라는 참조변수 자체는 실행중인 메서드의 스택 프레임 내에서 공간을 차지
Q. 해시함수에 대해서 설명
해시 함수는 어떤 입력값을 받아서 고정된 크기의 문자열을 반환하는 함수입니다. 이 반환된 값을 '해시값' 또는 '해시 코드'라고 합니다. 해시 함수의 주된 특징은 동일한 입력에 대해서는 항상 동일한 해시값을 반환하지만, 아주 작은 입력의 차이라도 다른 해시값을 반환한다는 것입니다. 이를 활용하여 데이터의 저장 및 검색을 효율적으로 수행하는 해시 테이블에 사용됩니다.
Q. 자바 자료구조 Sorted Map 구현체 -> TreeMap
"SortedMap"은 키를 특정 순서대로 정렬하여 저장하는 맵(Map)입니다. Java의 표준 라이브러리에서는 TreeMap이 이를 구현합니다.
TreeMap은 키를 기준으로 정렬된 순서를 유지하고, 키에 대한 자연 순서 또는 제공된 Comparator에 따라 키를 정렬합니다.
firstKey(), lastKey(), headMap(), tailMap(), subMap() 등의 메서드를 통해 정렬된 맵의 부분 집합에 접근할 수 있습니다.
시간 복잡도:
- get(), put(), remove(): O(log n)
- containsKey(), containsValue(): O(log n) (키의 경우), O(n) (값의 경우)
Q. string타입의 key, value를 저장하는 hash map에 n개의 키가 있고, 1개의 key를 get할때 평균 시간 복잡도
-> O(1), 최악의 경우, get 연산의 시간 복잡도는 O(n)
-> 근거는?
-> 해시 충돌이 최소화되어 데이터가 균일하게 분포되어 있을 때는 O(1)
-> 모든 키가 동일한 해시 값을 갖는 경우, 해시 충돌은 두 개 이상의 다른 입력값들이 동일한 해시 값을 가지게 될 때 발생하는 현상 -> O(n)
Q. 제곱근 구하는 함수인데 정수 부분만 구해야 함
-> Math.sqrt()
-> Math 사용을 하지 않는다면 이진 검색
중간 값을 구해서 반복
-> 시간 복잡도 log2(밑수)n
'💻dev > 🖥️CS' 카테고리의 다른 글
디자인 패턴 | 전략 패턴 (Strategy Pattern) (0) | 2023.10.19 |
---|---|
SQL | 데이터베이스에서의 NULL 값 관리: COALESCE 함수 사용하기 (0) | 2023.09.22 |
CS | CPU 스케줄러와 스케줄링 알고리즘 (0) | 2023.08.12 |
CS | Design Pattern (디자인패턴) (0) | 2023.08.12 |
CS | Operating System (운영체제) (0) | 2023.08.12 |