상세 컨텐츠

본문 제목

Java 8?

Log.Develop/Other

by bluayer 2022. 2. 15. 15:11

본문

서론

일반적으로 Java로 작업을 하다보면 Java 8과 11을 보통 많이 쓰는 것 같다.

문득, "왜 그렇지?"라는 생각이 들었고 특히 Java 8에서 어떤 변경사항이 있었는지 궁금해졌다.

따라서 알아보기로 결정했다.

 

Java 8?

Java 8에서 자바 개발자가 궁금해할만 한 변경 사항은 다음과 같다.

  • Lambda Expression & Method Reference
  • Stream API & Parallel
  • Optional
  • Interface Default Method
  • java.time 패키지
  • PermGen 제거

장난 아니게 많다. 심지어 다 중요한 변경들이다.

특히 함수형 프로그래밍(FP)와 관련한 이야기들이 많다.

위의 내용에서 한 주제씩만 해도 글 하나가 나올 수 있을 정도라고 할 수 있다.

하나씩 빠르고 간단하게 짚어보자.

 

Lambda Expression & Method Reference

람다 미적분학(Lambda Calculus)에서 따온 그 람다다.

이름을 가질 필요가 없기 때문에 익명 함수라고도 불리며 일급 객체의 특성을 가진다.

일급 객체이기 때문에 함수를 값으로 사용할 수도 있고 파라미터로 전달하거나(일명 동작 파라미터, Behavior Parameter) 변수에 대입할 수도 있다.

@FunctinoalInterface를 이용해 유연하게 구현하는 것도 가능하다.

Method Reference는 메소드를 쉽게 넘겨줄 수 있는 방식이다.

말 그대로 클래스의 메소드를 인자로 넘겨줄 수 있다.

const userCollection: List<User>
// 람다 방식
userCollection.stream().map(user -> user.getAge())

// Method Reference를 사용
userCollection.stream().map(User::getAge)

 

Stream & Parallel

스트림은 위에서도 썼지만, for나 while문 혹은 for-each문으로 collection 순환하던 방식을 함수형으로 바꿔서 쓸 수 있도록 도와주는 역할을 한다.

제어 관련한 부분의 코드가 간결해지고 가독성이 좋아진다는 장점이 있다.

사실 이 Stream이 나오게 된 계기는 병렬(parallel) 처리의 간편함을 위해서라고 한다.

java 7에는 병렬 처리를 더 간편하게 하기 위해서 Fork/Join 방식이 추가되었는데,

이런 Fork/Join을 더 쉽게 할 수 있도록 parallelStream과 parallel이 지원된다.

아래와 같이 parallelStream만 추가하면 알아서 fork/join 프레임워크를 이용하여 처리해준다.

// parallelStream
userCollection.parallelStream().map(User::getAge)

(단, 여러 벤치마크 테스트를 확인해보면 알 수 있듯 복잡하거나 무거운 연산을 할 때 parallelStream을 쓰는 것이 유리하다. 가벼운 연산일수록 성능이 오히려 저하될 가능성이 높다.)

 

Optional

이제는 다른 언어에서도 많이 지원하기 때문에 아주 짧게 설명하겠다.

null 처리를 도와주는 방식으로, 데이터가 없거나 있을지 불분명한 경우를 처리하기 위해 사용한다.

그렇다고 Optional을 마구잡이로 쓰는게 늘 좋지는 않다.

오히려 복잡성을 높여 오류 가능성을 키우는 상황이 존재할 수 있음을 인지하자.

Optional Anti-pattern과 관련된 글을 첨부한다.

https://dzone.com/articles/optional-anti-patterns

 

Optional Anti-Patterns - DZone Java

Optionals are great for handling data that might not be present, but they are easy to use incorrectly. Here are anti-patterns and code smells to avoid.

dzone.com

 

Interface Default Method

인터페이스에 디폴트 메서드를 작성할 수 있게 되었다.

도대체 이 디폴트 메서드가 왜 필요하지?라고 생각할 수 있다.

인터페이스인데 구현이 들어가는게 어떤 의미일까?

만약 인터페이스에서 변경이 일어나게 되면, 인터페이스를 구현하는 모든 클래스들이 해당 메소드를 구현해야 하는 문제가 있었다.

이런 문제를 해결하기 위하여 인터페이스에 메소드를 구현해 놓을 수 있도록 하였다.

public interface Calculator {
	public int plus(int i, int j);
	public int multiple(int i, int j);
	default int minus(int i, int j) {
		return i * j;
	}
}

 

java.time 패키지

원래 자바에서는 java.util.Date와 같은 패키지를 사용했었다.

하지만 Calendar와 Date를 이용하는 방식은 큰 문제가 있었는데,

  • 1월을 0으로 표현한다. 세상에!
  • 어느 곳에서는 일요일이 0이고, 어느 곳에서는 일요일이 1이다. 패키지를 다 뜯어봐야한다!
  • 불변 객체가 아니다. 즉, 어떤 함수에서 바꿨는지도 모르는 상황이 발생할 수 있다.

그래서 java8이 나오기 전까지 메이저하게 사용되고 있던 joda-time을 java.time 패키지로 포함하게 되었다.

joda-time은 위에서 이야기했던 문제들을 잘 해결했고, 좀 더 세심하게 다루고 있다.

 

PermGen 제거

사실상 Java 8의 중요한 이슈다.

이해를 위해 먼저 Java에서 프로그램을 어떻게 실행하는지 살펴보자.

  1. 컴파일을 통해 Class 파일 생성(ByteCode) 및 JVM으로 로딩
  2. ByteCode를 인터프리트하여 리소스 할당 관리

Interpreter 방식

ByteCode 해석 과정에서 하나씩 "읽고 쓰는"데 당연히 실행이 느림.

따라서 JIT Compiler 등장!

 

Jit Compiler 방식

인터프리터 방식으로 실행하다가 적절한 시점에 바이트 코드 전체 컴파일해서 네이티브 코드로 변환하고, 캐시에 있는 네이티브 코드를 실행하게 된다.

 

여기서 잠깐!!

Native Code : OS에 의해 직접적으로 컴파일되는 코드로, 보통 컴퓨터 기계어로 작성되는 경우가 많다.

 

Java 7 Hotspot JVM Memory 구조

 

Java 8 Hotspot JVM Memory 구조

 

java 7에서 Perm 영역은 이런 정보들을 담고 있었다.

  1. Class 의 Meta정보
  2. Method의  Meta 정보
  3. Static Object
  4. 상수화된 String Object
  5. Class와 관련된 배열 객체 Meta 정보
  6. JVM 내부적인 객체들과 최적화컴파일러(JIT)의 최적화 정보

 

아무래도 Native Memory에 Perm 영역이 있는 것이 아니다보니

OOM(Out Of Memory)이 흔하게 발생했다고 한다.

 

예시로,

  1. static Collection 객체 생성
  2. 값을 계속 추가
  3. Perm 영역이 가득 찼음 ⇒ OOM 발생

위와 같은 경우들이 빈번하게 발생했다고 한다.

또한 Class나 Method에 대한 Meta-data가 늘어날수록 공간이 좁아지는 문제도 있었다.

(참고로 Reflection은 이런 메타데이터들을 이용한다.)

 

결론적으로 Java 8에서는 Perm 영역을 제거하게 되었고, Metaspace라는 공간을 Native Memory 쪽에 추가하게 되었다.

따라서 Perm 영역에 저장하던 데이터들을 Heap이나 Native 쪽으로 옮겨가게 되었다.

 

Static object는 heap으로 옮겨서 GC 대상으로 바꾸게 되었으며,

수정이 적은 정보에 관해서는 Native에 존재하는 Metaspace로 몰아넣고 Metaspace사이즈를 JVM이 자동 조정하도록 개선되었다.

즉, Metaspace가 Native 메모리를 이용함으로서 개발자는 메모리 영역 확보의 상한을 크게 의식할 필요가 없어지게 되었다.

 

개인적으로 PermGen과 관련해서는 해당 레퍼런스를 추천한다.

https://johngrib.github.io/wiki/java8-why-permgen-removed/

 

JDK 8에서 Perm 영역은 왜 삭제됐을까

 

johngrib.github.io

 

관련글 더보기

댓글 영역