Java

CS 스터디 회고 - java 2

리콜 2024. 11. 15. 23:49

7. Synchronized 키워드에 대해 설명해 주세요.

여러개의 스레드가 한개의 자원을 이용하고자 할 때 현재 데이터를 사용하고 있는 스레드를 제외하고 나머지 스레드들은 데이터에 접근 할 수 없도록 막는 개념으로 즉, 멀티스레드 접근 제한 키워드입니다. 메소드 블럭 단위로 적용이 가능합니다.

 

Synchronized 키워드가 어디에 붙는지에 따라 의미가 약간씩 변화하는데, 각각 어떤 의미를 갖게 되는지 설명해 주세요.

  • 메서드에 붙으면 메서드가 호출되는 동안 해당 객체의 인스턴스에 대한 락을 획득하여 한스레드가 이 메서드를 실행 중이면 다른 스레드가 이객체의 메서드를 호출 할 수 없습니다.
  • 메서드 내부 블록에 사용하게 된다면 내부 코드만 동기화를 하여 동기화 범위를 줄여 성능을 최적화 할 수 있습니다.
  • 특정 객체에 동기화를 사용할 수 도 있습니다.
  • 정적 메서드에 붙이면 해당 클래스에 대해서 동시에 접근 할 수 없다.

효율적인 코드 작성 측면에서, Synchronized는 좋은 키워드일까요?

  • 데이터의 일관성을 보장해 주기 때문에 데이터 손상을 방지 할 수 잇다.
  • lock과 unlock이 발생하여 많이 사용할 경우에는 오히려 이 스레드를 만드는 과정이 연산하는데 오래걸릴 수 있습니다.
  • 스레드 간의 경합을 발생 시켜 성능 저하를 야기 할 수 있다. 즉 자주 호출 되는 메서드에 사용하면 프로그램 전체의 성능이 저하 될 수 있다.
    • 근데 println에 특정 부분에 동기화가 들어가 있는데 그래서 println을 많이 하면 성능이 저하 되는건가?

Synchronized 를 대체할 수 있는 자바의 다른 동기화 기법에 대해 설명해 주세요.

  • Synchronized 외에도 여러 다양한 매커니즘을 제공합니다. 대표적으로 ReentrantLock, ReadWriteLock이 있습니다.
  • ReentrantLock은 synchronized의 기능을 확장하여 더많은 제어와 유연성을 제공합니다. tryLock(), lockInterruptibly() 등 다양한 메소드를 제공하여 더욱 유연하게 구현 할 수 있다. Timeout 을 지정하여 특정 시간 동안 lock을 못 획득하면 다른 작업을 하도록 할 수 있다.
  • ReadWriteLock은 다중 읽기 와 단일 쓰기를 허용하여 읽기 작업이 많은 경우 성능을 향상 시킬 수 있습니다.
  • 낙관적 락 - 자원에 락을 걸지 않고, 동시성 문제가 발생하면 그때 처리 한다.
  • 간단한 원자적 연산에는 Synchroized가 오버헤드가 클 수 있다. 이러한 경우에는 Atomic 클래스를 사용하는 것이 효율적일 수 있다. Atomic은 CAS(비교 및 교환) 알고리즘을 사용하고, 적용범위가 작아 좀 더 빠르다고 한다.

Thread Local에 대해 설명해 주세요.

  • 각스레드가 독립적인 변수를 가지도록 하는 클래스 입니다.
  • Synchronized와 같은 동기화 매커니즘을 사용하면 성능 오버헤드가 발생할 수 있을때 사용한다. 별도의 동기화 처리가 없기 때문
  • 각 스레드마다 독립적인 DB를 연결하거나 스레드마다 로그를 분리하여 관리 할때 좋다. 또한 독립적인 트랜잭션을 관리 할 수 있다.
  • spring security에서 security context Holder에서 authentification 객체를 thread local로 저장

8. Java Stream에 대해 설명해 주세요.

Stream API는 자바에서 일련의 데이터 요소인 배열이나 컬렉션 등의 데이터를 처리하기 위한 api입니다.

 

Stream과 for ~ loop의 성능 차이를 비교해 주세요

  • for문이 압도적으로 빠릅니다. JIT 컴파일러가 for 문을 40년이상 다루어 for문에 대한 internal optimization이 잘되어 있지만 stream은 2015년 이후에 도입되어 아직 컴파일러가 최적화를 제대로 하지 못했기 때문입니다.
  • 원시 데이터는 15배 정도 for문이 빠르고 참조 데이터 형식에서는 그렇게 까진 차이 나진 않지만 1.27% 정도 더 for문이 성능이 좋다.ㅛ

Stream은 병렬처리 할 수 있나요?

  • stream은 병렬처리를 할 수 있고 간단하게 사용할 수 있습니다. parallelStream() 메서드를 호출하는 것만으로 간단하게 병렬 스트림으로 변환 할 수 있습니다.
  • 내부적으로는 ForkJoinPool을 이용하는데 ForkJoinPool은 자바 7 에서 도입된 프레임워크로 분할정복 알고리즘을 기반으로 작업을 작은 단위로 분할하고 이를 여러 스레드에 할당하여 병렬로 처리하는 것을 지원한다.

Stream에서 사용할 수 있는 함수형 인터페이스에 대해 설명해 주세요.

  • Function : 입력을 받아 출력으로 매핑하는 함수 - map
  • Consumer 입력을 받아 처리하고 반환값이 없는 함수 - foreach
  • Predicate 입력을 받아 boolean값을 처리하는 함수 - filter
  • Supplier 매개 변수 없이 값을 반환하는 함수 - 데이터 생성 및 초기화
  • 가끔 외부 변수를 사용할 때, final 키워드를 붙여서 사용하는데 왜 그럴까요? 꼭 그래야 할까요?
    • 스트림에서 외부 변수를 사용할 때 final 키워드를 붙이는 가장 큰 이유는 람다 표현식 때문입니다. 람다 표현식은 함수형 인터페이스를 구현하는 간결한 방법이지만, 람다 표현식 내부에서 사용하는 변수는 effectively final이어야 합니다. 즉, 한 번 값이 할당되면 변경되지 않아야 한다는 의미입니다.

9. Java의 GC에 대해 설명해 주세요.

자바는 메모리를 관리해주는 가비지 컬렉터, GC가 사용하지 않는 메모리를 관리해 주어 메모리 할당과 제거를 신경쓰지 않고도 프로그램을 작성할 수 있습니다.

 

finalize() 를 수동으로 호출하는 것은 왜 문제가 될 수 있을까요?

  • finalize는 Object에 들어있는 메서드로 GC에 의해 수거될때 자동으로 JVM에 의해 호출 되는데 수동으로 호출되어서는 안되는 메서드입니다.
  • finalize() 메서드의 호출 시점은 불확실합니다. finalize() 메서드는 GC에 의해 호출되므로, GC의 작업 스케줄링에 따라 호출 시점이 결정됩니다. 따라서, finalize() 메서드를 명시적으로 호출해도 객체가 즉시 제거되지 않을 수 있습니다.
  • finalize() 메서드는 객체 해제 과정에서 마지막에 호출되는 메서드이기 때문에, 이 메서드 내부에서는 다른 객체나 자원을 참조하거나 변경하는 작업을 수행하지 않는 것이 좋습니다. 만약 finalize() 메서드에서 자원을 참조하거나 변경하는 작업을 수행한다면, 예기치 않은 결과가 발생할 수 있습니다. 예를 들어 finalize에서 객체를 참조하면 이미 GC의 대상이 된 객체가 살아나 메모리 누수가 될 수 있습니다.

어떤 변수의 값이 null이 되었다면, 이 값은 GC가 될 가능성이 있을까요?

  • 변수의 값이 null이 되었다고 해서 GC의 대상이 될지 확신 할 수없습니다. null이면서 해당 변수에 대한 모든 참조가 없어야합니다.
  • 이때 참조가 존재하는 객체를 Reachable상태, 그렇지 않다면 Unreachable상태라고 하는데 참조의 유무는 Root Set과의 관계가 있는냐 없느냐로 판단된다.

  • Root Set 
    • Stack 영역의 지역 변수, 파라미터
    • Method 영역의 정적 변수
    • JNI에 의해 생성된 객체
  • 따라서 null 이면서 Root Set과의 연관이 없어야한다.

  • 참고 GC의 발생 시점 
    • Heap 메모리는 Eden, Survivor1, Survivor2, Old generation으로 나뉜다.Young generation에서 하는 GC는Minor GC, Old generation에서 일어나는 GC를 Major GC라고 한다.
    • 새로운 객체가 생성이 되면 Eden에 생성이 되고 어느 순간 Eden영역이 모두 차게 된다면 Minor GC가 발생하며 Mark and Sweep을 실행한다. 그뒤 살아남은 객체들은 Survivor1로 이동하게 되면 이때 age값을 가진다. ( age = 1) Eden에서 Suvivor로 옮겨지는 과정을 Stop and Copy라고 한다.
    • 또 Eden영역에 가득차면 Minor GC후 Survivor로 옮기는데 이때는 Survivor1로 가는게 아니라 비어있는 Survivor로 가게 된다. 그러므로 이때에는 Survivor2로 Survivor1의 값들과 Eden값들이 이동한다. 이동할때마다 age가 1이 이동한다. 단편화 문제를 해결 하기 위해 비어있는 Survivor로 이동하는 것
    • 그렇게 반복하다 보면 특정 age값이 되는데 이때 Old Generation으로 이동하게 된다. 이를 Promotion이라고한다. (HotSpot JVM 의 기본 임계값은 31 이다.)
    • 또 그렇게 Old 영역도 다차면 Major GC가 발생한다.
  • 이렇게 GC발생시점이 있는데 발생시점을 모른다는 것은 이 메모리 차는게 언제 일어날지 모른다는 의미인가?

10. equals()와 hashcode()에 대해 설명해 주세요.

equals와 hashcode메소드는 Object클래스에서 정의된 메소드로 객체의 동일성 및 동등성을 비교하는데 사용됩니다.

기본적으로 equal는 두 객체의 참조가 같은지를 비교합니다. 두객체의 메모리 주소가 같다면 true를 반환합니다.

우선 hashCode는 객체의 주소값을 변환하여 생성한 고유한 정수값입니다. 따라서 hashcode메서드는 객체를 유일하게 식별하는 정수 값을 반환합니다.

본인이 hashcode() 를 정의해야 한다면, 어떤 점을 염두에 두고 구현할 것 같으세요?

  • 동일한 객체는 동일한 메모리 주소를 갖기에 동일한 객체는 동일한 해시코드를 가져야하므로 equals를 재정의 했다면 hashcode도 재정의 해야합니다.
  • 그리고 해시 충돌이 일어나지 않도록 코드를 짜야 일관성을 유지 하는데 도움이 됩니다.

그렇다면 equals() 를 재정의 해야 할 때, 어떤 점을 염두에 두어야 하는지 설명해 주세요.

  • equal를 재정의 할때 String의 equal 처럼 다른 두개의 메모리 주소를 가진 객체여도 동일한 문자열을 가지고 있다면 true를 반환하도록 사용성에 맞게 만들 수 있습니다. 다만 이때 어떤 필드를 동등성의 여부로 사용할지 생각해봐야 한다.
  • 또한 equal를 통해 동등하다면 두객체의 hashcode는 동일해야합니다. 하지만 hashcode가 같다고 해서 equal가 동등하다는 것은 아닙니다. 이것을 해시 충돌이라고 합니다. 즉, 다른 객체라도 우연히 같은 해시 코드를 가질 수 있습니다.
  • Equals할때 instanceof 랑 getClass 를 많이 쓰는데 차이가 뭔가요?
  • 자바에서는 hashset 에서 hash 충돌이 일어나면 어떻게 하나
    • bucket만들어서 한다.

11. IoC와 DI에 대해 설명해 주세요.

IOC는 제어의 역전이라는 말로 메소드나 객체의 호출 작업을 개발자가 하는 것이 아니라 외부에서 결정되는 것입니다. 즉, 객체의 생성이나 생명 주기 관리 등을 개발자가 직접 하지 않고 스프링 컨테이너가 대신해주는 것입니다. 객체의 의존성을 역전 시켜 객체간의 결합도를 줄이고 유연한 코드를 작성 할 수 있게 합니다.

DI, 의존성 주입은 다른 프레임워크와 차별화 되어 제공하는 의존관계 주입 기능으로 객체를 직접 생성하는 것이 아니라 외부에서 생성하고 주입 시켜주는 방식입니다.

이를 통해 클래스 간의 결합도가 감소하여 객체간의 의존성을 명확하게 드러나고 동일한 클래스를 다양한 문맥에서 재사용 할 수 있습니다. 또한 시스템 구성요소를 쉽게 교체하거나 확장할 수 있습니다.

이를 통해 코드 중복, 유지 보수를 편하게 할 수 있습니다

—> DI가 필요한 이유 - 모듈화( 결합도 낮추는 방식) 과 같이 결합도 낮춰진다. 코드의 재사용성도 용이해진다. - 테스트 하기도 용이해진다.

  • IOC 와 DI 의 관계 : IOC를 구현학기 위한 구체적인 방법이 DI, DI를 통해 결합도를 낮추고 객체를 독립적으로 개발하고 테스트 할 수 잇다.

후보 없이 특정 기능을 하는 클래스가 딱 한 개하면, 구체 클래스를 그냥 사용해도 되지 않나요? 그럼에도 불구하고 왜 Spring에선 Bean을 사용 할까요?

  • 그런 상황이라면 구체클래스를 사용하는게 간단해 보일 수 있지만 Bean을 사용하면 클래스간의 결합도가 낮아지고 나중에 있을 코드 변경이 유연해질 수 잇습니다.또한 테스트 시에도 용이하게 할 수 있습니다.

Spring의 Bean 생성 주기에 대해 설명해 주세요.

  • 먼저 @Componet , javaconfig, XML 설정 파일을 스캔하여 빈이 정의됩니다.(빈의 이름, 클래스, 생성자 인자 등이 설정)

  • 빈생성 : 스프링컨테이너가 빈 정의를 읽고 해당 클래스의 인스턴스를 생성합니다.
  • 의존성 주입
    • 의존 관계를 주입하기 전의 준비단계 존재 여기서 객체의 생성이 일어나는데 여기서 알고 넘어가야할 사항은 : 생성자 주입 : 객체의 생성과 의존과계 주입이 동시에 일어남 Setter, Field 주입 : 객체의 생성 → 의존관계 주입으로 라이프 사이클이 나누어져 있음
    • 근데 만약 Controller에서 서비스를 참조하고 있으면 Controller 객체 생성 못한다. 즉 생성자 주입에서 빈 객체 생성과 의존관계 주입이 하나의 단계에서 일어난다.
      https://dev-coco.tistory.com/170#head5
    • 반면에 setter주입은 별도로 필요하지 않아서 객체생성 → 의존 관계 주입의 단계로 진행된다.
    • @Autowired @Inject 등의 어노테이션을 사용하여 의존성을 주입
    • POJO
  • 초기화 콜백
    • 빈이 사 용되기 전에 필요한 초기화 작업을 수행합니다.
    • @PostConstruct 어노테이션이 붙은 메소드를 호출하거나, InitializingBean 인터페이스의 afterPropertiesSet() 메소드를 구현하여 초기화 로직을 작성할 수 있습니다.
  • 사용
    • 초기화가 완료된 빈은 애플리케이션에서 필요한 곳에서 사용됩니다.
    • 스프링 컨테이너는 빈을 관리하며, 필요에 따라 빈을 제공합니다.
  • 소멸 콜백
    • 스프링 컨테이너가 종료되거나, 빈이 더 이상 필요 없을 때 소멸됩니다.
    • @PreDestroy 어노테이션이 붙은 메소드를 호출하거나, DisposableBean 인터페이스의 destroy() 메소드를 구현하여 소멸 로직을 작성할 수 있습니다.
    • 스프링 컨테이너는 이러한 소멸 메소드를 호출하여 빈을 소멸시킵니다.
 

[Spring] 빈 생명주기(Bean LifeCycle) 콜백 알아보기

스프링의 IoC 컨테이너는 Bean 객체들을 책임지고 의존성을 관리한다. 객체들을 관리한다는 것은 객체의 생성부터 소멸까지의 생명주기(LifeCycle) 관리를 개발자가 아닌 컨테이너가 대신 해준다는

dev-coco.tistory.com

 

프로토타입 빈은 무엇인가요?

  • 싱글톤 빈은 매번 같은 객체를 불러와 주지만 싱글톤이 아닌 하나의 빈으로 여러개의 객체를 만들어야할때
  • ApplicationContext타입을 이용하여 getBean메서드로 해당 빈을 가져올 때마다 매번 새로운 인스턴스가 생성되어 반환된다.
  • 매번 새로운 객체 필요한 경우 - 상태저장?
  • 생성 시점에 의존성 주입 되기 때문에 소멸 아니면 소멸 명시를 해줘야한다.

 

12. AOP에 대해 설명해 주세요.

관점 지향 프로그래밍으로 관점을 기준으로 다양한 기능을 분리하여 보는 프로그래밍

객체지향프로그래밍을 보완하기 위해 쓰인다.

3가지 적용 방식

  1. 컴파일 시점 적용
    1. AspectJ컴파일러가 일반 .java파일 컴파일할때 부가기능 넣어서 .class파일로
  2. 클래스 로딩 시점 적용
    1. 클래스 로더에 .class파일 올릴때 바이트 코드를 조작해 부가기능 로직 추가
  3. 런타임 시점 적용
    1. DI, 빈 을 이용해서 프록시를 통해 부가기능 적용

스프링에서는 앞의 두개는 별도의 컴파일러와 클래스 로더를 사용해야해 어렵고 복잡해 런타임시 AOP방식 사용

@Aspect는 어떻게 동작하나요?

  • 애스펙트 정의:
    • @Aspect 어노테이션을 사용하여 애스펙트를 정의합니다. 애스펙트 클래스 내에는 포인트컷(Pointcut)과 어드바이스(Advice)를 정의합니다.
  • 포인트컷(Pointcut):
    • 포인트컷은 어디에 어드바이스를 적용할지 정의하는 표현식입니다. 주로 메서드 실행 지점에 대해 정의됩니다.
    • 표현식 언어를 사용하여 특정 패키지, 클래스, 메서드를 선택할 수 있습니다.
  • 어드바이스(Advice):
    • 어드바이스는 포인트컷에서 정의한 위치에 적용할 구체적인 동작을 정의합니다. 어드바이스에는 여러 종류가 있습니다:
      • @Before: 메서드 실행 전에 실행
      • @After: 메서드 실행 후에 실행
      • @AfterReturning: 메서드가 정상적으로 실행된 후에 실행
      • @AfterThrowing: 메서드 실행 중 예외가 발생한 후에 실행
      • @Around: 메서드 실행 전후 또는 예외 발생 시 실행
  • 프록시 생성:
    • Spring AOP는 런타임에 프록시 객체를 생성하여 애스펙트를 적용합니다. 기본적으로 JDK 동적 프록시 또는 CGLIB 프록시를 사용합니다.
    • 클라이언트가 타겟 객체의 메서드를 호출할 때, 프록시 객체가 호출을 가로채고 어드바이스를 실행합니다.

— >스프링에서 AOP만드는 방식 2가지, 인터페이스 or 구체 클래스 구체 클래스 권장스프링이 프록시로 하는 이유 직접 참조로 하면 aspect의 부가기능을 계속 사용하면서 성능이 안좋아진다.?

  • cg lib collection

cglib 프록시와 JDK dynamic 프록시

반응형

'Java' 카테고리의 다른 글

CS 스터디 회고 - 운영체제 1  (0) 2024.12.01
Java 면접(인터뷰) 스터디 회고 3  (0) 2024.11.21
[CS 스터디 회고] JAVA 면접 질문 대비  (1) 2024.11.08
JAVA JVM Memory 정리  (1) 2024.07.08
Java priority queue  (0) 2023.09.30