5. 어노테이션과 MetaSpace, 배열 병렬 정렬
Java8에 변경된 사항중 어노테이션과, MetaSpace, 배열 정렬 알고리즘에 대해 정리한 글
애노테이션의 변화
1. 애노테이션 관련 두가지 큰 변화
자바 8부터 애노테이션을 타입 선언부에도 사용할 수 있게 됨
자바 8부터 애노테이션을 중복해서 사용할 수 있게 됨
1-1. 타입 선언 부
제네릭 타입
변수 타입
매개변수 타입
@Target(ElementType.TYPE_PARAMETER)
예외 타입
etc
1-1-1. 기존의 애노이션 Target 종류
TYPE : 클래스 / 인터페이스 / 어노테이션 / enum / record의 선언부
ANNOTATION_TYPE : 어노테이션의 선언부
FIELD : 클래스내의 내부필드의 선언부
METHOD : 클래스내의 메서드의 선언부
PARAMETER : 메서드내의 파리미터의 선언부
CONSTRUCT : 생성자의 선언부
LOCAL_VARIABLE : 지역변수의 선언부
PACKAGE : 해당 패키지의 package-info.java 에 붙일 수 있다.
1-1-2. java8에 추가된 Target
타입에 애노테이션을 사용하기 위해 아래와 같은 ENUM 타입을 명시해야 함.
TYPE_PARAMETER: 타입 변수에만 사용할 수 있다.TYPE_USE: 타입 변수를 포함해서 모든 타입 선언부에 사용할 수 있다.위의 어노테이션들과는 다르게 선언 주석이 아닌
TYPE 주석이다.선언 주석 : 주의사항, 사용방법, 사용처 등 설명
TYPE 주석 : 정수 값이 0보다 커야 한다, null이 아니다 와 같은 값에 대한 정보 제공함으로써 implements, throws, new 구절에 사용하거나 제네릭에 사용함으로써 외부의 프로젝트에도 적용할 수 있도록 확장한 범위
TYPE_PARAMETER와 TYPE_USE에 대해 더 자세히 알아보기 위해 다음 예제를 살펴보자.
TYPE_PARAMETER
타입 선언부에 사용이 가능
컴파일하면서 해당 타입이 무슨 타입인지 분석하여 CLASS_TYPE_PARAMETER / METHOD_TYPE_PARAMETER 로 변환하는 것을 볼 수 있다.
타입 선언부에 사용이 가능한 Target이므로 아래와 같이 실제 사용하는 부분에는 적용할 수 없다.
TYPE_USE
선언부 뿐만이 아닌 타입 사용되는 모든 곳에 적용이 가능 (클래스/인터페이스/내부필드/파라미터/제네릭/지역변수 등)
바이트 코드를 보면 TYPE_USE를 사용해도 컴파일 결과는 컴파일러가 적절한 어노테이션 TARGET으로 바꾸는 것을 볼 수 있다.
Lombok의 NonNull 어노테이션으로 해당 어노테이션도 Target범위가 TYPE_USE가 포함되어 있다.
1-2. 중복 사용할 수 있는 애노테이션을 만들기
중복 사용할 애노테이션 만들기
중복 애노테이션 컨테이너 만들기
컨테이너 애노테이션은 중복 애노테이션과
@Retention및@Target이 같거나 더 넓어야 한다.
1-2-1. 중복 사용할 애노테이션 생성
기존에는 애노테이션을 같은 범위에 중복해서 정의할 수 없었는데 java8 부터는 @Repeatable()이 추가되어 중복해서 사용이 가능해졌다.
Repeatable은 한개의 value를 가지고 있는데, 여기에 일종의 애노테이션 컨테이너 역할을 할 애노테이션 클래스를 넘겨주면 해당 컨테이너에 중복 사용한 애노테이션들을 담는 방식으로 동작하게 된다.
이때 주의할 점이 Repeatable의 value는 애노테이션의 컨테이너 역할이기 때문에 중복해서 사용할 어노테이션보다 생명주기(RetentionPolicy)가 길어야만 한다.
1-2-2. 중복 애노테이션의 컨테이너 생성
1-2-3. 컨테이너 애노테이션으로 중복 애노테이션 참조
@Chicken은 default value가 없으면 에러가 발생한다.
더 나아가서 main에서 2번째로 방법으로 컨테이너 애노테이션 타입으로 읽어들일 때 바이트 코드를 살펴보자.
ChickenContainer 애노테이션을 생성하여 Chicken 애노테이션들을 value로 할당하는 것을 볼 수 있다.
MethodHandles$Lookup라는 이름의 클래스로 innerClass가 만들어지는 것을 볼 수 있는데 이는 메서드 핸들을 생성하기 위한 factory class로 ChickenContainer의 value()인 Chicken[]에 접근하기 위한 class 이다.
배열 병렬 정렬
Arrays.parallelSort()가 추가되어 분산되어 정렬이 가능하다.
1. Arrays.parallelSort()
Fork/Join 프레임워크를 사용해서 배열을 병렬로 정렬하는 기능을 제공한다.
2. 병렬 정렬 알고리즘
배열을 둘로 계속 쪼갠다.
합치면서 정렬한다.
3. sort()와 parallelSort() 비교
알고리즘 효츌성은 같다.
시간복잡도 : O(n logN)
공간복잡도 : O(n)
단, 정렬하는 배열의 크기에따라 속도가 차이날 수 있다.
참고
size가 15000일 때 나오는 속도
Arrays.sort()
Arrays.sort() 알고리즘은 기본적으로 DualPivotQuicksort를 사용한다.
이 알고리즘은 기본적으로
Insertion Sort
Merge Sort
Quick Sort
위 3개의 알고리즘을 사용하는데 보통 1,2, 1,3을 섞어서 사용한다.
Arrays.parallelSort()
반면 Arrays.parallelSort() 는 Merge Sort를 사용함.

in-place 정렬이 아니기 때문에 배열이 커지면 느려질 수 밖에 없다.
Meta space
JVM의 여러 메모리 영역 중에 PermGen 메모리 영역이 없어지고 Metaspace 영역이 생겼다.
1. PermGen
메모리 구조

permanent generation, 클래스 메타데이터를 담는 곳.
Heap 영역에 속함.
기본값으로 제한된 크기를 가지고 있음.
동적으로 클래스를 생성하는 경우 PermGen 영역이 가득 차는 경우가 발생할 수 있다. 메모리 에러 발생.
위의 문제를 근본적으로 해결하려면 PermGen 사이즈를 늘리는 것이아니라 클래스를 동적으로 생성하는 코드를 수정해야한다.
XX:PermSize=N, PermGen 초기 사이즈 설정
XX:MaxPermSize=N, PermGen 최대 사이즈 설정
Old/Eden 영역보다 적은 메모리사이즈를 가지고 있으며 os bit, jdk 버전별로 다르다. 고정된 크기를 가지기 때문에 클래스를 계속해서 만들게 되면 GC가 메모리를 정리해도 PermGen사이즈를 넘어서면 memory out 에러가 발생한다.
2. Metaspace
메모리 구조

클래스 메타데이터를 담는 곳.
Heap 영역이 아니라, Native 메모리 영역이다.
기본값으로 제한된 크기를 가지고 있지 않다.
Native 메모리 영역이 가득찰 때까지 필요한 만큼 계속 늘어난다.
Native 메모리 영역이 가득차게 되면 서버가 죽는 치명적인 문제가 발생할 수 있다.
따라서 최소한 Metaspace의 최대 사이즈는 설정해주어야한다.
효율적으로 사용하려면 모니터링 하여 최적의 Metaspace 값을 찾아 최대값으로 설정하는 것이다. 만약 모니터링으로 도출해낸 이 값에 메모리 문제가 생기면 어딘가의 메모리 누수를 의심해보아야 한다.
자바 8부터는 PermGen 관련 java 옵션은 무시한다.
XX:MetaspaceSize=N, Metaspace 초기 사이즈 설정.
XX:MaxMetaspaceSize=N, Metaspace 최대 사이즈 설정.

Ref.
Last updated