기술 면접 준비 6

Jiwontwopunch·2022년 1월 17일
0

스터디

목록 보기
7/16
post-thumbnail

JVM 에 대해서, GC 의 원리

JVM(Java Virtual Machine) 자바 가상 머신

가상머신 : 프로그램을 실행하기 위해 물리적 머신과 유사한 머신을 소프트웨어로 구현한 것
JVM의 역할은 자바 애플리케이션을 클래스 로더를 통해 읽어들여 자바 API와 함께 실행하는 것이다. 그리고 Java와 OS사이의 중개자 역할을 수행하며 재사용을 가능하게 해주고 메모리 관리, Garbage collection을 수행한다. 간단히 말해 JVM은 Java 클래스 파일을 로드하고 바이트 코드를 해석하며, 메모리 등의 자원을 할당하고 관리하며 정보를 처리하는 작업을 하는 프로그램이다.

자바프로그램 실행과정

Java언어로 프로그래밍된 파일을 Java컴파일러가 가상 기계어 파일인 Java클래스 파일로 만든다. 다시 말해, 소스 코드를 Java바이트 코드로 번역한다. 이후 Java바이트 코드를 JVM이 읽고 실행하게 된다.

  • Java Compiler : Java Source파일을 JVM이 해석할 수 있는 Java Byte Code(. class)로 변경한다. 일반적인 윈도우 프로그램의 경우, Compile 이후 Assembly 언어로 구성된 파일이 생성된다.
  • Class Loader : JVM내로. class파일들을 Load 한다. Loading 된 클래스들을 Runtime Data Area에 배치된다. 일반적인 윈도우 프로그램의 경우 Load 과정은 OS가 주도한다.
  • Execution Engine : Loading 된 클래스의 Bytecode를 해석한다. 이 과정에서 ByteCode가 BinaryCode로 변경된다. 일반적인 윈도우 프로그램의 경우 Assembier가 Assembly언어로 쓰인 코드 파일을 BinaryCode로 변경한다.
  • Runtime Data Area : JVM이 프로세스로써 수행되기 위해 OS로부터 할당받는 메모리 영역이다. 저장 목적에 따라 다음과 같이 5개로 나눌 수 있다.
  1. Method Area : 모든 Thread에게 공유된다. 클래스 정보, 변수 정보, Method정보, static변수 정보, 상수 정보 등이 저장되는 영역
  2. Heap Area : 모든 Thread에게 공유된다. new 명령어로 생성된 인스턴스와 객체가 저장되는 구역, 공간이 부족해지면 Garbage Collection이 실행된다.
  3. Stack Area : 각 스레드마다 하나씩 생성된다. Method안에서 사용되는 값들(매개변수, 지역변수, 리턴 값 등)이 저장되는 구역, 메서드가 호출될 때 LIFO로 하나씩 생성되고, 메서드 실행이 완료되면 LIFO로 하나씩 지워진다.
  4. PC Register : 각 스레드마다 하나씩 생성된다. CPU의 Register와 역할이 비슷하다. 현재 수행 중인 JVM명령의 주소 값이 저장된다.
  5. Native Method Stack : 각 스레드마다 하나씩 생성된다. 다른 언어(C/C++ 등)의 메서드 호출을 위해 할당되는 구역 언어에 맞게 Stack이 형성되는 구역이다. JNI(Java Native Interface)라는 표준 규약을 제공한다.

자바의 기본 구조

자바 프로그램 분석

  • Package : 자바 소스파일을 기능별로 분류하는 방법이라고 생각하면 된다. 분류 기능 외에도 클래스명이 충돌하지 않도록 하는 것
  • import : 다른 패키지에 있는 클래스를 불러와서 사용하겠다고 표시하는 기능
  • public static void main(String [] args){} : 객체의 생성과 사용
  • Date date = new Date(); : new라는 연산자를 사용하여 Date객체를 만듦
  • public class HelloJava {} : 자바에서는 보통 하나의 파일에 하나의 class를 작성한다. public이 붙어 있는 클래스일 경우에 반드시 클래스 파일 이름이 되어야 한다. HelloJava라는 객체를 정의하는 부분, 자바에서 프로그램 실행의 시작점이 되는 부분, Java Virtual Machine은 프로그램이 로드된 후 main() 메서드를 찾아서 프로그램을 시작
  • System.out.println(); : 인자로 주어진 값을 화면에 출력하는 기능

GC(Garbage Collection) 가비지 컬렉션

https://asfirstalways.tistory.com/159
https://d2.naver.com/helloworld/1329

Collection

Java Collection 에는 List, Map, Set 인터페이스를 기준으로 여러 구현체가 존재한다. 이에 더해 Stack과 Queue 인터페이스도 존재한다. 이들은 다수의 Data 를 다루는데 표준화된 클래스들을 제공해주기 때문에 DataStructure 를 직접 구현하지 않고 편하게 사용할 수 있게 해준다. 또한 배열과 다르게 객체를 보관하기 위한 공간을 미리 정하지 않아도 되므로, 상황에 따라 객체의 수를 동적으로 정할 수 있어 프로그램의 공간적인 효율성 또한 높여준다.

  • List : List 인터페이스를 직접 @Override를 통해 사용자가 정의하여 사용할 수도 있으며, 대표적인 구현체로는 ArrayList가 존재한다. 이는 기존에 있었던 Vector를 개선한 것이다. 이외에도 LinkedList 등의 구현체가 있다.
    ArrayList와 Vector의 차이점
    : https://blog.naver.com/kkkths/221760312903
  • Map : 대표적인 구현체로 HashMap이 존재한다. (밑에서 살펴볼 멀티스레드 환경에서의 개발 부분에서 HashTable 과의 차이점에 대해 살펴본다.) key-value 의 구조로 이루어져 있으며 Map 에 대한 구체적인 내용은 DataStructure 부분의 hashtable 과 일치한다. key 를 기준으로 중복된 값을 저장하지 않으며 순서를 보장하지 않는다. key 에 대해서 순서를 보장하기 위해서는 LinkedHashMap을 사용한다.
    참고 블로그
    https://velog.io/@gillog/Map-%EC%BB%AC%EB%A0%89%EC%85%98-HashMap
  • Set : 대표적인 구현체로 HashSet이 존재한다. value에 대해서 중복된 값을 저장하지 않는다. 사실 Set 자료구조는 Map 의 key-value 구조에서 key 대신에 value 가 들어가 value 를 key 로 하는 자료구조일 뿐이다. 마찬가지로 순서를 보장하지 않으며 순서를 보장해주기 위해서는 LinkedHashSet을 사용한다. stream API를 사용하여 List로 변환 가능하다. List<String> list = set.stream()
    HashSet을 가장 많이 사용한다. LinkedHashSet은 앞 뒤 요소의 정보를 갖고 있고 index같은 순서가 필요 없다. TreeSet은 이진 검색 트리를 사용하여 자료를 관리한다.
    참고 블로그
    https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=heartflow89&logNo=220994601249
  • Stack 과 Queue : Stack 객체는 직접 new 키워드로 사용할 수 있으며, Queue 인터페이스는 JDK 1.5 부터 LinkedList에 new 키워드를 적용하여 사용할 수 있다. 자세한 부분은 DataStructure 부분의 설명을 참고하면 된다.

Annotation

어노테이션이란 본래 주석이란 뜻으로, 인터페이스를 기반으로 한 문법이다. 주석과는 그 역할이 다르지만 주석처럼 코드에 달아 클래스에 특별한 의미를 부여하거나 기능을 주입할 수 있다. 또 해석되는 시점을 정할 수도 있다.(Retention Policy) 어노테이션에는 크게 세 가지 종류가 존재한다. JDK 에 내장되어 있는 built-in annotation과 어노테이션에 대한 정보를 나타내기 위한 어노테이션인 Meta annotation 그리고 개발자가 직접 만들어 내는 Custom Annotation이 있다. built-in annotation 은 상속받아서 메소드를 오버라이드 할 때 나타나는 @Override 어노테이션이 그 대표적인 예이다. 어노테이션의 동작 대상을 결정하는 Meta-Annotation 에도 여러 가지가 존재한다.
추가 : https://loco-motive.tistory.com/29

Generic <>

제네릭은 자바에서 안정성을 맡고 있다고 할 수 있다. 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에서 사용하는 것으로, 컴파일 과정에서 타입체크를 해주는 기능이다. 객체의 타입을 컴파일 시에 체크하기 때문에 객체의 타입 안전성을 높이고 형변환의 번거로움을 줄여준다. 자연스럽게 코드도 더 간결해진다. 예를 들면, Collection 에 특정 객체만 추가될 수 있도록, 또는 특정한 클래스의 특징을 갖고 있는 경우에만 추가될 수 있도록 하는 것이 제네릭이다. 이로 인한 장점은 collection 내부에서 들어온 값이 내가 원하는 값인지 별도의 로직처리를 구현할 필요가 없어진다. 또한 api 를 설계하는데 있어서 보다 명확한 의사전달이 가능해진다.

final keyword

  • final class : 다른 클래스에서 상속하지 못한다.
  • final method : 다른 메소드에서 오버라이딩하지 못한다.
  • final variable : 변하지 않는 상수값이 되어 새로 할당할 수 없는 변수가 된다.
  • finally : try-catch or try-catch-resource 구문을 사용할 때, 정상적으로 작업을 한 경우와 에러가 발생했을 경우를 포함하여 마무리 해줘야하는 작업이 존재하는 경우에 해당하는 코드를 작성해주는 코드 블록이다.
  • finalize() : keyword 도 아니고 code block 도 아닌 메소드이다. GC에 의해 호출되는 함수로 절대 호출해서는 안 되는 함수이다. Object 클래스에 정의되어 있으며 GC 가 발생하는 시점이 불분명하기 때문에 해당 메소드가 실행된다는 보장이 없다. 또한 finalize() 메소드가 오버라이딩 되어 있으면 GC 가 이루어질 때 바로 Garbage Collecting 되지 않는다. GC 가 지연되면서 OOME(Out of Memory Exception)이 발생할 수 있다.

Overriding vs Overloading

  • 오버라이딩(Overriding) : 부모클래스로부터 상속받은 메소드를 자식 클래스에서 재정의
  • 오버로딩(Overloading) : 메소드의 이름이 같고 매개변수의 개수나 타입이 달라야한다. 리턴값만 다른 것은 오버로딩할 수 없다.

Access Modifier

변수 또는 메소드의 접근 범위를 설정해주기 위해서 사용하는 Java 의 예약어를 의미하며 총 네 가지 종류가 존재한다.

  • public : 어떤 클래스에서라도 접근이 가능하다.
  • protected : 클래스가 정의되어 있는 해당 패키지 내 그리고 해당 클래스를 상속받은 외부 패키지의 클래스에서 접근이 가능하다.
  • (default) : 클래스가 정의되어 있는 해당 패키지 내에서만 접근이 가능하도록 접근 범위를 제한한다.
  • private : 정의된 해당 클래스에서만 접근이 가능하도록 접근 범위를 제한한다.

Wrapper class

기본 자료형(Primitive data type)에 대한 클래스 표현을 Wrapper class 라고 한다. Integer, Float, Boolean 등이 Wrapper class 의 예이다. 일단 컬렉션에서 제네릭을 사용하기 위해서는 Wrapper class 를 사용해줘야 한다. 또한 null 값을 반환해야만 하는 경우에는 return type 을 Wrapper class 로 지정하여 null을 반환하도록 할 수 있다. 하지만 이러한 상황을 제외하고 일반적인 상황에서 Wrapper class 를 사용해야 하는 이유는 객체지향적인 프로그래밍을 위한 프로그래밍이 아니고서야 없다. 일단 해당 값을 비교할 때, Primitive data type 인 경우에는 ==로 바로 비교해줄 수 있다. 하지만 Wrapper class 인 경우에는 .intValue() 메소드를 통해 해당 Wrapper class 의 값을 가져와 비교해줘야 한다.

Multi-Thread 환경에서의 개발

Field member

필드(field)란 클래스에 변수를 정의하는 공간을 의미한다. 이곳에 변수를 만들어두면 메소드끼리 변수를 주고 받는 데 있어서 참조하기 쉬우므로 정말 편리한 공간 중 하나이다. 하지만 객체가 여러 스레드가 접근하는 싱글톤 객체라면 field에서 상태값을 갖고 있으면 안된다. 모든 변수를 parameter 로 넘겨받고 return 하는 방식으로 코드를 구성해야 한다.

동기화(Synchronized)

필드에 Collection 이 불가피하게 필요할 때는 어떠한 방법을 사용할까? Java 에서는 synchronized 키워드를 사용하여 스레드 간 race condition 을 통제한다. 이 키워드를 기반으로 구현된 Collection 들도 많이 존재한다. List를 대신하여 Vector를 사용할 수 있고, Map을 대신하여 HashTable을 사용할 수 있다. 하지만 이 Collection 들은 제공하는 API 가 적고 성능도 좋지 않다.

기본적으로는 Collections라는 util 클래스에서 제공되는 static 메소드를 통해 이를 해결할 수 있다. Collections.synchronizedList(), Collections.synchronizedSet(), Collections.synchronizedMap() 등이 존재한다. JDK 1.7 부터는 concurrent package를 통해 ConcurrentHashMap이라는 구현체를 제공한다. Collections util 을 사용하는 것보다 synchronized 키워드가 적용된 범위가 좁아서 보다 좋은 성능을 낼 수 있는 자료구조이다.

ThreadLocal

스레드 사이에 간섭이 없어야 하는 데이터에 사용한다. 멀티스레드 환경에서는 클래스의 필드에 멤버를 추가할 수 없고 매개변수로 넘겨받아야 하기 때문이다. 즉, 스레드 내부의 싱글톤을 사용하기 위해 사용한다. 주로 사용자 인증, 세션 정보, 트랜잭션 컨텍스트에 사용한다.

스레드 풀 환경에서 ThreadLocal 을 사용하는 경우 ThreadLocal 변수에 보관된 데이터의 사용이 끝나면 반드시 해당 데이터를 삭제해 주어야 한다. 그렇지 않을 경우 재사용되는 쓰레드가 올바르지 않은 데이터를 참조할 수 있다.

ThreadLocal 을 사용하는 방법은 간단하다.

  1. ThreadLocal 객체를 생성한다.
  2. ThreadLocal.set() 메서드를 이용해서 현재 스레드의 로컬 변수에 값을 저장한다.
  3. ThreadLocal.get() 메서드를 이용해서 현재 스레드의 로컬 변수 값을 읽어온다.
  4. ThreadLocal.remove() 메서드를 이용해서 현재 스레드의 로컬 변수 값을 삭제한다.

출처
https://pienguin.tistory.com/

https://github.com/JaeYeopHan/Interview_Question_for_Beginner/tree/master/Java#part-2-1-java

0개의 댓글