developer tip

Java 8의 문자열 중복 제거 기능

copycodes 2020. 12. 4. 19:14
반응형

Java 8의 문자열 중복 제거 기능


이후 String각 문자가 2 바이트를 소모하기 때문에 자바에서 (다른 언어와 같은) 메모리를 많이 소모, 자바 (8)라는 새로운 기능이 도입 문자열 중복 제거 , 위치의 char 배열은 문자열과 최종 내부에 있다는 사실을 활용 JVM을 그렇게 그들과 어울릴 수 있습니다.

지금 까지이 예제를 읽었 지만 프로 자바 코더가 아니기 때문에 개념을 이해하는 데 어려움을 겪고 있습니다.

여기에 그것이 말합니다.

문자열 복제에 대한 다양한 전략이 고려되었지만 이제 구현 된 전략은 다음 접근 방식을 따릅니다. 가비지 수집기가 String 개체를 방문 할 때마다 char 배열을 기록합니다. 해시 값을 가져 와서 배열에 대한 약한 참조와 함께 저장합니다. 동일한 해시 코드를 가진 다른 문자열을 찾으면 즉시 문자별로 비교합니다. 일치하면 하나의 String이 수정되고 두 번째 String의 char 배열을 가리 킵니다. 그러면 첫 번째 char 배열은 더 이상 참조되지 않으며 가비지 수집 될 수 있습니다.

물론이 전체 프로세스는 약간의 오버 헤드를 가져 오지만 엄격한 제한으로 제어됩니다. 예를 들어, 한동안 중복 된 문자열이 발견되지 않으면 더 이상 확인되지 않습니다.

나의 첫 번째 질문,

최근 Java 8 업데이트 20에 추가 되었기 때문에이 주제에 대한 리소스가 아직 부족합니다. 여기에있는 누구든지 StringJava에서 사용 하는 메모리를 줄이는 데 도움이되는 몇 가지 실용적인 예를 공유 할 수 있습니까?

편집하다:

위의 링크에 따르면

동일한 해시 코드를 가진 다른 문자열을 찾으면 즉시 문자별로 비교합니다.

두 번째 질문입니다.

두 해시 코드가있는 경우 String다음 동일하다는 Strings왜 그들을 비교, 이미 동일 charchar가 두 사람이 발견되면 String동일한 해시 코드가?


a String firstName와 a 를 가진 사람들이 포함 된 전화 번호부가 있다고 상상해보십시오 String lastName. 그리고 당신의 전화 번호부에서 100,000 명의 사람들이 같은 firstName = "John".

데이터베이스 또는 파일에서 데이터를 가져 오기 때문에 해당 문자열은 인턴되지 않으므로 JVM 메모리에는 {'J', 'o', 'h', 'n'}John 문자열 당 하나씩 10 만 번 char 배열이 포함 됩니다. 이러한 어레이 각각은 20 바이트의 메모리를 사용하므로 10 만 개의 John이 2MB의 메모리를 차지합니다.

중복 제거를 통해 JVM은 "John"이 여러 번 복제되었음을 인식하고 모든 John 문자열이 동일한 기본 char 배열을 가리 키도록하여 메모리 사용량을 2MB에서 20 바이트로 줄입니다.

JEP 에서 더 자세한 설명을 찾을 수 있습니다 . 특히:

현재 많은 대규모 Java 애플리케이션이 메모리 병목 현상을 겪고 있습니다. 측정 결과 이러한 유형의 애플리케이션에서 Java 힙 라이브 데이터 세트의 약 25 %가 String 객체에 의해 소비되는 것으로 나타났습니다. 또한 이러한 String 객체의 약 절반은 중복이며 중복 string1.equals(string2)은 사실임을 의미 합니다. 힙에 중복 String 객체를 갖는 것은 본질적으로 메모리 낭비입니다.

[...]

실제 예상되는 이점은 약 10 % 힙 감소로 끝납니다. 이 수치는 광범위한 애플리케이션을 기반으로 계산 된 평균입니다. 특정 응용 프로그램의 힙 감소는 위아래로 크게 다를 수 있습니다.


@assylias 답변은 기본적으로 작동 방식을 알려주며 매우 좋은 답변입니다. 문자열 중복 제거를 사용하여 프로덕션 애플리케이션을 테스트 한 결과 몇 가지 결과가 있습니다. 웹 앱은 Strings를 많이 사용하므로 이점이 매우 분명하다고 생각합니다.

문자열 중복 제거를 사용하려면 다음 JVM 매개 변수를 추가해야합니다 (최소 Java 8u20 필요).

-XX:+UseG1GC -XX:+UseStringDeduplication -XX:+PrintStringDeduplicationStatistics

마지막 항목은 선택 사항이지만 이름처럼 문자열 중복 제거 통계를 보여줍니다. 다음은 내 것입니다.

[GC concurrent-string-deduplication, 2893.3K->2672.0B(2890.7K), avg 97.3%, 0.0175148 secs]
   [Last Exec: 0.0175148 secs, Idle: 3.2029081 secs, Blocked: 0/0.0000000 secs]
      [Inspected:           96613]
         [Skipped:              0(  0.0%)]
         [Hashed:           96598(100.0%)]
         [Known:                2(  0.0%)]
         [New:              96611(100.0%)   2893.3K]
      [Deduplicated:        96536( 99.9%)   2890.7K( 99.9%)]
         [Young:                0(  0.0%)      0.0B(  0.0%)]
         [Old:              96536(100.0%)   2890.7K(100.0%)]
   [Total Exec: 452/7.6109490 secs, Idle: 452/776.3032184 secs, Blocked: 11/0.0258406 secs]
      [Inspected:        27108398]
         [Skipped:              0(  0.0%)]
         [Hashed:        26828486( 99.0%)]
         [Known:            19025(  0.1%)]
         [New:           27089373( 99.9%)    823.9M]
      [Deduplicated:     26853964( 99.1%)    801.6M( 97.3%)]
         [Young:             4732(  0.0%)    171.3K(  0.0%)]
         [Old:           26849232(100.0%)    801.4M(100.0%)]
   [Table]
      [Memory Usage: 2834.7K]
      [Size: 65536, Min: 1024, Max: 16777216]
      [Entries: 98687, Load: 150.6%, Cached: 415, Added: 252375, Removed: 153688]
      [Resize Count: 6, Shrink Threshold: 43690(66.7%), Grow Threshold: 131072(200.0%)]
      [Rehash Count: 0, Rehash Threshold: 120, Hash Seed: 0x0]
      [Age Threshold: 3]
   [Queue]
      [Dropped: 0]

10 분 동안 앱을 실행 한 후의 결과입니다. 보시다시피 문자열 중복 제거는 452실행 되었고 801.6MB 문자열이 "중복 제거" 되었습니다 . 문자열 중복 제거는 검사 27 000 000 문자열. 표준 병렬 GC가있는 Java 7의 메모리 소비를 G1 GC가있는 Java 8u20과 비교하고 문자열 중복 제거를 활성화하면 힙이 약 50 % 감소했습니다 .

자바 7 병렬 GC

Java 7 Parallel GC

문자열 중복 제거 기능이있는 Java 8 G1 GC

Java 8 G1 GC with String Deduplication


귀하의 첫 번째 질문은 이미 답변되었으므로 두 번째 질문에 답변 해 드리겠습니다.

String동일하지만 때문에 개체는 문자로 문자를 비교해야 Object의가 동일한 해시를 의미한다, 역은 하지 꼭 그렇지.

As Holger said in his comment, this represents a hash collision.

The applicable specifications for the hashcode() method are as follows:

  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

  • It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. ...

This means that in order for them to guarantee equality, the comparison of each character is necessary in order for them to confirm the equality of the two objects. They start by comparing hashCodes rather than using equals since they are using a hash table for the references, and this improves performance.


The strategy they describe is to simply reuse the internal character array of one String in possibly many equal Strings. There's no need for each String to have its own copy if they are equal.

In order to more quickly determine if 2 strings are equal, the hash code is used as a first step, as it is a fast way to determine if Strings may be equal. Hence their statement:

As soon as it finds another String which has the same hash code it compares them char by char

This is to make a certain (but slower) comparison for equality once possible equality has been determined using the hash code.

In the end, equal Strings will share a single underlying char array.

Java has had String.intern() for a long time, to do more or less the same (i.e. save memory by deduplicating equal Strings). What's novel about this is that it happens during garbage collection time and can be externally controlled.

참고URL : https://stackoverflow.com/questions/27949213/string-deduplication-feature-of-java-8

반응형