스트링 인터닝
String interning컴퓨터 과학에서 문자열 인터닝은 각각의 구별되는 문자열 값의 복사본 하나만 저장하는 방법인데, 반드시 불변해야 한다.[1] 문자열을 상호 연결하면 문자열을 생성하거나 삽입할 때 더 많은 시간이 소요되는 비용으로 일부 문자열 처리 작업을 더 시간 또는 공간 효율적으로 만들 수 있다. 개별 값은 문자열 인턴 풀에 저장된다.
각 문자열의 단일 복사본을 인턴이라고 하며, 일반적으로 문자열 클래스의 메서드(예: Java의 String.intern())[2]에 의해 조회된다. Java의 모든 컴파일 시간 상수 문자열은 이 방법을 사용하여 자동으로 삽입된다.[3]
문자열 인터닝은 Java, Python, PHP(5.4 이후), Lua,[4] Ruby(기호를 가진 루비), Julia 및 를 포함한 일부 현대 객체 지향 프로그래밍 언어에 의해 지원된다.NET 언어.[5] Lisp, Scheme, Smalltalk은 기본적으로 삽입된 문자열인 기호 유형을 가진 언어 중 하나이다. 뉴저지 스탠더드 ML의 도서관에는 같은 일을 하는 타입이 있다. 주로 메서드 이름으로 사용되는 Objective-C의 셀렉터는 인트라인이 삽입되어 있다.
현이 아닌 다른 사물은 인턴할 수 있다. 예를 들어, Java에서 원시 값을 래퍼 객체에 박스로 넣을 때 특정 값(임의의 값) boolean
, 아무거나 byte
, 아무거나 char
0부터 127까지, 그리고 그 어떤 것 short
또는 int
-117 ~ 127) 사이에 삽입되며, 이 값들 중 하나의 복싱 변환이 두 개라도 동일한 개체를 얻을 수 있도록 보장된다.[6]
역사
Lisp는 그것의 상징들을 위해 삽입된 끈의 개념을 도입했다. 역사적으로 문자열 인턴 풀로 사용되는 데이터 구조를 Oblist(링크된 목록으로 구현했을 때) 또는 Obarray(배열로 구현되었을 때)라고 불렀다.
현대의 Lisp 방언은 일반적으로 기호와 문자열을 구분한다. 주어진 문자열을 연결하면 기존 기호가 반환되거나 새로운 기호가 생성되는데, 그 이름이 그 문자열이다. 기호는 문자열에서 흔히 하지 않는 추가 속성(관련 값 저장 또는 이름 표시 등): 또한 구별은 삽입된 문자열을 불필요하게 삽입된 문자열과 우연히 비교하는 것을 방지하는데 유용하며, 이는 사용 패턴에 따라 간헐적인 고장으로 이어질 수 있다.
동기
스트링 인터닝은 스트링 비교를 가속화하는데, 이는 때때로 스트링 키와 연결 어레이에 의존하는 애플리케이션(예: 컴파일러 및 동적 프로그래밍 언어 런타임)의 성능 병목현상이 되기도 한다. 서로 연결하지 않고, 두 개의 구별되는 문자열을 비교하는 것은 양쪽의 모든 문자를 검사하는 것을 포함할 수 있다.[Note 1] 이 속도는 몇 가지 이유로 느리다. 문자열 길이에 본질적으로 O(n)이며, 일반적으로 시간이 걸리는 여러 메모리 영역에서 읽기를 요구한다. 그리고 읽기는 프로세서 캐시를 가득 채운다. 즉, 다른 필요에 사용할 수 있는 캐시가 더 적다는 것을 의미한다. 삽입된 문자열의 경우, 원래 인턴 작업 후에 간단한 객체 ID 테스트가 충분하다. 이는 일반적으로 메모리 참조가 전혀 없는 단일 기계 명령으로 포인터 평등 테스트로 구현된다.
문자열 인터닝은 동일한 문자열 값의 인스턴스가 많은 경우 메모리 사용도 줄인다. 예를 들어, 네트워크나 스토리지에서 읽힌다. 이러한 문자열은 매직 번호 또는 네트워크 프로토콜 정보를 포함할 수 있다. 예를 들어 XML 파서들은 메모리를 저장하기 위해 태그와 속성의 이름을 인턴으로 할 수 있다. Java RMI 직렬화 객체 스트림을 통한 객체의 네트워크 전송은 직렬화 시 String 객체의 핸들이 중복 객체 대신 사용되기 때문에 보다 효율적으로 인턴된 문자열을 전송할 수 있다.[7]
문제들
멀티스레딩
삽입된 문자열이 불변하지 않을 경우, 문자열을 다중 스레딩과 혼합할 경우 문제가 발생할 수 있다는 단점이 있다. 많은 시스템에서 문자열 인턴은 주소 공간 내의 모든 스레드(또는 포인터를 공유할 수 있는 모든 컨텍스트)에 걸쳐 글로벌해야 하므로 인턴 풀은 안전한 동시 액세스를 위해 동기화되어야 하는 글로벌 자원이다. 이는 문자열 생성(필요한 경우 인턴 풀을 점검하고 수정해야 하는 경우)에만 영향을 미치고, 안전한 최적화가 가능한 플랫폼에서는 이중 체크된 잠금을 사용할 수 있지만, 인턴 풀을 수정할 때 상호 배제해야 하는 필요성은 비용이 많이 들 수 있다.[8]
또한 문자열 공간을 여러 풀로 분할하여 경합을 줄일 수 있으며, 이 풀은 서로 독립적으로 동기화할 수 있다.
사용되지 않는 삽입 문자열 회수
삽입된 문자열의 많은 구현은 더 이상 사용되지 않는 문자열을 회수(수동 또는 다른)하려고 시도하지 않는다. 삽입된 문자열의 수가 작거나 고정되어 있거나 수명이 짧은 애플리케이션의 경우 시스템 자원의 손실을 견딜 수 있다. 그러나 많은 수의 현업 인턴이 런타임에 만들어지는 장기간 운영되는 시스템의 경우, 사용하지 않는 인턴을 회수할 필요가 생길 수 있다. 이 작업은 가비지 수집가가 처리할 수 있지만, 이 작업이 올바르게 작동하려면 끈 인턴에 대한 약한 참조가 인턴 풀에 저장되어야 한다.
참고 항목
메모들
- ^ 문자열 비교는 첫 번째 문자가 일치하지 않을 때 중지될 수 있다. 엄격한 평등을 위해, 문자열을 통과하기 전에 문자열의 길이를 비교할 수도 있다. 그러나 null-terminated 문자열의 길이를 찾는 것 자체가 문자열을 통과해야 한다.
참조
- ^ "String.Intern Method (String)". Microsoft Developer Network. Retrieved 25 March 2017.
- ^
String.intern()
- ^ "Chapter 15. Expressions". docs.oracle.com. Retrieved 30 January 2019.
- ^ "lua-users wiki: Immutable Objects". lua-users.org. Retrieved 30 January 2019.
- ^ rpetrusha. "String Class (System)". docs.microsoft.com. Retrieved 30 January 2019.
- ^ "Chapter 5. Conversions and Promotions". docs.oracle.com. Retrieved 30 January 2019.
- ^ "Java Object Serialization Specification: 1 - System Architecture". docs.oracle.com. Retrieved 30 January 2019.
- ^ admin (3 September 2013). "String.intern in Java 6, 7 and 8 - multithreaded access". java-performance.info. Retrieved 30 January 2019.