먼저 세 가지 특성을 기억해야합니다.
1. 정의
API를 볼 때 해당 문자열, StringBuffer 및 StringBuilder가 모두 CharEdegence 인터페이스를 구현할 수 있습니다. 그것들은 모두 문자열과 관련이 있지만 처리 메커니즘은 다릅니다.
2. 시나리오를 사용하십시오
문자열 클래스를 사용하는 시나리오 : 문자열이 자주 변경되지 않는 시나리오에서 상수 선언 및 소수의 가변 작업과 같은 문자열 클래스를 사용할 수 있습니다.
StringBuffer 클래스를 사용한 시나리오 : 문자열 작업이 자주 수행되는 경우 (스 플라이 싱, 대체, 삭제 등) 다중 스레드 환경에서 실행되면 XML 구문 분석, HTTP 매개 변수 구문 분석 및 캡슐화와 같은 StringBuffer를 사용하는 것을 고려할 수 있습니다.
StringBuilder 클래스를 사용하는 시나리오 : 문자열 작업이 자주 수행되는 경우 (스 플라이 싱, 대체 및 삭제와 같은) 단일 스레드 환경에서 실행되면 SQL 문 어셈블리, JSON 캡슐화 등과 같은 StringBuilder를 사용하는 것을 고려할 수 있습니다.
III. 분석
간단히 말해서 문자열 유형과 StringBuffer 유형의 주요 성능 차이는 실제로 문자열이 불변의 객체이므로 문자열 유형을 변경할 때마다 새 문자열 객체를 생성 한 다음 새 문자열 객체를 가리키는 것과 동일합니다. 따라서 각 객체 생성이 시스템 성능에 영향을 미치기 때문에, 특히 메모리에 참조 된 객체가 너무 많을 때 JVM의 GC가 작동하기 시작하기 때문에 컨텐츠를 자주 변경하는 문자열에 문자열을 사용하지 않는 것이 가장 좋습니다.
StringBuffer 클래스를 사용하면 결과가 다릅니다. 마다 결과가 새 개체를 생성 한 다음 객체 참조를 변경하는 대신 StringBuffer 객체 자체에서 작동 할 때마다. 따라서 일반적으로 StringBuffer를 사용하는 것이 좋습니다. 특히 String 객체가 종종 변경 될 때 일부 특별한 경우, 문자열 객체의 문자열 스 플라이 싱은 실제로 JVM에 의해 StringBuffer 객체의 스 플라이 싱으로 해석됩니다. 따라서 문자열 객체의 속도는이 시간에 StringBuffer 객체보다 느리지 않습니다. 특히 다음 문자열 객체 생성에서 문자열 효율은 StringBuffer보다 훨씬 빠릅니다.
문자열 s1 = "이것은" + "simple" + "test"일뿐입니다. stringbuffer sb = new StringBuilder ( "이것은 단지 A"). Append ( "Simple"). Append ( "Test");
String S1 객체를 생성하는 속도가 너무 빠르며 현재 StringBuffer는 속도에 대한 이점이 없습니다. 실제로 이것은 JVM의 속임수입니다. JVM의 눈에는 이것입니다
문자열 s1 = "이것은 단지" + "simple" + "test"입니다.
실제로:
문자열 s1 = "이것은 간단한 테스트 일뿐입니다";
물론 많은 시간이 걸리지 않습니다. 그러나 여기서 주목해야 할 것은 문자열이 다른 문자열 객체에서 나오면 속도가 빠르지 않다는 것입니다.
문자열 s2 = "이것은 단지"; 문자열 s3 = "simple"; 문자열 s4 = "test"; 문자열 s1 = s2 + s3 + s4;
현재 JVM은 원래 방식으로 정기적으로 수행합니다.
4. JVM의 심층 최적화 및 처리
위의 성능 비용이 실제로 있습니까? 문자열 스 플라이 싱은 매우 일반적이므로 특별한 처리 최적화가 없습니까? 답은이 최적화가 JVM에서 .java를 바이트 코드로 컴파일 할 때 수행된다는 것입니다.
Java 프로그램이 실행되기를 원한다면 시간이 걸리고 시간을 컴파일하고 시간이 걸립니다. 컴파일 시간에 Java JVM (컴파일러)은 Java 파일을 바이트 코드로 변환합니다. 런타임에 JVM (Java Virtual Machine)은 바이트 코드를 생성하는 컴파일 타임을 실행합니다. 이 두 기간 동안 Java는 소위 편집을 달성하고 어디서나 실행했습니다.
컴파일 기간 동안 어떤 최적화가 이루어 졌는지 실험하고 성능 비용이 발생할 수있는 코드를 만듭니다.
공개 클래스 연결 {public static void main (String [] args) {String username = "Andy"; 문자열 age = "24"; 문자열 job = "개발자"; 문자열 정보 = username + age + job; System.out.println (정보); }}Concatenation.java를 컴파일하십시오. Concatenation.class를 얻으십시오
Javac Concatenation.java
그런 다음 Javap을 사용하여 컴파일 된 Concatenation.class 파일을 디 컴파일합니다. Javap -C 연결. Javap 명령을 찾을 수없는 경우 Javap이 환경 변수에 위치하거나 Javap의 전체 경로를 사용하는 디렉토리를 추가하는 것이 좋습니다.
17 : 22 : 04 Androidyue ~/workspace_adt/strings/src $ javap -c "concatenation.java"공개 클래스 연결 {public concatenation (); 코드 : 0 : aload_0 1 : invokespecial #1 // 메소드 Java/lang/Object. "<init>":() v 4 : 공개 정적 무효 메인을 반환합니다 (java.lang.string []); 코드 : 0 : LDC #2 // String Andy 2 : String 2 : Stor_1 3 : LDC #3 // String 24 5 : Store_2 6 : LDC #4 // String Developer 8 : Store_3 9 : New #5 // 클래스 Java/Lang/StringBuilder 12 : Dup 13 : intokescial #6 // method java/lang/lang/stringbuilder. InvokeVirtual #7 // 메소드 java/lang/stringbuilder.append : (ljava/lang/string;) ljava/lang/stringbuilder; 20 : aload_2 21 : InvokeVirtual #7 // 메소드 Java/lang/StringBuilder.Append : (ljava/lang/string;) ljava/lang/StringBuilder; 24 : aload_3 25 : InvokeVirtual #7 // 메소드 Java/Lang/StringBuilder.Append : (ljava/lang/string;) ljava/lang/StringBuilder; 28 : InvokeVirtual #8 // 메소드 Java/Lang/StringBuilder.toString :() ljava/lang/string; 31 : 저장 4 33 : getstatic #9 // 필드 Java/Lang/System.out : ljava/io/printstream; 36 : aload 4 38 : invokevirtual #10 // 메소드 java/io/printstream.println : (ljava/lang/string;) v 41 : return}그중에서도 LDC, 상점 등은 조립 지침과 유사한 Java 바이트 코드 지침입니다. 다음 주석은 Java 관련 컨텐츠를 사용하여 설명합니다. 우리는 그것에 많은 StringBuilder가 있음을 알 수 있지만, 디스플레이없이 Java 코드로 호출합니다. 이것은 Javajvm의 최적화입니다. Javajvm이 문자열 스 플라이 싱을 만나면 StringBuilder 객체가 생성됩니다. 그 뒤에 스 플라이 싱은 실제로 StringBuilder 객체의 Append 메소드를 호출하고 있습니다. 이런 식으로, 우리가 걱정하는 문제는 없을 것입니다.
5. JVM 최적화만으로 의존합니까?
JVM이 최적화에 도움이되었으므로 JVM 최적화에만 의존하기에 충분합니까? 물론.
낮은 성능에 최적화되지 않은 코드를 살펴 보겠습니다.
public void implicitusestringBuilder (string [] value) {String result = ""; for (int i = 0; i <value.length; i ++) {result += value [i]; } system.out.println (결과);}Javac과 함께 컴파일하고 Javap으로보기
public void implicitusestringBuilder (java.lang.string []); 코드 : 0 : LDC #11 // 문자열 2 : String 2 : ICONST_0 4 : ISTORE_3 5 : ILOAD_3 6 : aload_1 7 : ArrayLength 8 : IF_ICMPGE 38 11 : 새로운 #5 // 클래스 Java/lang/StringBuilder 14 : Dup 15 : intokescial #6 // methob/lang/langbuilder. " 18 : aload_2 19 : invokevirtual #7 // 메소드 java/lang/stringbuilder.append : (ljava/lang/string;) ljava/lang/stringbuilder; 22 : aload_1 23 : iload_3 24 : aaload 25 : invokevirtual #7 // 메소드 java/lang/stringbuilder.append : (ljava/lang/string;) ljava/lang/stringbuilder; 28 : InvokeVirtual #8 // 메소드 Java/Lang/StringBuilder.toString :() ljava/lang/string; 31 : Store_2 32 : Iinc 3, 1 35 : Goto 5 38 : getstatic #9 // 필드 Java/Lang/System.out : ljava/io/printstream; 41 : aload_2 42 : invokevirtual #10 // 메소드 java/io/printstream.println : (ljava/lang/string;) v 45 : 반환
그중 8 : IF_ICMPGE 38 및 35 : GOTO 5는 루프를 형성합니다. 8 : IF_ICMPGE 38은 JVM 피연산자 스택의 정수 비교가 (i <값의 반대 결과)보다 크거나 동일하다면 38 행 (System.out)으로 이동한다는 것을 의미합니다. 35 : GOTO 5는 5 행으로 직접 점프하는 것을 의미합니다.
그러나 여기서 StringBuilder 객체 생성이 루프간에 발생한다는 것은 매우 중요한 일이 있습니다. 즉, 루프가 여러 번 여러 StringBuilder 객체를 생성 할 것인지를 의미합니다. 알몸의 저수준 코드.
품질을 즉시 향상시키기 위해 약간 최적화하십시오.
public void explicitusestringBuilder (string [] value) {StringBuilder result = new StringBuilder (); for (int i = 0; i <value.length; i ++) {result.append (values [i]); }}해당 편집 된 정보
11 : Aload_1 12 : Arraylength 13 : If_icmpge 30 16 : aload_2 17 : aload_1 18 : iload_3 19 : aaload 20 : invokevirtual #7 // 메소드 java/lang/stringbuilder.append : (ljava/lang/string;) ljava/lang/lang/stringbuilder; 23 : Pop 24 : Iinc 3, 1 27 : Goto 10 30 : 반환
위에서 볼 수 있듯이, 13 : if_icmpge 30 및 27 : goto 10은 루프 루프를 형성하고 0 : 새로운 #5는 루프 외부에 있으므로 StringBuilder는 여러 번 생성되지 않습니다.
일반적으로, 우리는 루프 본체에서 StringBuilders의 암묵적 또는 명백한 생성을 피하려고 노력해야합니다. 따라서 코드가 컴파일되는 방법과 내부적으로 실행되는 방법이 비교적 높은 코드 수준을 갖는 사람들을 이해하는 사람들.
6. 결론
대부분의 경우 StringBuffer> String
java.lang.stringbuffer는 가변 문자의 스레드 안전 시퀀스입니다. 문자열과 같은 문자열 버퍼이지만 수정할 수 없습니다. 어느 시점에서나 특정 문자 순서가 포함되어 있지만 일부 메소드 호출에 의해 시퀀스의 길이와 내용을 변경할 수 있습니다. 문자열 버퍼는 멀티 스레딩 프로그램에서 안전하게 사용할 수 있습니다. 이러한 방법은 필요할 때 동기화 될 수 있으므로 특정 인스턴스의 모든 작업은 관련된 각 스레드에서 작성한 메소드 호출 순서와 일치하는 직렬 순서로 발생하는 것으로 보입니다.
StringBuffer의 주요 작업은 Append and Insert 메소드이며 모든 유형의 데이터를 수락하도록 과부하 할 수 있습니다. 각 메소드는 주어진 데이터를 효과적으로 문자열로 변환 한 다음 해당 문자열의 문자를 문자열 버퍼에 추가 또는 삽입 할 수 있습니다. Append 메소드는 항상 버퍼 끝에 이러한 문자를 추가합니다. 삽입 메소드는 지정된 지점에서 문자를 추가합니다.
예를 들어, z가 현재 내용이 "시작"인 문자열 버퍼 객체를 지칭하는 경우이 메소드는 z.append ( "le")를 문자열 버퍼에 "startle"(축적)로 만듭니다. Z.Insert (4, "le")는 문자열 버퍼를 "스타 렛"을 포함하도록 변경합니다.
대부분의 경우 StringBuilder> StringBuffer
java.lang.stringbuilder 변수 문자 순서는 Java 5.0에서 새로 제공됩니다. 이 클래스는 StringBuffer 호환 API를 제공하지만 동기화되는 것을 보장하지 않으므로 사용 시나리오는 단일 스레드입니다. 이 클래스는 문자열 버퍼가 단일 스레드에서 사용될 때 StringBuffer를 간단하게 대체하도록 설계되었습니다 (일반적). 가능하면 대부분의 구현에서 StringBuffer보다 빠르기 때문에이 클래스를 먼저 차지하는 것이 좋습니다. 둘 다의 사용 방법은 기본적으로 동일합니다.