Language/JAVA

jdk15 charAt 분석해보기

String class의 내장함수 charAt분석

 

 

로직설명

 

  1. 라틴문자 혹은 UTF16인지 구별한다. 
  2. index 변수를 받아서 byte[] value 변수의 길이(value.length)의 범위를 index가 넘어간다면 StringIndexOutofBoundException을 throws 한다.
  3. 범위를 넘어가지 않는다면 byte배열인 value의 index 번째의 값과 0xff를 비트 논리곱 연산하여 양수값만 도출 후 char형으로 캐스팅  ((char)value[index] & 0xff)
  4. 캐스팅한 문자를 리턴

latin1 or utf16 -> 바이트 배열의 byte[index] & 0xff로 만든 바이트 값을 char형으로 캐스팅하고 리턴해주는 것으로 보입니다.

java15 기준

public char charAt(int index) {
        if (isLatin1()) {// 무엇을 가지고 라틴임을 판별하는가?
            return StringLatin1.charAt(value, index);
        } else {
            return StringUTF16.charAt(value, index);
        }
    }
    
    
    boolean isLatin1() {
        return COMPACT_STRINGS && coder == LATIN1;
        static final boolean COMPACT_STRINGS;

      static {
          COMPACT_STRINGS = true;
      }
      
      private final byte coder;
      
    @Native static final byte LATIN1 = 0;
    @Native static final byte UTF16  = 1;
    }
    
    LATIN1과 UTF16은 고정값으로 보이고
    
    @Native: native 메소드에 의해 참조되는 상수에 사용된다 (jdk1.8부터)
    JNI java native interface os레벨에서 
    c언어로 만들어진 코드를 퍼올려서 설정하는것으로 보인다..

그리고 COMPACT_STRINGS는 jvm에서 string값을 판단하여 주입하니 
일부러 접근 불가하게 막아놓은 조치로 보인다.

@Stable	// -> null 아님 보장
    private final byte[] value;	// 이 값은 문자 저장에 사용됩니다.
    
    이 필드는 VM에서 신뢰하며 String 인스턴스가 상수 인 경우 상수 폴딩이 적용됩니다.
    \구성 후이 필드를 덮어 쓰면 문제가 발생합니다.
    또한 배열의 내용을 신뢰하기 위해 Stable로 표시됩니다.
    
    

 

 

.java -> .class -> compile -> classloader가 로딩 -> class가 heap Area로 저장
-> static field 실행 (COMPACT_STRING = true;)

-> Execution Engine heap에 적재된 아무개 Class 기계어로 변환
        -> 명령어 단위로 실행 (charAt)
        -> garbage Collector에서 아무개 class 객체 제거
        -> 프로그램 종료

 

COMPACT_STRINGS의 설명

문자열 압축이 비활성화 된 경우 값의 바이트는 항상 UTF16으로 인코딩됩니다.
가능한 여러 구현 경로가있는 메서드의 경우 문자열 압축이 비활성화되면 하나의 코드 경로 만 사용됩니다.
인스턴스 필드 값은 일반적으로 JIT 컴파일러 최적화에 불투명합니다.
따라서 성능에 민감한 장소에서는 static boolean COMPACT_STRINGS가 
최적화 JIT 컴파일러에 의해 상수로 접혀지기 때문에 코더 필드를 확인하기 전에 먼저 
정적 부울 COMPACT_STRINGS에 대한 명시 적 검사가 수행됩니다.
이러한 경우의 관용구는 다음과 같습니다. 
다음과 같은 코드의 경우 : if (coder == LATIN1) {...}은 (coder () == LATIN1) {...} 
또는 : if (COMPACT_STRINGS && coder == LATIN1) {. ..} 최적화 JIT 컴파일러는 
위의 조건을 다음과 같이 접을 수 있습니다.
COMPACT_STRINGS == true => if (coder == LATIN1) {...} COMPACT_STRINGS == false => if (false) {...}

추가 설명

이 필드의 실제 값은 JVM에 의해 주입됩니다.
정적 초기화 블록은이 정적 최종 필드가 정적으로 접을 수 없음을 알리고
VM 초기화 중에 가능한 순환 종속성을 피하기 위해 여기에서 값을 설정하는 데 사용됩니다.

coder설명

값의 바이트를 인코딩하는 데 사용되는 인코딩의 식별자입니다.
이 구현에서 지원되는 값은 LATIN1 UTF16입니다.

coder 추가 설명

이 필드는 VM에서 신뢰하며 String 인스턴스가 상수 인 경우 상수 폴딩이 적용됩니다.
구성 후이 필드를 덮어 쓰면 문제가 발생합니다.

상수 폴딩이란?

컴파일 과정의 코드 생성 단계에서 코드 효율성을 높이기 위한 최적화 기법.
실행 시간 성능을 향상시키고 코드 크기를 절약할 수 있다.
상수 폴딩 예제 코드

// code
static final int a = 2;
int b = 30 * a;

// folding would create
int b = 60;

https://stackoverflow.com/questions/2264178/what-is-constant-folding-in-java-compiler

 

StringLatin1

final class StringLatin1 {

	public static char charAt(byte[] value, int index) {
        if (index < 0 || index >= value.length) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return (char)(value[index] & 0xff);
        /*
        (char)로 캐스팅 value는 바이트배열의 index번째값과 bit 연산 & 0xff(11111111)
        byte -128 ~ 127인데 양수값만 도출하기 위한 의도로 보임
        */
    }
}

StringUTF16

final class StringUTF16 {

public static char charAt(byte[] value, int index) {
        checkIndex(index, value);
        return getChar(value, index);
    }
}

public static void checkIndex(int off, byte[] val) {
        String.checkIndex(off, length(val));
    }
    
    /*
     * StringIndexOutOfBoundsException  if {@code index} is
     * negative or greater than or equal to {@code length}.
     */
    static void checkIndex(int index, int length) {
        if (index < 0 || index >= length) {
            throw new StringIndexOutOfBoundsException("index " + index +
                                                      ", length " + length);
        }
    }
    
    @HotSpotIntrinsicCandidate
    // intrinsic performs no bounds checks	// 실제 실행시 경계 검사가 없다?
    // index bound에 대해서 검사를 하지 않는다는 얘기인거 같다.
    static char getChar(byte[] val, int index) {
        assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
        index <<= 1;
        return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) |
                      ((val[index]   & 0xff) << LO_BYTE_SHIFT));
    }

 

 

 

 


Oracle Docs / java 11

 

charAtpublic char charAt​(int index)

Returns the char value at the specified index. An index ranges from 0 to length() - 1. The first char value of the sequence is at index 0, the next at index 1, and so on, as for array indexing.

If the char value specified by the index is a surrogate, the surrogate value is returned.

Specified by:charAt in interface CharSequenceParameters:index - the index of the char value.Returns:the char value at the specified index of this string. The first char value is at index 0.Throws:IndexOutOfBoundsException - if the index argument is negative or not less than the length of this string.

 

charAtpublic char charAt (int index)

지정된 인덱스의 char value를 반환. 인덱스의 범위는 0 ~ length() - 1

배열 인덱싱과 마찬가지로 첫째 값은 index 0 다음은 1

 

char지정된 인덱스 의 값을 반환합니다 . 색인 범위는 0에서 length() - 1. 배열 인덱싱과 char마찬가지로 시퀀스 의 첫 번째 값은 index 0에 있고 다음은 index 1에 있습니다.

char 값이 surrogate가 index로 지정되어 있다면, surrogate 값이 리턴됩니다.

 

CharSequence 인터페이스의 charAt

매개변수 : index - char값의 인덱스

반환 - 이 문자열에 지정된 인덱스의 char 값

첫번째 char 값은 인덱스 0에 있음

예외 - IndexOutOfBoundsException - 인덱스가 음수 혹은 문자열 길이 넘어갈때

 

 

docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html

 

String (Java SE 11 & JDK 11 )

Compares two strings lexicographically. The comparison is based on the Unicode value of each character in the strings. The character sequence represented by this String object is compared lexicographically to the character sequence represented by the argum

docs.oracle.com


참조 블로그

 

m.blog.naver.com/gngh0101/221562878280

 

자바11 Compact Strings 에 대해서 쉽게 이해하기 (vs Compressed, Performance Test)

자바11 Compact Strings 에 대해서 쉽게 이해하기 (vs Compressed, Performance Test)​Java 9 부터 ...

blog.naver.com

자바 11에서도 isLatin1을 통해 다른 메서드를 반환했던 모양이다.

 

m.blog.naver.com/nakim02/221478419731

 

[Java] Surrogate, Surrogate Pair란?

초기 Java는 UFT-16은 (0x0000부터 0xFFFF로) 문자를 나타내도록 개발되었다. 하지만 나중에 20배...

blog.naver.com