객체지향적 프로그램이 무엇인가요?
객체 지향 프로그래밍 (Object-Oriented Programming, OOP)은 프로그래밍에서 필요한 데이터를 추상화 시켜 상태와 행위를 가진 객체로 만들고, 객체들간의 상호작용을 통해 로직을 구성하는 프로그래밍 방법입니다. 객체 지향 특징은 크게 4가지로 추상화, 캡슐화, 상속, 다형성이 있습니다.
절차지향 : 일이 진행되는 순서대로 프로그래밍
객체 지향의 특징 4가지에 대해서 설명해주세요
[추상화]
객체에서 공통된 속성과 행위를 추출하여 타입을 정의하는 과정을 의미합니다. 즉, 불필요한 정보는 숨기고 중요한 정보만을 표현함으로써 프로그램을 간단하게 만드는 것을 말합니다.
[캡슐화]
필드와 메서드를 하나로 묶는 것을 의미하며 접근 제어자로 데이터 보호 및 은닉을 가능하게 합니다.
[상속]
상위 클래스의 속성과 행위를 물려받는 것을 말합니다.
[다형성]
하나의 변수명, 함수명이 상황에 따라 다른 의미로 해석될 수 있는 것을 의미하며 오버라이딩과 오버로딩이 있습니다.
오버라이딩과 오버로딩에 대해 설명해주세요
[오버라이딩]
상위 클래스에 있는 메소드를 하위 클래스에서 재정의 하는 것을 말합니다.
[오버로딩]
매개변수의 개수나 타입을 다르게 하여 같은 이름의 메소드를 여러개 정의하는 것을 말합니다.
객체 지향 프로그래밍의 장단점을 말해주세요
[장점]
상위 클래스를 가져와서 상속을 통한 확장으로 코드 재사용에 용이하며 특정 메서드에 대해 오버라이딩하여 재정의하여 유지보수가 쉽습니다. 또한 클래스 단위로 모듈화 시켜 개발하기 때문에 대형 프로젝트에 적합합니다.
[단점]
객체가 많으면 용량이 커질 수 있고 처리 속도가 상대적으로 느립니다. 또한, 설계시 많은 시간과 노력이 필요합니다.
JAVA는 Call by Value인지 Call by reference인지
Call by value 입니다. 특정 method()에 참조 타입 A를 매개변수로 전달했다고 가정해볼게요.
해당 메서드에서 전달받은 매개변수 A에 new 키워드로 새로 할당했을 때 JVM 메모리에서 새로운 메모리 공간을 할당받고 스택의 변수에는 할당받은 메모리 주소 번지를 가리키고 있습니다. 이후 메소드 호출이 끝난 후 호출한 곳으로 돌아가면 파라미터로 전달했을 때의 이전 메모리 번지를 가리키고 있습니다. Call by reference였다면 레퍼런스 자체를 넘겨주기때문에 호출 지역으로 돌아가도 새로 할당된 메모리 번지를 가리키고 있어야하지만 자바는 콜바이 벨류 방식이기 때문에 레퍼런스 자체가 아닌 메모리 번지를 전달할 뿐입니다.
SOLID 5원칙에 대해서 설명해주세요
[단일책임원칙]
모든 클래스는 각각 하나의 책임만 가져야합니다. 예를 들어 A라는 로직이 존재한다면 어떠한 클래스는 A에 관한 클래스여야하고 이를 수정한다고 했을 때도 A와 관련된 수정이어야합니다.
[개방 폐쇠 원칙]
유지보수 사항이 생긴다면 코드를 쉽게 확장할 수 있도록 하고 수정할 때는 닫혀있어야합니다. 즉, 기존의 코드는 잘 변경하지 않으면서도 확장은 쉽게 할 수 있어야합니다.
[리스코프 치환 원칙]
프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 하는 것을 의미합니다. 클래스는 상속이 되기 마련이고 부모, 자식이라는 계층 관계가 만들어집니다. 이때 부모 객체에 자식 객체를 넣어도 시스템이 문제 없이 돌아가게 만드는 것을 말합니다.
[인터페이스 분리 원칙]
하나의 일반적인 인터페이스보다 구체적인 여러개의 인터페이스를 만들어야하는 원칙입니다.
[의존 역전 원칙]
자신보다 변하기 쉬운 것에 의존하던 것을 추상화된 인터페이스나 상위 클래스를 두어 변하기 쉬운 것은 변화에 영향받지 않게 하는 원칙을 말합니다. 예를 들어 타이어를 갈아끼울 수 있는 틀을 만들어 놓은 후 다양한 타이어를 교체할 수 있어야합니다. 즉, 상위 계층은 하위 계층의 변화에 대한 구현으로부터 독립해야하빈다.
Checked Exception과 Unchecked exception에 대해서 설명해주세요.
Exception(예외)는 프로그램 실행 중에 개발자의 실수로 예기치 않은 상황이 발생했을 때 입니다. 크게 CheckedException과 UncheckedException이 있습니다.
[Checked Exception]
Checked Exception은 RuntimeException의 하위 클래스가 아니면서 Exception 클래스의 하위 클래스들입니다. 체크 예외의 특징은 반드시 에러 처리를 해야하는 특징(try/catch or throw)을 가지고 있습니다. Checked Exception은 컴파일 시점에 예외를 catch 하는지 정적으로 확인합니다. 만일 컴파일 시점에 예외에 대한 처리(try/catch)를 하지 않는다면 컴파일 에러가 발생합니다. (ex. IOException, FileNotFoundException, ClassNotFoundException)
[Unchecked Exception]
Unchecked Exception은 RuntimeException의 하위 클래스들을 의미합니다. 이것은 체크 예외와는 달리 에러 처리를 강제하지 않습니다. 말 그대로 runtime 시점에 발생할 수 있는 예외를 의미합니다.
(ex. ArrayIndexOutOfBoundsException, NullPointerException)
* Error는 시스템에 비정상적인 상황이 발생했을 경우에 발생합니다. 예를들어, OutOfMemoryError나 StackOverFlowError와 같이 복구할 수 없는 것.
Array와 ArrayList 차이점에 대해서 설명해주세요
배열부터 말씀드리자면, 배열은 크기가 고정되어있고 원시 타입과 참조 타입을 모두 담을 수 있으며 제네릭은 사용할수 없습니다. 어레이 리스트는 사이즈가 동적인 배열이라서 데이터 추가 삭제 시 메모리를 재할당하기 때문에 속도가 배열보다 느립니다. 또한 참조타입만 담을 수 있으며 타입 안정성을 보장해주는 제네릭을 사용할 수 있습니다.
ArrayList의 내부적으로 어떻게 돌아가는지 아시나요?
arrayList의 add 메서드를 들여다본적이 있었는데요.
해당 메서드에서는 elementData Object 배열의 크기와 현재 ArrayList에 들어가있는 객체의 개수를 뜻하는 s 변수와 비교해서 일치한다면 grow 메소드를 호출합니다. 즉, 할당할 수 있는 공간이 부족하니 늘려줘야하는 메소드입니다. grow 메소드 내부에는 elementData Object 배열의 기존 크기에 우측 쉬프트 1칸을 연산한 값을 더해서 1.5배의 크기로 newCapactiy를 만듭니다. 그리고 기존의 배열의 원소들을 새로 할당받은 메모리 공간으로 복사합니다.
제네릭이 무엇인가요?
제네릭은 데이터의 타입을 하나로 지정하지 않고 사용할 때마다 범용적이고 포괄적으로 지정한다는 의미입니다. 제네릭 타입을 사용함으로써 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거할 수 있어 에러를 사전에 방지할 수 있습니다. 즉, 제네릭을 사용하면 타입 안정성을 제공할 수 있으며, 컴파일 타임에 타입 체크를 하기 때문에 런타임에 발생할 수 있는 예외를 사전에 방지할 수 있습니다. 또한, 제네릭을 사용하면 타입체크와 형변환을 생략할 수 있기 때문에 코드가 간결해집니다
자바는 왜 상속 하나만 가능할까요?
다중 상속을 지원하게 되면 하나의 클래스가 여러 상위 클래스를 상속 받을 수 있습니다. 이런 특징 때문에 발생하게 되는 문제가 있는데, 바로 '다이아몬드 문제' 입니다. 예를 들어, GrandFather 클래스가 myMethod() 메서드를 가지고 있고 FatherA와 FatherB가 오버라이딩하여 구현하였다면, FatherA와 FatherB를 모두 상속받은 Son 클래스 입장에선 어떤 myMethod를 사용해야할지 충돌이 생깁니다. 대신 인터페이스는 다중 상속이 가능합니다. 인터페이스는 기능에 대한 선언만 해두면 되기 때문에, 다이아몬드 상속이 되더라도 충돌할 여지가 전혀 없습니다. 그러므로 인터페이스의 경우 다중상속을 통한 문제가 발생하지 않습니다.
인터페이스는 모두 다중 상속이 가능하다는 말씀인가요?
자바 8의 경우 인터페이스 내부에서 구현할 수 있는 default method가 추가되었는데 이런 경우는 마치 class 처럼 다중 상속을 받을 수 없게 됩니다.
상속과 구현의 차이에 대해서 설명해주세요
[상속(extends)]
자식 클래스가 부모 클래스의 메서드 등을 상속받아 사용하며 자식 클래스에서 추가 및 확장을 할 수 있습니다. 이로 인해 재사용성, 중복성의 최소화가 가능합니다. (일반 클래스, 추상 클래스를 기반)
[구현(implements)]
부모 인터페이스를 자식 클래스에서 재정의하여 구현하는 것을 말하며, 상속과 달리 반드시 인터페이스의 메서드를 재정의하여 구현해야 합니다. 즉, 인터페이스를 기반으로 구현합니다
추상 클래스와 인터페이스의 차이에 대해서 설명해주세요
[추상 클래스]
클래스 안에 “추상 메소드”가 하나 이상 포함되거나 abstract으로 정의된 경우를 말합니다. 추상 클래스를 상속(extends) 받아서 기능을 이용하며 확장 시킬 수 있습니다.
[인터페이스]
모든 메서드가 추상 메서드인 경우입니다. 함수의 껍데기만 있으며, 그 함수의 구현(implements)을 강제하기 위해서 사용됩니다.
* 자바 8버전 이후부터는 인터페이스 내부에 default 키워드를 이용해 메소드의 구현도 가능
함수형 인터페이스가 무엇인가요?
추상 메서드가 오직 하나인 인터페이스를 의미합니다. 추상 메서드가 하나라는 뜻은 default method 또는 static method 는 여러 개 존재해도 상관 없다는 뜻입니다. 이 추상 메서드를 어떻게 구현을 하냐면 기존의 방법은 익명 내부 클래스를 만들어 사용을 했지만 자바 8 이후로 람다 표현식을 이용하여 간략하게 구현할 수 있습니다. 그리고 @FunctionalInterface 어노테이션을 사용하는데, 이 어노테이션은 해당 인터페이스가 함수형 인터페이스 조건에 맞는지 검사해줍니다. @FunctionalInterface 어노테이션이 없어도 함수형 인터페이스로 동작하고 사용하는 데 문제는 없지만, 인터페이스 검증과 유지보수를 위해 붙여주는 게 좋습니다.
*자바8은 인터페이스에서 static 메소드의 정의와 구현을 허용.
Synchronize에 대한 설명해주세요
동기화란 프로세스 또는 스레드들이 수행되는 시점을 조절하여 서로가 알고 있는 정보가 일치하는 것을 의미합니다.
멀티스레드를 잘 사용하면 프로그램적으로 좋은 성능을 낼 수 있지만, 멀티스레드 환경에서 반드시 고려해야할 점인 스레드간 동기화라는 문제는 꼭 해결해야합니다. 따라서 data의 thread-safe를 하기 위해 자바에서는 synchronized 키워드를 제공해 스레드간 동기화를 시켜 data의 thread-safe를 가능하게 합니다.
Synchronized 키워드는 변수와 함수에 사용해서 동기화 할 수 있습니다. 하지만 Synchronized 키워드를 너무 남발하면 오히려 프로그램 성능저하를 일으킬 수 있습니다. 그 이유는 Synchronized 키워드를 사용하면 자바 내부적으로 메서드나 변수에 동기화를 하기 위해 block과 unblock을 처리하게 되는데 이런 처리들이 만약 너무 많아지게 되면 오히려 프로그램 성능저하를 일으킬수 있는 것입니다.
Restful API가 무엇인가요?
RESTful API는 자원을 이름으로 구분하여 해당 자원의 상태를 주고 받는 API 디자인 원칙 중 하나입니다. RESTful API는 URI(Uniform Resource Identifier)를 사용하여 자원(Resource)을 명시하고, HTTP Method(GET, POST, PUT, DELETE, PATCH 등)를 사용하여 해당 자원에 대한 CRUD(Create, Read, Update, Delete) 연산을 적용합니다. 이를 통해 클라이언트와 서버 간의 통신이 단순하고 일관성 있게 이루어지며, API의 가독성과 유지보수성이 좋아지는 장점이 있습니다.
해시 함수를 많이 쓰는데, 해시로 데이터를 저장하는 방식과 해시 충돌이 나면 어떻게 처리하나요?
먼저 객체의 해시코드에 대해서 설명하겠습니다. hashcode는 그 객체의 값을 대표합니다. 기본 구현은 객체의 주소에 hash 함수를 적용 시켜 만듭니다. 주소값을 그대로 가져와 쓰는 것이 아니고 사용하기 좋게 변형을 하는 거죠. 기본적으로 Java 환경에서 HashTable의 버킷은 무한하지 않기 때문에 1차적으로 해시코드에 해당하는 버킷 인덱스에 접근하게 됩니다. hashCode() 메서드를 먼저 실행해서 리턴된 hashcode() 값을 확인합니다. 만약 같다면, equals로 다시 비교하면서 선형 탐색이 이루어집니다. 만약 hashcode의 값이 다르면 비교 연산 자체를 하지 않습니다. put 작업이라면 기존 객체를 덮어씌우고 없다면 해당 객체를 LinkedList에 추가합니다. get 일 경우 있다면 그 객체 리턴, 없으면 null 리턴합니다.
즉, hashcode를 확인하여 이미 객체가 존재한다면 충돌된 것이며 해당 버킷에 연결되어있는 객체들을 equals를 통해 값들을 비교합니다. 만약 없다면 체이닝 기술을 통해 링크드 리스트로 추가합니다.
**동일성 = 완전히 같은 것
**동등성 = 다른 객체이지만 가지고 있는 값이 같음.
해시를 사용할 때 equals와 hashcode 왜 오버라이딩 해줘야하나요?
hashcode()를 재정의 하지 않으면 같은 값 객체라도 해시값이 다를 수 있다. 따라서 HashTable에서 해당 객체가 저장된 버킷을 찾을 수 없습니다. 반대로 equals()를 재정의하지 않으면 hashcode()가 만든 해시값을 이용해 객체가 저장된 버킷을 찾을 수는 있지만 해당 객체가 자신과 같은 객체인지 값을 비교할 수 없기 때문에 null을 리턴하게 된다. 따라서 역시 원하는 객체를 찾을 수 없다. Object.equals() 의 원형을 보면 객체의 주소 값을 비교합니다. String.equals의 메소드 구현체를 보면 Object.equals()를 오버라이딩해서 새롭게 정의를 합니다. 객체가 가진 문자열 한 글자씩 비교하여 동일하다면 True를 반환하는 방식으로요. String hashcode는 조금 다른게 String 객체가 가진 주소값이 아니라 문자열에서 한 글자씩 가져와 변환하여 String 객체 간의 동일성을 비교하는 hashCode()를 생성합니다.
참고로 자바는 32비트 운영체제만 사용할 때 개발된 언어라 32비트 주소값을 이용해서 해쉬함수를 만듭니다. 64비트 운영체제의 경우 주소값에 절반만 사용하는데, 알고리즘으로 최대한 겹치지 않게 만들었어도 정말 낮은 확률로 같은 주소값이 2개 나올 수 있다. 주소값이 같으면 당연히 해시코드가 같습니다.
Object.hashcode는 어떻게 정의되어 있나요?
public class Object { public native int hashCode(); // 인터페이스마냥 구현부가 없음.
자바 코드상 Object의 hashCode()는 인터페이스만 정의되어있습니다. 따로 구현체는 존재하지 않고 메서드 앞에 native라는 키워드가 붙여져있습니다. 해당 키워드를 사용하면, 외부에서 다른 언어로 컴파일된 네이티브 메서드를 사용할 수 있는데 자바의 hashCode() 메서드는 운영체제에 작성되어 있는 메서드 (보통 C언어로 작성)를 이용해 hashcode 메서드를 가져와 사용된다.
정적 언어와 동적 언어의 차이에 대해서 설명해주세요
[동적 언어]
프로그램의 타입 검사를 실행 시간(runtime)에 수행하는 언어를 의미합니다. 즉, 변수의 타입이 컴파일 시간(코드를 실행하기 전)이 아니라 프로그램이 실행되는 도중에 결정됩니다. 동적 언어의 대표적인 예로는 Python, JavaScript 등이 있습니다. 이러한 언어들은 코드 작성이 간단하고 유연하며, 런타임 중에 변수 타입을 동적으로 변경할 수 있어 편리한 면이 있지만 동적 타입 검사는 프로그램의 오류를 실행 시점에 발견할 수 있어서 디버깅이 어려울 수도 있습니다.
[정적 언어]
프로그램의 타입 검사를 컴파일 시간(코드를 실행하기 전)에 수행하는 언어를 의미합니다. 즉, 변수의 타입은 코드를 작성하는 동안 명시적으로 선언되며, 컴파일러가 타입을 검사합니다. 정적 언어의 대표적인 예로는 Java, C, C++, 등이 있습니다. 이러한 언어들은 컴파일 시간에 타입 오류를 발견할 수 있어서 프로그램의 안정성을 높이고 디버깅이 용이하지만 변수의 타입을 미리 명시해야 하므로 코드 작성이 상대적으로 번거로울 수 있습니다.
요약하면, 동적 언어는 런타임에 타입이 결정되고, 정적 언어는 컴파일 시간에 타입이 결정됩니다.
리플렉션에 대해서 설명해주세요
리플렉션은 구체적인 클래스 타입을 알지 못하더라도 그 클래스의 메서드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API를 말하며, 컴파일 시간이 아닌 실행 시간에 동적으로 특정 클래스의 정보를 추출할 수 있는 프로그래밍 기법이라 할 수 있습니다. 사용 예시로 IntelliJ의 자동 완성기능이나 스프링 어노테이션이 그 예를 들 수 있습니다.
Java의 main문이 public static void인 이유에 대해서 설명해주세요
[public]
접근 제한이 없고, 자바 프로젝트 내에서 자유롭게 접근할 수 있는 접근제어자입니다.
JVM이 Java 프로그램을 시작하고 main 메서드를 실행하려 하는 시점은 어떤 클래스도 로드 되어 있지 않은 상태입니다.
이때 main 메서드의 접근 제한자가 default, private, protected 으로 되어있다면 클래스 내부에서만 접근을 허용한다던지, 패키지 내부에서만 접근을 허용한다는 뜻이 됩니다. 즉, 접근 제약이 생기게 됨으로 JVM이 초기에 접근 제약을 통과하기 위해서는 public 을 사용해야합니다.
[static]
프로그램이 시작하게 되면 따로 인스턴스화 하지 않아도 프로그램의 시작과 동시에 메모리에 호출이 되어집니다.
static의 특징은 프로그램이 종료되는 시점까지 유지가 되며 new 키워드로 생성한 객체들은 Garbage Collector 의 메모리 청소의 대상이 되지만 static 은 그렇지 않습니다. 즉, main 메소드는 프로그램의 종료되기까지 살아남아 있어야한다는 뜻입니다.
[void]
리턴 값이 없다는 뜻입니다. main에서 void 를 사용하는 것은 프로그램 자체가 종료가 되는 시점에서는 어떠한 반환값도 없어야 하기 때문입니다. 즉, main 메서드가 종료되면 그대로 프로그램도 종료되기 때문에 반환값이 필요하지 않는 void를 사용합니다.
라이브러리와 프레임워크 차이점이 뭘까요?
라이브러리와 프레임워크의 차이는 제어 흐름에 대한 주도성이 누구에게 있는가에 있습니다. 다시 말해, 라이브러리는 라이브러리를 가져다가 사용하고 호출하는 측인 개발자에게 전적으로 주도성이 있고 프레임워크는 그 틀안에 이미 제어 흐름에 대한 주도성이 내재(내포)하고 있습니다. 즉, 전체적인 흐름에 대한 주도권을 프레임워크가 가지고 있습니다.
직렬화와 역직렬화에 대해서 설명해주세요
자바에서 입출력에 사용되는 것은 스트림이라는 데이터 통로를 통해 이동합니다. 이 스트림은 운영체제에 의해 생성되는 가상의 연결 고리를 의미하며, 중간 매개자 역할을 합니다. 하지만 객체는 바이트형이 아니기 때문에 스트림을 통해서 저장하거나 네트워크로 전송하는 것이 불가능합니다. 따라서 객체를 스트림으로 입출력하기 위해서 바이트 배열로 변환하는 것을 직렬화라고 합니다. 반대로 스트림으로 받은 직렬화된 객체를 다시 원래로 돌리는 건 역직렬화라고 말합니다.
serialVersionUID를 선언해야하는 이유가 있을까요?
JVM은 직렬화나 역직렬화를 하는 시점의 클래스에 대해 version 번호를 부여합니다. 그런데 만약 이 시점에서 클래스의 정의가 바뀌게 되면, version 번호도 새롭게 할당해주는데요. 직렬화와 역직렬화의 version 번호가 서로 다르면 안되기 때문에 serialVersionUID를 선언해서 문제를 해결할 수 있습니다. 즉, 클래스 버전이 맞는지 확인하기 위한 용도로 사용된다고 말씀드릴 수 있습니다.
바이트 코드가 무엇인가요?
자바 바이트 코드(Java bytecode)란 자바 가상 머신이 이해할 수 있는 언어로 변환된 자바 소스 코드를 의미합니다. 자바 컴파일러에 의해 변환되는 코드의 명령어 크기가 1바이트라서 자바 바이트 코드라고 불리고 있습니다. 이러한 자바 바이트 코드의 확장자는 .class입니다. 자바 바이트 코드는 자바 가상 머신만 설치되어 있으면, 어떤 운영체제에서라도 실행될 수 있습니다.
Garbage Collection(GC)에 대해서 설명해주세요
프로그램을 개발하다보면 유효하지 않은 메모리인 가비지가 발생하게 됩니다. C언어를 이용하면 free()라는 함수를 통해 직접 메모리를 해제해주어야 하지만 Java는 JVM의 가비지 컬렉터가 불필요한 메모리를 알아서 정리해줍니다. 즉, new 키워드로 할당한 힙 메모리의 영역 중 사용하지 않는 메모리를 청소해줍니다. 대부분의 객체는 금방 접근 불가능 상태 (Unreachable)가 됨으로 한 번 쓰이고 버려지는 객체들 즉, 접근 불가능 상태가 된 객체를 주기적으로 비워줌으로써 한정된 메모리를 효율적으로 사용할 수 있게 해줍니다.
GC의 장점으로는 개발자가 객체를 직접 제거하지 않아도 되므로 메모리 관리가 간편해집니다.
단점으로는 GC 실행 시간을 예측할 수 없고 Major GC가 발생할 경우 Stop The World가 발생합니다.
Java 8과 Java 11의 변화를 알려주세요
[Java 8]
1. 람다 표현식
2. 함수형 인터페이스
3. Default Method
4. Stream
5. Optional Class
6. Default GC : Parallel GC
[Java 11]
1. Default GC : G1 GC (Java 9부터)
2. 람다 파라미터 var 사용
3. String 클래스에 메소드 추가
Java 8과 Java 11의 GC를 설명해주세요
[Parell GC - Java 8]
처음 생성된 객체는 young 영역의 eden영역에 위치하게 됩니다. 그리고 minor gc가 발생하게 되면 사용하지 않는 객체는 메모리에서 제거됩니다. eden 영역에서 살아남은 객체는 young 영역의 survivor 영역으로 이동하게 됩니다. suvivor 영역으노 suvivor1, survivor2로 구성되는데 minor gc가 발생할 때마다 서로간의 영여으로 객체가 이동하게 됩니다. 이 과정에서 참조되지 않는 객체는 메모리에서 제거됩니다. minor gc 가 발생하는 동안 최종적으로 살아남은 객체들은 old영역으로 옮겨지며 old 영역에 있다가 미사용된다고 식별되는 객체들을 full gc를 통해 메모리에서 제거된다. 이때 stop the word가 발생하며 해당 스레드를 제외한 모든 스레드는 중지됩니다.
minor gc에서 살아남은 횟ㅅ수를 기록하는 age bit를 가지고 잇꼬 minor gc가 발생할 때 마다 age bit 값은 1씩 증가하게되며, age bit값이 maxTenuringThreshold라는 설정값을 초과하게 되는경우 old generation영역을 객체가 이동된다. 또는 age bit가 maxtenuringthreshold초과하기 전이라도 suvivor 영역의 메모리가 부족할 경우에는 미리 old generation으로 객체가 옮겨질수 있다.
[G1GC - Java 11]
큰 메모리에서 좋은 성능(짧은 STW)을 내기 힘들었기 때문에 이에 초점을 둔 G1 GC가 등장하게 되었습니다.
G1 GC는 큰 힙 메모리에서 짧은 GC 시간을 보장하는데 그 목적을 둡니다. G1 GC는 Eden, Survivor, Old 영역이 존재하지만 고정된 크기로 고정된 위치에 존재하는 것이아니며, 전체 힙 메모리 영역을 Region 이라는 특정한 크기로 나눠서 각 Region의 상태에 따라 그 Region에 역할(Eden, Survivor, Old)이 동적으로 부여되는 상태입니다. JVM 힙은 2048개의 Region 으로 나뉠 수 있으며, 각 Region의 크기는 1MB ~ 32MB 사이로 지정될 수 있습니다. G1GC는 일시 정지 시간을 줄이기 위해 각 스레드가 자신만의 region을 잡고 작업하는 방식으로 병렬 GC를 수행합니다. G1 GC에서는 그동안 봐왔던 Heap 영역에서 보지 못한 Humongous(휴먼거스), Available/Unused영역 이 존재하며 두 Region에 대한 역할은 아래와 같다. Humongous : Region 크기의 50%를 초과하는 큰 객체를 저장하기 위한 공간이며, 이 Region 에서는 GC 동작이 최적으로 동작하지 않는다. Available/Unused : 아직 사용되지 않은 Region을 의미한다. G1 GC에서 Young GC 를 수행할 때는 STW(Stop-The-World) 현상이 발생하며, STW 시간을 최대한 줄이기 위해 멀티스레드로 GC를 수행한다. Young GC는 각 Region 중 GC대상 객체가 가장 많은 Region(Eden 또는 Survivor 역할) 에서 수행 되며, 이 Region 에서 살아남은 객체를 다른 Region(Survivor 역할) 으로 옮긴 후, 비워진 Region을 사용가능한 Region으로 돌리는 형태 로 동작한다. FULL GC가 수행될 때는 아래의 단계를 거치게 됩니다.
G1 GC에서 Full GC 가 수행될 때는 Initial Mark -> Root Region Scan -> Concurrent Mark -> Remark -> Cleanup -> Copy 단계를 거치게된다.
- Initial Mark
- Old Region 에 존재하는 객체들이 참조하는 Survivor Region 을 찾는다. 이 과정에서는 STW 현상이 발생하게 된ㄷ.
- Root Region Scan
- Initial Mark 에서 찾은 Survivor Region에 대한 GC 대상 객체 스캔 작업을 진행한다.
- Concurrent Mark
- 전체 힙의 Region에 대해 스캔 작업을 진행하며, GC 대상 객체가 발견되지 않은 Region 은 이후 단계를 처리하는데 제외되도록 한다.
- Remark
- 애플리케이션을 멈추고(STW) 최종적으로 GC 대상에서 제외될 객체(살아남을 객체)를 식별해낸다.
- Cleanup
- 애플리케이션을 멈추고(STW) 살아있는 객체가 가장 적은 Region 에 대한 미사용 객체 제거 수행한다. 이후 STW를 끝내고, 앞선 GC 과정에서 완전히 비워진 Region 을 Freelist에 추가하여 재사용될 수 있게 한다.
- Copy
- GC 대상 Region이었지만 Cleanup 과정에서 완전히 비워지지 않은 Region의 살아남은 객체들을 새로운(Available/Unused) Region 에 복사하여 Compaction 작업을 수행한다.
JVM 구조에 대해서 설명해주세요
JVM 구조는 크게 클래스 로더(Class Loader), 런타임 데이터 영역(Runtime Data Area), 실행 엔진(Execution Engine), 그리고 가비지 컬렉터(Garbage Collector)로 구성됩니다.
클래스 로더는 클래스 파일을 로딩하는 역할을 합니다. 해당 클래스 파일들은 런타임 영역에 저장하게 됩니다.
런타임 데이터 영역은메서드 영역, 힙 영역, 스택 영역으로 구성됩니다.
- 메서드 영역은 클래스, 인터페이스, 필드, 메소드 등의 정보를 저장하는 공간입니다.
- 힙 영역은 동적으로 할당하는 메모리 공간으로, 객체들이 생성되는 공간이다. 힙 영역은 GC에 의해 관리되며, 더 이상 참조되지 않는 객체들은 GC에 의해 제거됩니다.
- 스택 영역은 스레드마다 별도로 할당되며, 메소드 호출 시 매개변수, 지역변수, 리턴값 등이 저장되며, 메소드 호출이 완료되면 해당 공간은 해제된다.
- PC Register는 현재 실행 중인 명령어 주소를 저장하는 레지스터로, 각 스레드마다 별도로 할당되며, 스레드가 생성될 때마다 초기화됩니다.
- Native Method는 자바 프로그램에서 C/C++ 등의 외부 언어로 작성된 메소드를 의미하며, 자바에서 실행되는 코드와는 달리, 운영체제나 하드웨어와 같은 외부 자원에 직접 접근할 수 있습니다.
실행 엔진은 런타임 데이터 영역에 저장된 바이트 코드를 기계어로 번역하여 실행하는 역할을 한다. 실행 엔진은 크게 인터프리터(Interpreter), JIT 컴파일러(JIT Compiler) 구성됩니다.
- 인터프리터 : 바이트 코드를 한 줄씩 읽어서 해석하고, 실행하는 역할을 한다. 인터프리터는 빠르게 실행이 가능하지만, 반복적인 실행에서는 성능상 이슈가 있습니다.
- JIT 컴파일러 : 인터프리터의 성능 문제를 해결하기 위해 등장한 기술입니다. 실행 중에 인터프리터가 자주 실행되는 부분을 찾아 미리 기계어로 변환하여 캐시에 저장합니다. 이후에 해당 부분이 다시 실행될 때, JIT 컴파일러에서 미리 변환한 기계어를 실행하여 성능 향상을 이루어냅니다.
JVM의 동작과정 및 원리에 대해서 설명해주세요
- 작성한 자바소스를 컴파일러에게 전달하면, 컴파일러는 해당 소스코드를 분석하여 바이트 코드(Bytecode)로 변환합니다. 바이트 코드는 플랫폼 독립적인 이진 파일로, JVM에서 실행될 수 있는 명령어들의 집합입니다.
- 클래스 로더는 컴파일된 바이트 코드를 JVM에 로딩합니다. 클래스 로더는 클래스 파일을 읽어들이고, 해당 클래스의 정보를 메모리에 로드하여 클래스들 간의 의존성을 관리합니다.
- 로딩된 클래스 파일은 런타임 데이터 영역(Runtime Data Area)에 저장됩니다. 런타임 데이터 영역은 크게 메서드 영역(Method Area), 힙 영역(Heap Area), 스택 영역(Stack Area)으로 구성되며, 각각의 영역은 다른 목적으로 사용됩니다.
- 실행엔진은 로딩된 클래스 파일을 JVM의 런타임 데이터 영역에 저장된 명령어들로 해석하여 실행합니다.
클래스 로더 동작 과정에 대해서 설명해주세요
클래스 로더의 세부 동작은 다음과 같습니다.
- 가장 먼저, 클래스 파일을 가져와서 JVM의 메모리에 로드합니다.
- 그 다음, 자바 언어 명세 및 JVM 명세에 명시된 대로 구성되어 있는지 검사합니다.
- 검사 후에는, 필드, 메서드, 인터페이스 등 메서드클래스가 필요로 하는 메모리를 할당합니다.
- 이후, 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경합니다.
- 마지막으로 클래스 변수들을 적절한 값으로 초기화합니다.
* 심볼릭 레퍼런스
심볼릭 레퍼런스란 참조할 때, 클래스의 특정 메모리 주소를 참조 관계로 구성한 것이 아닌 참조하는 대상의 이름을 지칭합니다. Class 파일이 JVM에 올라가게 되면 심볼릭 레퍼런스는 그 이름에 맞는 객체의 주소를 찾아서 연결하는 작업을 수행합니다. 그러므로, 실제 메모리 주소가 아니라 이름만을 가집니다.
* 다이렉트 레퍼런스로 변경이라는 의미는?
실제 메모리 주소 값으로 변경해 주는 작업을 의미합니다.
JAVA ThreadLocal 이 무엇인가요?
Java ThreadLocal은 스레드 간 공유되지 않는 쓰레드 로컬 변수를 제공하는 클래스입니다. 스레드 로컬 변수는 각각의 스레드에 고유한 값을 저장하므로, 다른 스레드에서는 그 값을 볼 수 없습니다.
ThreadLocal은 한 쓰레드에서 실행되는 코드가 동일한 객체를 사용할 수 있도록 해 줍니다. 즉, ThreadLocal 변수에 저장된 값을 하나의 쓰레드에서 변경하면, 다른 쓰레드에서는 그 값이 변경되지 않습니다. 이는 각 쓰레드마다 별도의 변수를 가지고 있기 때문입니다.
ThreadLocal 내부 구조는 각 쓰레드마다 별도의 Map을 가지고 있습니다. ThreadLocal 변수에 값을 저장하면, 현재 실행 중인 쓰레드를 키(key)로 하여 해당 Map에 값을 저장합니다. 따라서, 같은 쓰레드에서는 저장한 값에 바로 접근할 수 있고, 다른 쓰레드에서는 값에 접근할 수 없습니다.
ThreadPool을 사용하는 경우, 각 쓰레드마다 별도의 Map을 가지고 있기 때문에 ThreadLocal 변수에 값을 저장하고 나서, 해당 쓰레드가 재사용되는 경우 문제가 발생할 수 있습니다. 따라서, ThreadLocal 변수를 사용한 후에는 반드시 remove() 메서드를 호출하여 값을 제거해주어야 합니다. 이렇게 함으로써, 다른 쓰레드에서 해당 값에 접근할 수 없게 되므로 예기치 않은 동작을 방지할 수 있습니다.
jdbc가 무엇인가요
JDBC(Java Database Connectivity)는 자바에서 데이터베이스와 연동하여 데이터베이스를 조작할 수 있도록 해주는 자바 API입니다. JDBC를 사용하면, 데이터베이스에서 데이터를 검색, 삽입, 수정, 삭제 등의 작업을 수행할 수 있습니다.
JDBC API는 자바에서 데이터베이스와 연결하기 위한 인터페이스를 제공하며, 이를 이용하여 다양한 데이터베이스 드라이버를 로드하고, 데이터베이스와의 연결을 수립할 수 있습니다. 연결이 수립된 이후에는 SQL문을 이용하여 데이터베이스를 쿼리하거나 업데이트할 수 있습니다.
JDBC API는 자바에서 가장 널리 사용되는 데이터베이스 접속 API 중 하나이며, 다양한 데이터베이스 제품군을 지원합니다. 이를 통해 자바 애플리케이션에서 다양한 데이터베이스와 연동할 수 있으며, 데이터베이스 관련 작업을 효율적으로 처리할 수 있습니다.
메모리 상수풀 영역에 대해 설명해보세요
메모리 상수풀(Constant Pool) 영역은 자바에서 사용되는 상수 값들을 저장하는 특별한 메모리 영역입니다. 이 영역은 JVM의 메모리 영역 중 하나이며, Heap 영역과는 별도로 관리됩니다.
자바에서는 리터럴(literal)과 같은 상수 값들을 상수 풀에 저장합니다. 리터럴이란 코드에서 고정된 값을 의미하는 문자열, 숫자, 불리언 등의 값입니다. 이러한 상수 값들은 한 번 정의되면 변경할 수 없는 값으로 취급되어야 하기 때문에, 메모리 상수풀 영역에 저장하여 값을 유지합니다.
메모리 상수풀 영역은 중복된 값이 존재하지 않도록 관리됩니다. 예를 들어, "Hello World"와 같은 문자열 리터럴이 여러 곳에서 사용될 경우, 이 값은 상수풀에 한 번만 저장되고, 이후에는 해당 값을 참조하는 방식으로 사용됩니다. 이렇게 상수풀을 사용함으로써 메모리 공간을 절약할 수 있습니다.
상수풀 영역에 저장되는 값은 자바 클래스 파일(.class) 내에 저장됩니다. 프로그래머가 작성한 소스 코드에서 사용하는 리터럴 값들은 컴파일러에 의해 상수풀에 저장되어, 클래스 파일 내에 상수 풀 테이블(Constant Pool Table)에 저장됩니다. 이 테이블에서 각 상수는 고유한 인덱스 값으로 참조됩니다.
불변 객체가 무엇인지 설명하고 대표적인 Java의 예시를 설명해주세요
불변 객체는 객체 생성 이후 내부의 상태가 변하지 않는 객체를 말합니다. Java에서는 필드가 원시 타입인 경우 final 키워드를 사용해 불변 객체를 만들 수 있고, 참조 타입일 경우엔 추가적인 작업이 필요합니다.
생성자에 대해 설명해주세요
생성자는 클래스와 같은 이름의 메소드로, 객체가 생성될 때 호출되는 메소드입니다.
명시적으로 생성자를 만들지 않아도 default로 만들어지며, 생성자는 파라미터를 다르게하여 오버로딩할 수 있습니다.
String 객체가 불변인 이유에 대해 아는대로 설명해주세요
1. 캐싱 기능에 의한 메모리 절약과 속도 향상
- Java에서 String 객체들은 Heap의 String Pool 이라는 공간에 저장되는데, 참조하려는 문자열이 String Pool에 존재하는 경우 새로 생성하지 않고 Pool에 있는 객체를 사용하기 때문에 특정 문자열 값을 재사용하는 빈도가 높을 수록 상당한 성능 향상을 기대할 수 있다.
2. thread-safe
- String 객체는 불변이기 때문에 여러 쓰레드에서 동시에 특정 String 객체를 참조하더라도 안전하다.
3. 보안기능
- 중요한 데이터를 문자열로 다루는 경우 강제로 해당 참조에 대한 문자열 값을 바꾸는 것이 불가능하기 때문에 보안에 유리하다.
List, Map, Set에 대해서 설명해주세요
List는 배열 크기가 가변적이며 순서가 있고 중복이 허용됩니다.
Map은 key, value 한 쌍으로 저장되고 순서가 없습니다. Key는 중복될 수 없지만 value는 중복이 허용됩니다.
Set은 순서가 없고 중복도 허용되지 않습니다.
static 과 non-static 에 대해서 설명해주세요
static 키워드를 사용한 변수나 메소드는 클래스가 메모리에 올라갈 때 자동으로 생성되며 클래스 로딩이 끝나면 바로 사용할 수 있습니다. 즉, 인스턴스(객체) 생성 없이 바로 사용 가능합니다.
non-static은 모든 객체가 메모리를 공유한다는 특징이 있고, GC 관리 영역 밖에 있기 때문에 프로그램이 종료될 때까지 메모리에 값이 유지된 채로 존재하게 됩니다.
static을 사용하는 이유에 대해 설명해주세요
static은 자주 변하지 않는 값이나 공통으로 사용되는 값 같은 공용자원에 대한 접근에 있어서 매번 메모리에 로딩하거나 값을 읽어들이는 것보다 일종의 '전역변수'와 같은 개념을 통해 접근하는 것이 비용도 줄이고 효율을 높일 수 있습니다. 인스턴스 생성 없이 바로 사용 가능하기 때문에 프로그램 내에서 공통으로 사용되는 데이터들을 관리할 때 이용합니다.
inner class 의 장점에 대해 설명해주세요
클래스 내에 선언된 클래스를 의미하며,
- 내부 클래스에서 외부 클래스의 멤버에 손쉽게 접근할 수 있다.
- 서로 관련 있는 클래스를 논리적으로 묶어서 표현함으로써, 코드의 캡슐화를 증가시킨다.
- 외부에서는 내부 클래스에 접근할 수 없으므로, 코드의 복잡성을 줄일 수 있다.
박싱과 언박싱
원시형을 Wrapper Class로 변환하는 것이 박싱이고, Wrapper Class를 원시형으로 변환하는 것이 언박싱입니다.
new String()과 리터럴(””)의 차이에 대해 설명해주세요.
new String()은 new 키워드로 새로운 객체를 생성하기 때문에 Heap 메모리 영역에 저장되고, ""는 Heap 안에 있는 String Constant Pool 영역에 저장됩니다.
클래스와 객체에 대해 설명해주세요
클래스는 객체를 만들어내기 위한 설계도 혹은 틀 이라고 할 수 있고, 객체를 생성하는데 사용합니다. 객체는 설계도(클래스)를 기반으로 생성되며, 자신의 고유 이름과 상태, 행동을 갖습니다. 여기서 상태는 필드(fields), 행동은 메소드(Method)라고 표현합니다. 객체에 메모리가 할당되어 실제로 활용되는 실체는 '인스턴스'라고 부릅니다.
원시타입과 참조타입에 대해서 설명해주세요
primitive type는 기본 데이터 타입으로,종류는 byte, short, char, int, float, double, boolean이 있습니다. reference type은 참조 데이터 타입으로,기본 데이터 타입을 제외하고는 모두 참조 데이터 타입입니다.
- 참조 데이터 타입 종류는 class, array, interface, Enumeration이 있습니다.
- 참조 타입은 값이 저장된 곳의 주소를 저장합니다.
- 참조 타입은 4 byte 크기의 주소값이 들어갑니다.
primitive type과 reference type의 차이점
- 기본 데이터 타입은 실제 데이터 값 자체를 저장합니다. 데이터의 크기가 작고 고정적이기 때문에 메모리의 Stack 영역에 저장됩니다.
- 반면, array와 class와 같은 참조 데이터 타입은 객체가 메모리 상에 위치한 주소를 저장합니다.데이터의 크기가 동적이기 때문에 동적으로 관리하는 Heap 영역에 저장됩니다.
- 또한, 참조 데이터 타입의 경우 더이상 참조하는 변수가 없을 때, 가비지 컬렉션에 의해 파괴 됩니다.
기본 데이터 타입과 참조 데이터 타입이 사용되는 경우
primitive type은 null을 다루지도 못하고, generic에 담기지도 못합니다. primitive type이 reference type과 비교해서 갖는 장점은 성능과 메모리에 이점이 있습니다. 왜냐하면, reference type은 스택 메모리에는 참조 값만 있고 실제 값은 힙 메모리에 존재하기 때문입니다. 따라서 reference type은 값이 필요로 할 때마다, primitive type과 비해 접근 속도가 느려지게 됩니다. 데이터가 동적이지 않으면, 성능과 메모리에 장점이 있는 primitive type을 먼저 고려해보고, 만약 Null을 다뤄야 하거나, Generic 타입에서 사용되어야 한다면 reference type을 사용합니다.
자바 특징을 설명해주세요
1. 객체지향 프로그래밍 언어
Java는 객체지향 프로그래밍 언어로, 기본 자료형을 제외한 모든 요소들이 객체로 표현됩니다. 이를 통해 캡슐화, 상속, 다형성 등의 객체 지향 개념을 적용할 수 있습니다.
2. 플랫폼 독립성
Java는 JVM (Java Virtual Machine) 위에서 동작하므로 운영체제에 독립적입니다.
3. 자동 메모리 관리
Java는 GC (Garbage Collector)를 통해 자동적인 메모리 관리가 가능합니다. 메모리 할당과 해제를 자동으로 처리하기 때문에 개발자는 메모리 관리에 대한 부담을 덜 수 있습니다.
4. 다중 스레드 지원
Java는 다중 스레드를 지원합니다. 멀티 코어 CPU를 활용하여 높은 성능을 발휘할 수 있습니다.
5. 실행속도와 제약
JIT (Just-In-Time) 컴파일러를 사용하여 컴파일 시간을 단축하고 실행 속도를 향상시키지만, C++ 등의 언어에 비해 상대적으로 느린 실행 속도를 보입니다. 또한, Java는 다중 상속을 지원하지 않으며, 타입에 엄격합니다. 이는 일부 개발자들에게 제약으로 다가올 수 있습니다.
객체 지향 프로그래밍의 장, 단점에 간단하게 설명해주세요
[장점]
1. 코드 재사용에 용이 ( 남이 만든 클래스 가져와서 이용, 상속을 통한 확장)
2. 유지보수 쉬움 (클래스 내부 멤버 변수 혹은 메서드의 해당 부분만 수정)
3. 대형 프로젝트에 적합 (클래스 단위로 모듈화 시켜 개발)
[단점]
1. 처리 속도가 상대적으로 느림
2. 객체가 많으면 용량이 커질 수 있음
3. 설계시 많은 시간과 노력이 필요
Getter, Setter를 사용하는 이유는 무엇인가요??
Getter와 Setter를 이용하여 객체의 속성에 접근할 때, 메서드를 통해서 접근하게 됩니다. 따라서, 메서드 안에서 매개변수와 같이 어떤 올바르지 않은 입력에 대해 사전에 처리할 수 있게 됩니다. 이를 통해 유효성 검사나 예외 처리 등을 할 수 있습니다. 또한, Setter를 이용하여 객체의 속성 값을 변경할 때에도, 무결성을 보호할 수 있습니다.
즉, Getter와 Setter를 사용하면, 객체의 속성에 대한 직접적인 접근을 막고, 메서드를 통해 간접적으로 접근함으로써 객체의 무결성을 보호하고 유지보수성을 높일 수 있습니다.
final / finally / finalize 의 차이를 설명해주세요
final은 클래스, 메소드, 변수, 인자를 선언할 때 사용되며, 한 번만 할당하고 변경할 수 없음을 나타냅니다. final 변수는 한 번 초기화된 이후에는 값을 변경할 수 없습니다. final 메소드는 다른 클래스에서 오버라이딩을 금지합니다. final 클래스는 다른 클래스에서 상속할 수 없습니다.
finally는 try-catch 블록에서 예외 발생 여부와 상관없이 항상 실행되어야 하는 코드 블록입니다. 예외 처리 코드 뒤에 작성되어, 예외 처리 이후에 항상 실행됩니다.
finalize는 Object 클래스에 정의된 메소드로, GC에 의해 호출되는 메소드입니다. finalize() 메소드를 오버라이딩하여 구현하면 GC가 실행될 때 해당 객체가 참조하는 모든 객체들이 모두 해제되기 전에 호출되며, 객체의 소멸 직전에 마지막으로 실행되는 코드를 작성할 수 있습니다. 하지만 finalize() 메소드는 GC가 실행되는 시점이 불분명하기 때문에 오버라이딩해서 구현하는 것은 권장되지 않습니다. 대신, try-with-resources 블록을 사용하여 리소스를 해제하는 것이 좋습니다.
정적타입 언어와 동적 타입 언어의 차이 및 장단점
정적 타입 언어는 컴파일할 때 타입을 결정하고, 런타임 시 타입에 대한 검증이 이루어지지 않습니다. 이러한 특성 때문에 컴파일러가 코드를 검증하므로 런타임에 타입 에러를 잡아내기 쉽고, 안정성과 예측 가능성이 높습니다. 또한, 코드 작성시 변수형을 결정해줘야 하는 불편함이 있지만, 변수의 형식을 지정함으로써 타입이 맞지 않는 에러를 줄일 수 있습니다.
반면, 동적 타입 언어는 런타임 시 타입을 결정하고, 변수에 대한 타입 검사를 실행합니다. 이러한 특성 때문에 유연성과 개발 속도가 빠르며, 코드 작성시 타입에 대한 고민을 하지 않아도 되어 개발자가 자유롭게 코드를 작성할 수 있습니다. 하지만, 런타임에 타입 에러가 발생할 가능성이 높아집니다. 따라서, 코드가 복잡해지면 타입 에러를 찾기가 어려울 수 있으며, 예상치 못한 에러를 일으킬 수 있습니다.
정적 타입 언어의 장점은 안정성과 예측 가능성이 높다는 것이며, 단점은 변수형 결정에 대한 불편함이 있습니다. 동적 타입 언어의 장점은 개발 속도와 유연성이 높다는 것이며, 단점은 런타임 에러가 발생할 가능성이 높아진다는 것입니다.
java가 파이썬 보다 속도가 빠른 이유
일괄 컴파일 방식 언어인 C, C++는 코드를 컴파일하면 CPU에서 직접 실행 가능한 기계어(native code)로 변환되므로 매우 빠른 속도로 실행됩니다. 반면에 Java는 컴파일된 바이트 코드를 JVM(Java Virtual Machine)에서 기계어로 변환하여 실행하므로 C, C++에 비해 실행 속도가 느릴 수 있습니다. 하지만 최근의 JVM은 JIT(Just-In-Time) 컴파일러를 사용하여 런타임에 바이트 코드를 컴파일하고 바로 실행 가능한 기계어로 변환하는 방식을 사용하여 속도를 개선하고 있습니다.
Python과 같은 인터프리터 방식의 언어는 코드를 바로 실행하기 때문에 컴파일된 코드보다는 실행 속도가 느릴 수 있습니다. 그러나 최근에는 JIT 컴파일러를 사용하는 Python 구현체가 개발되어 속도를 개선하는 방식이나, 코드 실행 중에 최적화를 수행하는 방식 등으로 속도를 개선하는 노력이 이루어지고 있습니다.
현재 파이썬은 JIT(Just-In-Time) 컴파일러를 사용하는 구현체가 여러 개 있습니다. 예를 들면, PyPy(파이파이)와 GraalPython(그라얼파이썬) 등이 있습니다. 이들 구현체는 런타임에 코드를 컴파일하고, 최적화를 수행하여 빠른 속도로 실행할 수 있도록 지원하고 있습니다. 또한, 이들 구현체는 GIL(Global Interpreter Lock)을 해제하여 멀티 스레딩을 지원하고 있어 파이썬의 병렬성을 개선하는데 기여하고 있습니다.
따라서, 일반적으로 C, C++는 가장 빠른 수행 속도를 가지며, Java는 중간 수준의 속도를 가지며, Python과 같은 인터프리터 방식의 언어는 상대적으로 느린 수행 속도를 가지지만 최근에는 다양한 방식으로 속도를 개선하고 있습니다.
'기타 > 기술 면접 대비' 카테고리의 다른 글
[신입 개발자 기술 면접] 네트워크 (0) | 2022.06.20 |
---|---|
[신입 개발자 기술 면접] 운영체제 (2) | 2022.05.31 |
[신입 개발자 기술 면접] 데이터베이스 (0) | 2022.01.14 |
댓글