▶ StringPool
문자열 데이터가 담기는 영역으로 heap메모리 영역 안에 항상 고정적으로 상수풀 영역이 할당되어 있다. (JDk7 버전 이상)
String은 불변클래스(변하지 않는 클래스) 이기 때문에 수정하는 순간 기존의 값이 담겨있던 공간에서 수정되지 않고 새로운 주소값이 생긴다.
■ String의 특징
자바에서 가장 많이 사용되는 객체가 바로 String이다.
String이 메모리 영역에서 사라지지 않는다면 메모리부족 이슈가 발생한다.
기존의 상수풀의 연결이 끊어진 문자열들은 가비지 컬렉터가 알아서 정리해 준다. (java7 이후)
불변이라고 해서 수정이 안되는게 아니라, 있던 자리 그대로에서 수정이 안된다는 뜻이다. 때문에 매번 새로운 주소값을 참조하게 된다.
따라서 문자열을 사용할 때 new String을 통해 객체를 생성하는 것보다, 리터럴로 문자열을 만드는 것을 더 권장한다.
■ 1. 생성자를 통해 문자열 담기
public void method1() {
String str1 = new String("hello"); // new 객체생성 무조건 heap안에 고유한 저장공간이 할당된다.
String str2 = new String("hello");
System.out.println("str1 == str2 ? " + (str1 == str2)); // false
System.out.println(str1); // hello
// String클래스의 toString()메서드의 경우 실제 담겨있는 문자열을 반환하도록 오버라이딩 되어있다.
System.out.println(str1.equals(str2)); // true
// String클래스의 equals()메서드의 경우 주소값 비교가 아닌 문자열 비교를 하도록 오버라이딩 되어있다.
// 정말 주소값에 대해서 알고 싶다면 System.identityHashCode(변수);
System.out.println(System.identityHashCode(str1)); // 710239027
System.out.println(System.identityHashCode(str2)); // 2104545713
System.out.println(str1.hashCode()); // 99162322
System.out.println(str2.hashCode()); // 99162322
}
기존 hashcode()의 경우 16진수의 메모리 주소값을 10진수 형태로 반환해 준 값
String클래스의 hashcode()메서드의 경우 주소값 기반이 아닌 실제 담긴 문자열 기반으로 해시코드값을 만들어서 반환하도록 오버라이딩 되어있다.
오버라이딩을 한 이유
문자열은 문자열 기반의 hashcode값을 상수풀상의 주소값으로 저장하고 있다.(HashMap으로 구현)
따라서 상수풀 안에 데이터를 저장할 때 문자열을 해시코드로 변환한 후 그 자리에 데이터가 들어가 있는지 검사를 실행하고 이미 들어가 있다면 상수풀상의 주소값을 변수에 참조시키고, 없다면 상수풀에 등록시키고 그 주소값을 반환해 준다.
■ 2. 문자열 리터럴로 생성
프로그램 첫 시작 시에는 문자열 상수풀에 들어가 있는 데이터가 없다.
따라서 문자열을 사용할 때마다 추가가 된다.
public void method2() {
String str = new String("hello"); // heap메모리에 객체생성된다.
String str1 = "hello"; // 상수풀에 hello문자열 저장
String str2 = "hello"; // hello문자열이 이미 상수풀에 저장되어 있기 때문에 값을 저장하지 않고 같은 주소값을 str2에 저장
String str3 = "world"; // 상수풀에 world문자열 저장
System.out.println(str1 == str2); // true
System.out.println(System.identityHashCode(str1)); // 710239027
System.out.println(System.identityHashCode(str2)); // 710239027
// 상수풀에 "hello" 문자열이 저장되어 있어 idnetityHashCode를 써도 같은 값이 나온다.
}
String str = new String("hello").intern();
JVM이 클래스 안에 정보를 읽어드리면서 문자열 리터럴 값을 만나면 상수풀에 등록한다.
new String("hello")으로 감싸준다. 그 후에 intern()이라는 메서드를 호출한다.
intern() : 상수풀 안에 내가 생성한 문자열이 존재하는지 체크하고 존재한다면 문자열을 반환해 준다.
내가 생성한 문자열이 존재하지 않는다면 상수풀에 문자열을 등록해 준 후 문자열을 반환해 준다.
따라서 동일한 문자열 리터럴 제시시 새롭게 저장공간을 할당하지 않고 이미 저장된 주소값을
공유해서 사용하므로 효율적으로 저장공간을 활용할 수 있다.
'JAVA' 카테고리의 다른 글
JAVA (18-4) API (StringBuffer, StringBuilder) (0) | 2023.04.11 |
---|---|
JAVA (18-3) API (StringMethod) (0) | 2023.04.11 |
JAVA (18-1) API(Math) (0) | 2023.04.11 |
JAVA(17) 상속 (0) | 2023.04.10 |
JAVA(16) 인터페이스 (0) | 2023.04.08 |