String 은 두 가지 생성 방식이 있고 각각의 차이점이 존재한다.
new 연산자를 이용하여 String을 선언하는 방식과 리터럴을 이용한 방식에 차이에 대해서 알아보도록 한다.
String 리터럴로 생성하면 해당 String 값은 Heap 영역 내 String Constant Pool에 저장되어 재사용되지만, new 연산자로 생성하면 같은 내용이라도 여러 개의 객체가 각각 Heap 영역을 차지하게 된다.
public static void main(String... args) {
String s1 = "Cat";
String s2 = "Cat";
String s3 = new String("Cat");
String s4 = "Dog";
//객체의 주소값을 비교
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
//equals는 문자열을 비교하기 때문에 true
System.out.println(s1.eqauls(s3)); // true
}
Heap 영역에 생성된 String 객체와 리터럴을 이용해 string constant pool에 저장된 String 객체의 주소값은 다를 수밖에 없다.
Heap 영역에는 "Cat", "Dog"과 같은 값 들이 들어가게 되는데, 그림의 우측을 보면 중요한 차이를 발견할 수 있다.
String을 literal로 선언할 경우 내부적으로 String의 intern() 메서드가 호출되게 된다. intern() 메서드는 주어진 문자열이 string constant pool에 존재하는지 검색하고 있다면 그 주소값을 반환하고 없다면 string constant pool에 넣고 새로운 주소값을 반환하게 된다.
String Constants Pool에 저장하고 사용하는 것을 String Interning 이라고 한다. 이렇게 String Interning에 저장된 String 값은 불변성(Immutability)을 가지게 된다. 불변성을 가진다는 의미는 값은 변함이 없으며 동일한 String 값을 가지고 있다면 같은곳을 가리킨다는 의미이다.
Java6까지 string constant pool의 위치는 Perm 영역이였으며 Java7에서는 OOM 문제로 인하여 Heap 영역으로 변경 되었다고 한다.
Perm 영역은 고정된 사이즈이며 Runtime에 사이즈가 확장되지 않는다.
java6까지는 String의 intern() 메서드를 호출하는 것은 OutOfMemoryException을 발생시킬 수 있고 그 부분을 컨트롤할 수 없었기 때문에 거의 사용하지 않았다.
그래서 Oracle의 엔지니어들이 Java7에서 Perm 영역이 아닌 Heap 영역으로 string constant pool의 위치를 변경 되서 String 객체들은 GC의 대상이 된다.자세한 내용은 여기를 참고해보자.
https://starkying.tistory.com/entry/what-is-java-string-pool?category=689625
https://medium.com/@joongwon/string-%EC%9D%98-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B3%A0%EC%B0%B0-57af94cbb6bc
https://starkying.tistory.com/entry/what-is-java-string-pool