자바 성능 튜닝 - String By starseat 2024-04-18 18:26:57 java/spring Post Tags [자바 성능 튜닝 이야기](https://product.kyobobook.co.kr/detail/S000001032977) 책을 읽고 정리한 내용 입니다. # String String 클래스는 잘 사용하면 상관 없지만 잘못 사용하면 메모리에 많은 영향을 준다. String 이 GC 에 영향을 주는 것은 확실하다. ## String 클래스를 잘못 사용한 사례 SQL 을 작성하는 String 사용 예제를 보면 ```java String sql = ""; sql += "select * "; sql += "from ( "; sql += "select A_column, "; sql += "B_column, "; // 이하 생략. 약 400 라인 ``` 이런식으로 사용할 수 있는데 이 경우 개발 시에는 편할지 몰라도 **메모리를 많이 사용하게 된다는 문제가 있다.** 위와 같은 패턴을 100회 수행 (400 라인 정도 수행) 한다는 가정하에 메모리 사용량 이다. | 구분 | 결과 | | ------ | ------ | | 메모리 사용량 | 10회 평균 약 5mb | | 응답 시간 | 10회 평균 약 5ms | 위 코드를 `StringBuilder` 로 변경 한 경우 이다. ```java StringBuilder sql = new StringBuilder(); sql.append("select * "); sql.append("from ( "); sql.append("select A_column, "); sql.append("B_column, "); // 이하 생략. 약 400 라인 ``` | 구분 | 결과 | | ------ | ------ | | 메모리 사용량 | 10회 평균 약 371kb | | 응답 시간 | 10회 평균 약 0.3ms | # StringBuffer 와 StringBuilder StringBuffer 클래스와 StringBuilder 클래스에서 제공하는 메서드는 동일하다. 그렇담 무슨 차이가 있을까? - 차이점 | 구분 | ThreadSafe | 비고 | | ------ | ------ | -- | | StringBuiler | X | 단일 스레드의 안정성만 보장. 스레드 안전 여부가 필요 없을 경우 사용 | | StringBuffer | O | 스레드에 안전한 프로그램이나 스레드에 안전한지 모를 경우 사용 | # String vs StringBuffer vs StringBuilder 성능 체크를 위해 아래 소스를 사용하였다. ```java int OUT_LOOP = 10; int IN_LOOP = 10000; String test = "abcde"; void stringTest() { String result = ""; System.currentTimeMillis(); for(int i=0; i 자바 기반의 애플리케이션의 응답 측정할 경우 '무조건' 처음 수행한 결과값은 무시해야 한다. > 클래스를 메모리로 로딩하는 시간이 있어 이 때의 측정 결과 값은 의미가 없다. - 응답 시간 | 구분 | 주요 소스 부분 | 응답 시간 (ms) | 비고 | | -- | -- | -- | -- | | String | str += test; | 95,801.41 ms | 95 초 | | StringBuffer | sb.append(test);result = sb.toString(); | 247.48 ms14.21 ms | 0.24 초 | | StringBuilder | sb.append(test);result = sb.toString(); | 174.17 ms13.38 ms | 0.17 초 | - 메모리 사용량 | 구분 | 주요 소스 부분 | 메모리 사용량 (byte) | 생성된 임시 객체 수 | 비고 | | -- | -- | -- | -- | -- | | String | str += test; | 100,102,000,000 | 4,000,000 | 약 95 gb | | StringBuffer | sb.append(test);result = sb.toString(); | 29,493,60010,004,000 | 1,200200 | 약 28 mb약 9.5 mb | | StringBuilder | sb.append(test);result = sb.toString(); | 29,493,60010,004,000 | 1,200200 | 약 28 mb약 9.5 mb | 위 결과로 보면 String 보다 StringBuffer 가 약 367배 빠르며, StringBuilder 가 약 512배 빠르다. 메모리는 StringBuffer 와 StringBuilder 보다 String 에서 약 3,390배 더 사용한다. 왜 이럴까? ## String 처리 방식 `String` 의 `str += test;` 이 부분을 수행할 때 새로운 `String` 객체가 생성되고, 이전 객체는 필요 없는 쓰레기 값이 되어 GC 대상이 되어 버린다. ```text String 첫번째 수행 (str += test;) : abcde String 두번째 수행 (str += test;) : abcdeabcde // 첫번째 수행 했던 String 은 쓰레기 값이 됨. String 세번째 수행 (str += test;) : abcdeabcdeabcde // 두번째 수행 했던 String 은 쓰레기 값이 됨. ``` 이렇게 이전 `String` 객체는 사라지고, 새로운 주소로 `String` 객체가 생성 되는 과정을 거치면서 메모리를 많이 사용하게 되고, 응답 속도에도 많은 영향을 미치게 된다. GC 를 수행할 수록 시스템의 CPU를 사용하게 되고 시간도 많이 소요된다. ## StringBuffer 와 StringBuilder 의 처리 방식 StringBuffer 와 StringBuilder 는 String 과는 다르게 새로운 객체를 생성하지 않고, 기존에 있는 객체의 크기를 증가시키면서 값을 더한다. # 추가 jdk 5.0 이상인 버전은 String 을 사용하면 컴파일 시 자동으로 StringBuilder 로 변환해준다. ```java // 샘플 소스 String str = "This " + "is " + "\"String\" " + "sample."; // 컴파일러 자동 변환 String str = (new StringBuilder("This ").append("is ").append(""\"String\" ").append("sample."); ``` # 정리 - **String**: 짧은 문자열을 더할 경우 사용 - **StringBuffer** - 스레드에 안전한 프로그램이 필요할 때나, 개발 중인 시스템의 부분이 스레드에 안전한지 모를 경우 사용 - 클래스에 static 으로 선언한 문자열을 변경하거나, singleton 으로 선언된 클래스에 선언된 문자열일 경우 이 클래스 사용 - **StringBuilder** - 스레드에 안전한지의 여부와 전혀 관계 없는 프로그램을 개발할 때 사용 - 메서드 내에 변수를 선언했다면, 해당 변수는 그 메서드 내에서만 존재하므로 StringBuilder 사용 Previous Post [SpotBugs] might be used to include CRLF characters into log messages Next Post 자바 성능 튜닝 - Collection 관련