티스토리 뷰

Java & Kotlin

[Java] 자바 9에 개선 및 최적화된 String 내부 구조(JEP254: Compact Strings)

망나니개발자 2025. 1. 14. 10:00
반응형

 



1. 자바 9에 개선 및 최적화된 String 내부 구조 (JEP254: Compact Strings)


[ 자바 8까지의 String 클래스의 문제점 ]

자바 8까지는 내부적으로 char형 배열을 사용하여 문자열을 다루고 있었다.

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    
    private final char value[];

    private int hash;
    
    // ...
}

 

 

 

하지만 이러한 방식은 메모리 비효율적인 문제가 있었다. 왜냐하면 자바의 Char 타입과 String 타입은 유니코드를 통해 전 세계적으로 일관되게 텍스트 데이터를 표현하고자 그 당시에 주목받던 UTF-16을 활용하기 때문이다.

UTF-16은 가변 길이 인코딩 방식으로, 대부분의 문자는 2바이트를 사용하지만, 보조 평면 문자(U+10000 이상)는 4바이트를 사용한다. 하지만 UTF-16은 당시 주류였던 아스키코드와 호환되지 않으며, 아스키코드에서는 대부분의 사용 비중을 차지했던 영어가 1바이트만 사용되는 데 반해 UTF-16은 영어에도 2바이트를 사용하는 등의 문제가 있었다.

그 외에도 여러 가지 이유들로 인해 현재는 UTF-8이 사실상 표준이 되었지만, 내부적으로 UTF-16을 사용하는 자바의 Char 타입과 이를 활용하는 String 타입에 의해 불필요한 메모리 낭비가 존재했다.

 

 

 

[ 자바 9부터 개선된 String 클래스의 내부 동작 ]

자바 언어의 개발자들 역시 이러한 문제를 인지하고 있었고, 자바 6에 -XX:+UseCompressedStrings 옵션을 통한 문자열 압축 기능으로 최적화 시도를 했었지만, 의도치 않은 성능 저하로 인해 자바 7에 해당 기능이 바로 제거되었다. 그리고 이후에 JEP 254:Compact String이라는 내용으로 해당 부분을 다시 개선하고자 하였다.

자바 9부터는 UTF-16이 아닌 LATIN-1 표현을 사용하여 char[]가 아닌 byte[]로 문자열을 저장하게 되었다. 참고로 ISO_8859_1으로도 불리는 LATIN-1은 기존 아스키코드에 서유럽 문자를 표현하는 문자 집합이 추가된 것이다. LATIN-1은 영어와 같이 한 문자만 필요한 경우에는 1바이트만 사용하므로 문자열 사용에 따른 메모리 사용량을 절감 할 수 있게 되었다.

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    
    @Stable
    private final byte[] value;
    
    private final byte coder;
    
    private int hash;
    
    // ...
    
    static final byte LATIN1 = 0;
    static final byte UTF16 = 1;
    static final boolean COMPACT_STRINGS;

    
    static {
        COMPACT_STRINGS = true;
    }
    
    public int indexOf(int ch, int fromIndex) {
        return isLatin1() 
            ? StringLatin1.indexOf(value, ch, fromIndex) 
            : StringUTF16.indexOf(value, ch, fromIndex);
    }
}  

    private boolean isLatin1() {
        return COMPACT_STRINGS && coder == LATIN1;
    }
}

 

 

이로 인해 문자열의 길이를 계산하는 로직 등에도 최적화가 가능해졌다. 문자열이 LATIN-1만 포함하는 경우, coder의 값은 0이 되어 문자열의 길이는 바이트 배열의 길이와 동일하다. 문자열이 UTF-16 표현인 경우 coder의 값은 1이 되고, 따라서 길이는 실제 바이트 배열 크기의 절반이 된다.

public int length() {
    return value.length >> coder;
}

 

 

 

 

 

즉, 자바9 이전에는 String에서 1바이트만 필요한 경우에도 내부적으로 2바이트가 사용되었지만, 자바 9부터는 String 내부의 개선을 통해 필요한 만큼의 메모리만 사용 가능하도록 개선이 있었다. 메모리 사용량이 줄어든다는 것은 그 만큼의 GC 발생 역시 줄어든다는 것을 의미하여 큰 효과가 있을 것으로 기대되며, 내부 벤치마킹 결과에 따르면 Latin형식의 문자열이 20%정도 빠르며 30%정도 가비지가 적었다고 한다. 그리고 무엇보다도 Compact String에 대한 모든 변경 사항은 String 클래스의 내부 구현에만 적용되며, String을 사용하는 개발자에게는 어떠한 영향도 미치지 않는다.

다음과 같은 상황에서는 해당 기능의 비활성화를 고려할 수 있다고 하는데, 이를 위해 새로운 JVM 옵션 -XX:-CompactStrings가 도입되었다.

  1. JVM/애플리케이션에서 사용되는 문자열 객체가 다중 바이트 문자 문자열이 압도적으로 많은 경우
  2. JDK 8에서 JDK 9로 마이그레이션하는 과정에서 Compact String에 의한 심각한 성능 저하가 발생한 경우

 

 

 

참고 자료

 

 

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG more
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함