티스토리 뷰

반응형

String 사용? 미사용?

String은 메모리에 어떻게 저장되는가? 객체의 값은 변경가능 한가?

  • String 객체 대부분은 원시(Primitive) 타입으로 취급하므로 new 키워드를 사용할 필요가 없다.

    • new 키워드를 사용 안 하고 String 리터럴로 표현할 경우 String Constant Pool 영역(힙 영역이 감싸고 있음)에 존재한 후 해당 영역의 값을 가리키게 된다.
    • new 키워드를 사용할 경우에는 heap영역에 String 객체를 생성하게 되는 것이다.
  • Java에서 String은 특별한 참조 자료형이다. 다른 객체들과 마찬가지로 new 생성자를 이용해서 인스턴스를 만들고 heap영역에 올라가지만, 다른 참조형과는 다르게 한번 객체가 생성되면 해당 값은 변하지가 않는다.

    String str = new String("cat");
    str = str+str;
    //1. str은 cat이라는 문자열을 참조
    //2. str+str 연산 진행으로 인해 "catcat" 문자열 만들어짐
    //3. 새로운 String 객체 생성
    //4. 새로운 String 객체에 "catcat" 삽입
    //5. 기존 str 객체는 새로운 str String 객체를 참조
  • 이 처럼 String 연산은 계속적으로 객체를 만들어내기 때문에 비효율적이다.

 

String Constant pool

반복적으로 객체 생성되는 것을 어느정도(같은 값 일때만) 방지하기 위한 새로운 메모리 영역

String s1 = "Cat";	//String 리터럴 방식
String s2 = "Cat";	//String 리터럴 방식
//String Constant pool에 의해 str1과 str2는 같은 인스턴스를 참조하고 있다.
//Cat은 String Constant pool 메모리 영역에 한 부분만 차지하기 때문이다.

String s3 = new String("Cat");    //heap 영역에 할당
s1 == s2;    //true
s1 == s3;    //false

image

출처 : https://brunch.co.kr/@kd4/1

이 처럼 String 리터럴 추가 시 같은 값 (== true)일 때는 String Constant Pool 영역의 같은 위치에 담아두게 되는 것이다.

 

intern(인터닝이란)

String str1 = new String("ball");
String str2 = new String("ball");
String str3 = "ball";

//main
System.out.println(str1 == str1.intern());    //false
System.out.println(str3 == str1.intern());    //true
  • intern()메소드를 사용하면 Heap영역에 있는 인스턴스를 String Constant pool영역으로 이동한다.
  • 위 결과를 다시한번 살펴보면,
  • System.out.println(str1 == str1.intern());false가 나타난다.
    • 이유는 str1은 이제 Heap영역이 아닌 String Constatn pool 영역에 있으므로 같은 인스턴스를 참조하지 않는다.
  • System.out.println(str3 == str1.intern());true가 나타난다.
    • 위에서 본 바와 같이 str1.intern()으로 인해 str1은 String Constatn pool로 이동하였으므로 str3str1은 같은 인스턴스를 참조하는 것이다.
  • String 리터럴("")을 이용하여 String 객체를 생성하면 내부에서 intern(); 메소드를 호출한다.
  • 인터닝을 사용하면 문자열 비교 시 equals()보다 속도적인 부분이나 메모리 부분에서 효과를 얻을 수 있다.

 

String vs String Buffer vs String Builder

String

  • String은 immutable(불변)으로서 한번 메모리에 생성되면 변하지 않는다.
  • 위에서 살펴본 바와 같이 +연산 또는 concat을 통해 기존에 생성된 String 클래스의 변화를 주어도 해당 클래스는 변하는 것이 아니라 새로운 String 클래스를 생성 후 기존 String 클래스가 새로운 String 클래스를 참조하도록 하는 것이다.
  • String이 객체는 문자열 연산이 많은 경우 새로운 String 객체를 생성하므로 성능이 우수하지 못하다.
  • String은 짧은 문자열 연산에 사용하면 좋다
  • String은 불변으로 인해 Thread-safe가 보장되는데 이유는, 컴파일러에 의해 바이트코드가 JVM에 로드될 시 JVM은 String Constant Pool에 동일한 문자열이 존재하는지 확인 후  존재할 시 재사용하고 없을 시에 새로운 값을 만들기 때문이다.

 

StringBuffer와 StringBuilder

  • StringBuffer와 StringBuilder 공통점

    • String과는 다르게 mutable(변경가능)하다. 즉, 클래스는 한번만 만드므로, 문자열 연산이 많을 시 사용하면 성능이 좋다.
    • String과는 다르게 문자열 연산으로 인해 객체 공간이 부족한 경우에는 버퍼 크기를 늘려 유연하게 동작한다.
    • 서로 동일한 메소드를 가지고 있다.
  • StringBuffer와 StringBuilder 차이점

    • StringBuffer는 메소드별로 Synchronized Keyword가 존재하여 멀티 스레드 환경에서도 동기화를 지원하나, StringBuilder동기화를 보장하지 않는다.
    • 그로인해, 멀티 스레드 환경이라면 StringBuffer를 사용하고 단일 스레드 환경이라면 StringBuilder를 사용하는 것이 좋다. 단일 스레드에서 StringBuffer를 사용해도 되나 StringBuilder에 비해 성능이 좋지 않다.
    • why 성능? StringBuilder는 동기화를 고려하지 않기 때문에 단일 스레드 환경에서는 StringBuffer에 비해 연산처리가 빠른 것이다.

 

결과적으로,

String은 불변객체이기 때문에 문자열 연산이 많은 프로그래밍이 필요할 시 연속적으로 객체를 생성하므로 성능은 떨어지나, 조회가 많은 환경이나 멀티 스레드 환경에서는 유리하다.

StringBuffer와 StringBuilder는 문자열 연산이 자주 발생할 때 추가적인 객체 생성 없이 문자열 변경이 가능하므로 성능적으로 유리히다.

StringBuffer는 스레드에 안전한 프로그램이 필요할 때나, 개발 중인 시스템이 스레드에 안전한지 모르는 경우에 사용하면 좋다. (Thread-safe)

StringBuilder는 스레드에 안전한지의 여부가 전혀 관계없는 프로그램을 개발할 때 사용하면 좋다.

반응형

'Java > Java 기초' 카테고리의 다른 글

[Java 기초] JVM 구조  (0) 2020.01.24
[Java 기초] 배열과 리스트의 관계  (0) 2020.01.14
[Java 기초] 추상클래스와 인터페이스  (0) 2020.01.05
[Java 기초] 접근제한자  (0) 2020.01.04
[Java 기초] 상속과 구성  (2) 2020.01.04
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함