Post

자바 9와 미래

15. 자바 9와 미래

자바 9는 ‘모듈과 그 나머지’

모듈은 소프트웨어를 개발/배포하는 완전히 새로운 방식이라서 하나씩 단계적으로 도입하기가 쉽지 않음

15.1 자바 9에서 소소하게 개선된 기능

15.1.1 코드 캐시 세그먼트화

자바 9부터 코드 캐시 성능을 개선코자 다음 항목별 영역으로 분리

  • 인터프리터 등의 논메서드 코드(컴파일러 버퍼, 바이트코드 인터프리터 등 JVM 내부에 구현된 논메서드 코드)
  • 프로파일드 코드
  • 논프로파일드 코드

덕분에 (논메서드 영역은 스위핑할 필요가 없으므로) 스위퍼 가동 시간이 짧아지고 풀 최적화 코드에 대한 코드 지역성이 향상됨

15.1.2 콤팩트 스트링

자바 9 이후는 콤팩트 스트링(compact string) 덕분에 스트링 단위로 최적화 가능

자바 9부터는 value 필드 타입이 이전 버전과 달리 char[]가 아니라 byte[]

컴팩트 스트릉 기능은 -XX:-CompactStrings, -XX:+CompactStrings(디폴트) 스위치로 각각 끄고 켤 수 있다

15.1.3 새로운 스트링 연결

StringConcatFactory.makeConcatWithConstants()라는 팩토리 메서드로 스트링 연결 레시피를 제공

이 기법을 응용하면 새로운 커스텀 메서드용 바이트코드 작성 등 다양한 전략 구사 가능

15.1.4 C2 컴파일러 개선

현대 CPU에 선보인 SIMD(single instruction multiple data) 확장 기능을 응용하여 성능 개선 기대 가능

다른 프로그래밍 환경에 비해 자바/JVM은 플랫폼 특성 덕분에 SIMD를 더 효과적으로 활용하기 좋은 조건을 가지고 있다.

  • 바이트코드는 플랫폼과 무관
  • JVM은 시동 시 CPU를 탐색하므로 자신이 어떤 능력을 지닌 하드웨어에서 실행되고 있는지 런타임에 파악 가능
  • JIT 컴파일은 코드를 동적 생성하기 때문에 사용 가능한 모든 명령을 호스트에서 사용 가능

핫스팟은 이미 다음과 같은 x86 SIMD 명령어를 지원

  • 자바 코드의 자동 벡터화(automatic vectorization)
  • 순차 코드에서 SIMD 코드를 얻기 위해 C2에서 슈퍼워드(superword) 최적화
  • 배열 복사/적재/비교 등의 JVM SIMD 인트린직

자바 9 버전은 SIMD의 장점 및 그와 연관된 프로세서 특성을 더 잘 활용하고자 기존 인트린직을 개선하거나 새로운 인트린직을 탑재

일반적으로 인트린직은 범용 기술이 아닌, 특정 해결책

SIMD은 유용한 기술이지만, 성능 엔지니어에게는 확실히 수확 체감(diminishing return, 성능을 개선하려는 목표를 성취하기 위한 단위 비용은 더 들어감)을 안겨주는 접근 방법

15.1.5 G1 새 버전

G1은 중단 시간을 쉽게 튜닝하고 더 효과적으로 제어하는 특성을 지닌, 여러 가지 문제를 한꺼번에 해결하고자 설계된 수집기

자바 9부터 디폴트 수집기

15.2 자바 10과 그 이후 버전

15.2.1 새로운 릴리즈 절차

자바 10부터는 정확한 시기에 릴리즈되는 모델로 개편

자바 새 버전이 6개월마다 한번씩 릴리즈

오라클은 일부 특성 릴리즈에 대해서 장기 지원(long-term support, LTS)을 제공

15.2.2 자바 10

지역 변수를 선언할 떄 반복되는 코드를 줄여 쓸 수 있음

1
2
var list = new ArrayList<String>();
var stream = list.stream();

이 구문은 for 루프의 초기자, 지역 변수에만 쓸 수 있게 제한됨

의미(sematincs) -> 구문(syntax) -> 어휘 구문(lexical syntax) -> 주석(comment), 이렇게 하위 스케일로 내려갈수록 언어 특성을 둘러싼 감정적인 논쟁이 더욱 격해짐

생략

15.3 자바 9 Unsafe 그 너머

자바 9에는 –illegal-access라는 스위치가 추가돼서 이 API에 대한 런타임 액세스를 조정 가능

자바 9에서 이들 API는 jdk.unsupported라는 JDK에 종속된 모듈

15.3.1 자바 9의 varHandle

메서드 핸들은 실행 가능한 메서드의 레퍼런스를 직접 조작할 수 있게 해주지만, 게터/세터 액세스만 지원하고 필드까지 100% 완벽하게 지원하는 건 아님

자바 9부터 메서드 핸들을 가변 핸들(variable handle)까지 포괄하도록 확장됨

Unsafe를 대체하기 위한 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class AtomicIntegerWithVarHandles extends Number {
	private volatile int value = 0;
  private static Final Varhandle V;
  
  static {
    try {
      MethodHandles.Lookup l = MethodHandles.lookup();
      V = l.FindVarHandle(AtomicIntegerWithVarHandles.class, "value", int.class);
    } catch (ReflectiveOperationException e) {
      throw new Error(e);
    }
  }
  
  public Final int getAndSet(int newValue) {
    int v;
    do {
      v = (int)V.getVolatile(this);
    } while(!V.compareAndSet(this, v, newValue));
    
    return v;
  }
}

15.4 발할라 프로젝트와 값 타입

발할라 프로젝트의 주요 목표

  • JVM 메모리 레이아웃을 최신 하드웨어의 비용 모델에 맞게 조정
  • 제네릭스(generics)가 기본형, 값, 심지어 void까지 포함하도록 모든 타임에 추상화
  • 기존 라이브러리, 특히 JDK가 이러한 특성을 최대한 활용하는 방향으로 호환성을 유지하며 진화하도록 한다

JVM에서 값 타입의 사용 가능성을 모색하는 것이 이 프로젝트에서 가장 역점을 둔 연구 분야

기본형 int 타입 배열들은 원소들이 나란히 배열됨

박싱된 Integer는 객체라서 레퍼런스로 참조하며, 당연히 Integer 객체 배열은 레퍼런스의 배열

단순하지만, 객체 배열을 처리하려면 간접화는 불가피하며 캐시 미스도 수반되므로 성능 감소는 감수해야 함

내장 기본형과 비슷하게 동작하는 사용자 정의 타입 등 다른 가능성도 모색 가능

자바는 최상위 타입이 없기 때문에 Object와 int 모두의 슈퍼타입은 존재하지 않음

즉, 자바의 타입은 하나의 조상에서 분화된 객체가 아님

이런 이유로, 자바 제네릭스는 참조형에만 적용됨

값 타입은 반드시 제네릭스를 개선한 형태에서 타입 매개변수 값으로 유효하다는 전제하에 설계해야 함

생략…

15.5 그랄과 트러플

새로운 대체품을 찾는 연구 프로젝트는 현재 그랄(Grall, 특수한 JIT 컴파일러)트러플(Truffle, JVM 런타임에 호스팅된 언어에 대한 인터프리터 생성기) 위주로 진행됨

C2 컴파일러는 C++로 작성되어 있어서 자칫 심각한 문제를 일으킬 소지가 있다

그랄은 JVM용 JIT 컴파일러를 자바 언어로 개발함

그랄 프로젝트의 사상은, JIT 컴파일러는 JVM 바이트코드를 받아 기계어를 생성하기만 하면 된다는 것

자바를 자바로 접근하는 방식은 단순함, 메모리 보안 등 여러 면에서 좋음

트러플은 JVM에 기반한 언어 전용 인터프리터를 개발하는 프레임워크로, 입력 언어에 대한 고성능 JIT 컴파일러로 인터프리터에서 자동 생성하는 라이브러리

최근 실시간 시스템에서 점점 더 많이 응용되고 있다

트러플과 그랄을 함께 사용하면 그 전보다 성능이 대폭 향상됨

이 모든 작업이 메트로폴리스 프로젝트(Project Metropolis)라는 새로운 프로젝트의 일부로 진행 중

아직은 시험 버전 수준

1
-XX:+UnlockExperimentalVMOptions -xx:+EnableJVMCI -XX:+UseJVMCICompiler

서브스트레이트VM(SubstrateVM)은 기능을 더욱 확장시켜 아예 자바 애플리케이션과 JVM을 통째로 컴파일하여 정적으로 링크된 하나의 네이티브 실행 코드를 생성하려는 연구 프로젝트

15.6 바이트코드의 항후 발전 방향

invokedynamic는 JVM 바이트코드 작성 방법을 다시 생각하게 만든 도화선 역할

메서드 핸들을 이용하면 상수 풀에도 아주 큰 영향을 미침

invokedynamic을 지원하려면 이런 새로운 종류의 상수가 필요함

궁극적인 목표는, invokedynamic으로 메서드를 호출하더라도 기존 invokevirutal처럼 JIT 컴파일하기 좋고 성능도 좋게 나올 수 있게 만드는 것

15.7 동시성의 향후 발전 전망

룸 프로젝트(Project Loom)는 지금까지 동시성을 JVM 상에서 지원했던 것과 달리, 더 저수준에서 지원하는 방안을 모색

코어 자바 스레드의 근본적인 문제점은 모든 스레드가 스택을 달고 다닌다는 점

OS가 직접 스케줄링할 수 없는 실행 유닛은 오버헤드가 낮고 어쩌면 ‘거의 죽은’ 것이나 다름 없다

이 부분이 다른 언어의 접근 방법과 멋진 조화를 이룸

자바 7부터 등장한 포크/조인 API는 실행 가능한 태스트킈 재귀 분해와 작업 훔쳐오기, 두 가지 개념에 기초

재귀 분해가 모든 태스크에 다 유용한 건 아니지만, 작업 훔쳐오기 알고리즘이 가미된 실행다 스레드 풀은 다양한 상황에 적용 가능

참조

  1. Optimizing Java(자바 최적화)
This post is licensed under CC BY 4.0 by the author.