반응형
Jake Seo
제이크서 위키 블로그
Jake Seo
전체 방문자
오늘
어제
  • 분류 전체보기 (715)
    • 일상, 일기 (0)
    • 백준 문제풀이 (1)
    • 릿코드 문제풀이 (2)
    • 알고리즘 이론 (10)
      • 기본 이론 (2)
      • 배열과 문자열 (8)
    • 데이터베이스 (15)
      • Planet Scale (1)
      • MSSQL (9)
      • 디비 기본 개념 (1)
      • SQLite 직접 만들어보기 (4)
    • 보안 (7)
    • 설계 (1)
    • 네트워크 (17)
      • HTTP (9)
      • OSI Layers (5)
    • 회고 (31)
      • 연간 회고 (2)
      • 주간 회고 (29)
    • 인프라 (52)
      • 도커 (12)
      • AWS (9)
      • 용어 (21)
      • 웹 성능 (1)
      • 대규모 서비스를 지탱하는 기술 (9)
    • 깃 (7)
    • 빌드 도구 (7)
      • 메이븐 (6)
      • 그레이들 (0)
    • Java (135)
      • 이펙티브 자바 (73)
      • 자바 API (4)
      • 자바 잡지식 (30)
      • 자바 디자인 패턴 (21)
      • 톰캣 (Tomcat) (7)
    • 프레임워크 (64)
      • next.js (14)
      • 스프링 프레임워크 (28)
      • 토비의 스프링 (6)
      • 스프링 부트 (3)
      • JPA (Java Persistence API) (5)
      • Nest.js (8)
    • 프론트엔드 (48)
      • 다크모드 (1)
      • 노드 패키지 관리 매니저 (3)
      • CSS (19)
      • Web API (11)
      • tailwind-css (1)
      • React (5)
      • React 새 공식문서 요약 (1)
      • HTML (Markup Language) (5)
    • 자바스크립트 (108)
      • 모던 자바스크립트 (31)
      • 개념 (31)
      • 정규표현식 (5)
      • 코드 스니펫 (1)
      • 라이브러리 (6)
      • 인터뷰 (24)
      • 웹개발자를 위한 자바스크립트의 모든 것 (6)
      • 팁 (2)
    • Typescript (49)
    • 리눅스와 유닉스 (10)
    • Computer Science (1)
      • Compiler (1)
    • IDE (3)
      • VSCODE (1)
      • IntelliJ (2)
    • 세미나 & 컨퍼런스 (1)
    • 용어 (개발용어) (16)
      • 함수형 프로그래밍 용어들 (1)
    • ORM (2)
      • Prisma (2)
    • NODEJS (2)
    • cypress (1)
    • 리액트 네이티브 (React Native) (31)
    • 러스트 (Rust) (15)
    • 코틀린 (Kotlin) (4)
      • 자바에서 코틀린으로 (4)
    • 정규표현식 (3)
    • 구글 애널리틱스 (GA) (1)
    • SEO (2)
    • UML (2)
    • 맛탐험 (2)
    • 리팩토링 (1)
    • 서평 (2)
    • 소프트웨어 공학 (18)
      • 테스팅 (16)
      • 개발 프로세스 (1)
    • 교육학 (1)
    • 삶의 지혜, 통찰 (1)
    • Chat GPT (2)
    • 쉘스크립트 (1)
    • 컴파일 (2)
    • Dart (12)
    • 코드팩토리의 플러터 프로그래밍 (4)
    • 플러터 (17)
    • 안드로이드 스튜디오 (1)
    • 윈도우즈 (1)
    • 잡다한 백엔드 지식 (1)
    • 디자인 패턴 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • serverless computing
  • 느린 쿼리
  • Pre-rendering
  • item7
  • 자바 검증
  • 서버리스 컴퓨팅
  • 싱글톤
  • 빈 검증
  • 싱글톤 패턴
  • rust
  • 이펙티브자바
  • item9
  • 슬로우 쿼리
  • 자바스크립트 인터뷰
  • 자바스크립트
  • 토비의 스프링
  • 싱글턴
  • 작업기억공간
  • try-with-resources
  • 자바
  • 도커공식문서
  • prerendering
  • NEXT JS
  • 외래키 제약조건
  • Next.js
  • item8
  • 메이븐 페이즈
  • 참조 해제
  • 알고리즘
  • 러스트
  • Java
  • 디자인패턴
  • 자바스크립트 면접
  • 프로그래머의 뇌
  • 이펙티브 자바
  • Javadoc 자바독 자바주석 주석 Comment
  • 플라이웨이트패턴
  • 추상 팩터리 패턴
  • pnpm
  • 객체복사
  • 스프링 검증
  • next js app
  • 이펙티브 자바 item9
  • MSSQL
  • 자료구조
  • 메이븐 라이프사이클
  • 메이븐 골
  • 팩터리 메서드 패턴
  • bean Validation
  • 자바 디자인패턴

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Jake Seo

제이크서 위키 블로그

Java/자바 API

자바 스트링 풀에 대해 쉽게 이해하기

2021. 12. 25. 16:07

자바의 스트링 풀

String 객체의 값은 불변이다.

자바에 익숙하지 않은 사람은 String 객체의 값이 불변이라는게 무슨 뜻인지 잘 모를 것이다. 불변이란 것은 한번 저장된 값이 절대 변하지 않는다는 뜻이다. 그런데, String 타입의 변수를 선언하고, 내부의 값을 바꾸면 값이 잘 바뀌는 것처럼 보이는 것은 왜일까?

사실 우리가 문자열 변수의 내용을 변경할 때는 실제로 값이 변경되는 것이 아니라, 스트링풀이라는 저장소에 미리 사용될 문자들이 몽땅 저장되어 있는데, 문자열 변수의 내용 자체가 바뀌는 것이 아니라 문자열 변수가 가리키는 주소만 바뀌는 것이다.

@Test
public void immutableString() {
    String a = "abc";
    System.out.println("a의 시스템상 주소: " + System.identityHashCode(a));
    a = "bbc";
    System.out.println("a의 시스템상 주소: " + System.identityHashCode(a));

    String b = "bbc";
    System.out.println("b의 시스템상 주소: " + System.identityHashCode(b));
    b = "bbc";
    System.out.println("b의 시스템상 주소: " + System.identityHashCode(b));
}

/*
출력결과
a의 시스템상 주소: 1939990953
a의 시스템상 주소: 1092572064
b의 시스템상 주소: 1092572064
b의 시스템상 주소: 1092572064
*/

위는 간단한 테스트코드와 그 출력결과이다. 위 결과로 다음과 같은 내용을 알 수 있다.

System.identityHashCode() 는 객체가 메모리에서 가지는 고유한 주소값을 반환한다.

  • String 객체에 들어있는 문자열 값이 바뀌면, String 값이 가리키는 주소도 바뀐다.
    • String 객체가 불변임을 알 수 있다. 불변이 아닌 객체의 내용을 바꿨다면, 주소 값은 그대로고 주소에서 가리키는 값의 내용이 바뀌었을 것이다.
  • String 객체에 들어있는 문자열을 같은 값으로 재할당해도 String 값이 가리키는 주소는 그대로다. 심지어 나중에는 a 변수와 b 변수가 가리키는 주소가 같아진다.
    • 같은 값의 문자열은 계속 같은 주소를 가리키게 된다. 문자열을 어딘가에서 저장해놓고 관리한다고 의심할 수 있다.

String은 사실 스트링 풀에 저장된다

스트링 풀이란 무엇인가?

내가 한번 썼던 문자열들을 저장해놓는 창고라고 생각하면 된다. 예를들면 위에서는 abc라는 문자열을 한번 사용했다. 이제 JVM은 해당 문자열을 Heap 영역에 저장해놓고, 프로그램에서 사용하는 문자열을 보고 해당 문자열이 이미 스트링 풀에 저장되어 있는지 확인한다.

스트링 풀이라는 저장소를 사용함으로써, 매번 같은 문자를 쓸 때 새로운 객체를 만들지 않아서 객체 생성 비용의 낭비를 줄인다.

자바 6 이하 버전에서는 PermGen 영역에 스트링 풀을 구성해놓았는데, 너무 긴 문자열을 스트링 풀에 저장할 때 Out of memory exception이 자주 터지는 관계로 자바 7 버전부터는 heap 영역에 저장하도록 바뀌었다고 한다.

스트링 풀에 문자열을 저장해놓고 쓰는 방식은 사실 다른 언어에서도 많이 차용하는 방식이다. 참고링크

String의 intern() 메서드

/**
     * Returns a canonical representation for the string object.
     * <p>
     * A pool of strings, initially empty, is maintained privately by the
     * class {@code String}.
     * <p>
     * When the intern method is invoked, if the pool already contains a
     * string equal to this {@code String} object as determined by
     * the {@link #equals(Object)} method, then the string from the pool is
     * returned. Otherwise, this {@code String} object is added to the
     * pool and a reference to this {@code String} object is returned.
     * <p>
     * It follows that for any two strings {@code s} and {@code t},
     * {@code s.intern() == t.intern()} is {@code true}
     * if and only if {@code s.equals(t)} is {@code true}.
     * <p>
     * All literal strings and string-valued constant expressions are
     * interned. String literals are defined in section {@jls 3.10.5} of the
     * <cite>The Java Language Specification</cite>.
     *
     * @return  a string that has the same contents as this string, but is
     *          guaranteed to be from a pool of unique strings.
     */
    public native String intern();

String은 위처럼 내부에 intern()이란 메서드가 있는데, 이 메서드를 간단히 표현한 말이 다음과 같다. returns a canonical representation 기본적인 표현을 반환한다는 것인데, 이는 다양한 방식으로 표현될 수 있는 기본 표현을 말한다. 잘 보면, 반환 타입도 native인 것을 볼 수 있다.

canonical representation에 대한 이해 참고 링크

이 메서드는 문자열이 문자열 풀에 있는지 먼저 확인 후에 있다면, 문자열 풀 속 해당 literal을 가리키는 참조를 가져올 것이고 만일 없다면 문자열 풀 속에 해당 literal을 넣고 그 주소를 가져올 것이다.

intern() 메서드 간단 테스트로 원리 깨닫기

@Test
public void stringInternTest() {
    String a = "Hello";
    String b = new String("Hello");

    System.out.println("a == b ? : " + (a == b)); // false
    System.out.println("a == b.intern() ? : " + (a == b.intern())); // true
}

위의 프린트 문 중 첫번째는 false를 반환하고 두번째는 true를 반환한다. 이로써 .intern() 메서드를 사용함으로써 확실히 메모리 낭비를 줄일 수 있다는 것을 깨달을 수 있다.

JVM에서 잘 알아서 해주기 때문에 아마 현실 세상에서는 딱히 intern() 메서드를 이용할 일은 없을 것 같다.

참고 링크

https://www.nakjunizm.com/2017/07/25/String_Pool/

반응형

'Java > 자바 API' 카테고리의 다른 글

자바 Objects.requireNonNull() 을 사용하는 이유  (1) 2023.11.06
자바 CountDownLatch란?  (0) 2022.01.03
자바 WeakMap 쉽게 알아보기  (0) 2021.12.23
    'Java/자바 API' 카테고리의 다른 글
    • 자바 Objects.requireNonNull() 을 사용하는 이유
    • 자바 CountDownLatch란?
    • 자바 WeakMap 쉽게 알아보기
    Jake Seo
    Jake Seo
    ✔ 잘 보셨다면 광고 한번 클릭해주시면 큰 힘이 됩니다. ✔ 댓글로 틀린 부분을 지적해주시면 기분 나빠하지 않고 수정합니다. ✔ 많은 퇴고를 거친 글이 좋은 글이 된다고 생각합니다. ✔ 간결하고 명료하게 사람들을 이해 시키는 것을 목표로 합니다.

    티스토리툴바