JAVA JIT 컴파일러/ 역최적화 / 티어드 컴파일 레벨

25gStroy·2022년 5월 31일
0

JAVA

목록 보기
6/18

역최적화

역최적화는 컴파일러가 선생한 컴파일의 일부를 원상태로 되돌리는 것을 의미합니다.

이에 대한 영향은 컴파일러가 대상 코드를 다시 컴파일 할 수 있을 때 까지 애플리케이션의 성능은 감소합니다.
(그렇게 크지는 않다)
역최적화는 코드가 진입 불가와 좀비화일때 발생합니다.

진입불가

코드에 진입하지 못하게 만드는 요인은 두 개가 있습니다.

하나는 클래스와 인터페이스가 동작하는 방식에 기인한 것이고 다른 하나는 티어드 컴파일의 구현 세부 사항입니다.

주식 어플리케이션은 인터페이스로 StockPriceHistory를 가지고 있습니다. 이 인터페이스의 구현체로 StockPriceHistoryImpl과 로그를 쌓기 위한 인터페이스 구현체로 StockPriceHistoryLogger가 있습니다.

서블릿 코드에서는 URL 피러미터로 구현체를 구별합니다.

StorkPriceHistory sph;

String log = request.getParameter("log");

if(log != null && log.equals("true")){
    sph = new StockPriceHistoryLogger(); 
}else{
    sph = new StorkPriceHistoryImpl(); 
}

이 예제에서 만약 로그 요청 없이 http://localhost:8080/StockServlet 로 다수의 호출이 일어났다면 컴파일러는 sph의 객체의 실제 타입이 StockPriceHistoryImpl 이라는 걸 알고 최적화 할 것입니다.

하지만 나중에 로그 요청을 포함한 http://localhost:8080/StockServlet?log=true 요청을 호출한다면 sph 객체의 타입에 대한 컴파일러의 최적화는 틀렸다라고 판단하고 이전 최적화는 더 이상 유효하지 않을 것입니다.

이로 인해 역최적화의 함정에 빠져 이전 최적화는 폐기되고 진입 불가 상태가 되고 후에 좀비화가 될 것입니다.

역최적화가 처리되는 순간적인 시점을 제외하고는 적어도 성능 측면에서 역최적화는 그렇게 나쁘지 않습니다.

두번째 요인은 티어드 컴파일이 동작하는 방식입니다.
티어드 컴파일에 의해 먼저 클라이언트 컴파일러로 컴파일이 되고 후에 서버 컴파일러에 의해 재컴파일 됩니다.

즉 서버 컴파일러에 의해 재컴파일할 때 컴파일된 코드를 교체합니다. 이는 오래된 코드를 진입 불가로 만들고 새로 교체될 코드로 대체하기 위한 과정입니다.

좀비 코드 역 최적화

성능 측면에서 좀비 코드의 역최적화는 좋은 일입니다.

코드는 고정 크기의 코드 캐시에서 컴파일 됩니다. 좀비 메소드가 발견되면 다른 클래스를 컴파일할 공간을 코드 캐시에서 제거하면서 만듭니다.

티어드 컴파일 레벨

티어드 컴파일을 사용하는 프로그램에 대한 컴파일 로그에는 각 메소드가 컴파일된 티어 레벨이 출력됩니다.

각 레벨의 컴파일은 다음과 같습니다.

0: 인터프리트된 코드

1: 단순 C1 컴파일된 코드

2: 제한된 C1 컴파일된 코드

3: 전체 C1 컴파일된 코드

4: C2 컴파일된 코드

전형적인 컴파일 로그를 보면 대부분의 메소드는 3레벨인 전체 C1 컴파일된 코드로 처음 컴파일된다는 걸 보여줍니다.

모든 메소드는 0레벨부터 시작하고 매우 빈번하게 수행된다면 3레벨을 거쳐서 4레벨에서 컴파일 될 것입니다. 그리고 3레벨의 코드는 진입 불가 상태가 됩니다.

만약 서버 컴파일러 큐가 가득차서 4레벨의 컴파일이 불가능하다면 프로파일 피드백을 필요로 하지않는 2레벨에서 컴파일 될 것입니다. 그 후 프로파일 정보를 모은 후 C1 컴파일러가 3레벨에서 컴파일 하고 마지막으로 서버 컴파일러 큐가 덜 바쁘다면 4레벨에서 컴파일 됩니다.

반면 클라이언트 컴파일러 큐가 가득 차면 3레벨에서 컴파일될 예정인 메소드가 4레벨 컴파일 대상이 될 수 있습니다. 이 경우에는 2레벨로 컴파일 된 다음 바로 4레벨로 넘어갑니다.

그리고 코드가 역최적화될 때 0레벨로 갑니다.

profile
애기 개발자

0개의 댓글