Java 문자열이 변경 불가능합니까?


399

우리는 이것이 String자바에서 불변 이라는 것을 알고 있지만 다음 코드를 확인하십시오.

String s1 = "Hello World";  
String s2 = "Hello World";  
String s3 = s1.substring(6);  
System.out.println(s1); // Hello World  
System.out.println(s2); // Hello World  
System.out.println(s3); // World  

Field field = String.class.getDeclaredField("value");  
field.setAccessible(true);  
char[] value = (char[])field.get(s1);  
value[6] = 'J';  
value[7] = 'a';  
value[8] = 'v';  
value[9] = 'a';  
value[10] = '!';  

System.out.println(s1); // Hello Java!  
System.out.println(s2); // Hello Java!  
System.out.println(s3); // World  

이 프로그램은 왜 이렇게 작동합니까? 그리고 왜 가치가 s1있고 s2변화 되었는가 s3?


394
리플렉션을 사용하여 모든 종류의 바보 같은 트릭을 할 수 있습니다. 그러나 기본적으로 클래스에서 "제거 된 경우 보증 무효"스티커를 즉시 해제합니다.
cHao

16
@DarshanPatel은 SecurityManager를 사용하여 리플렉션을 비활성화합니다
Sean Patrick Floyd

39
당신이 정말로 물건을 엉망으로 만들고 싶다면 (Integer)1+(Integer)2=42캐시 된 오토 박스로 엉망으로 만들 수 있습니다 . (Disgruntled-Bomb-Java-Edition.) ( thedailywtf.com/Articles/Disgruntled-Bomb-Java-Edition.aspx )
Richard Tingle

15
거의 5 년 전에 쓴이 답변에 놀랄 것입니다. stackoverflow.com/a/1232332/27423-C #의 불변 목록에 관한 것이지만 기본적으로 동일합니다. 사용자가 내 데이터를 수정하지 못하게하려면 어떻게해야합니까? 대답은 할 수 없습니다. 반사는 매우 쉽습니다. 이 문제가없는 주류 언어 중 하나는 클로저 내부의 로컬 변수에 액세스 할 수있는 리플렉션 시스템이 없기 때문에 JavaScript입니다. 따라서 private은 키워드가 없지만 private을 의미 합니다.
Daniel Earwicker

49
누구든지 끝까지 질문을 읽고 있습니까? 문제는 "이 프로그램이 왜 이렇게 작동합니까? 왜 s1과 s2의 값이 s3에 대해 변경되고 변경되지 않습니까?"라는 질문을 반복하겠습니다. 문제는 s1과 s2가 왜 바뀌지 않았는가! 질문은 : 왜 s3가 변경되지 않습니까?
Roland Pihlakas

답변:


403

String 변경할 수 없지만 공개 API를 사용하여 변경할 수 없음을 의미합니다.

여기서하는 일은 리플렉션을 사용하여 일반 API를 우회하는 것입니다. 같은 방법으로 열거 형의 값을 변경하고 정수 자동 상자 등에 사용되는 조회 테이블을 변경할 수 있습니다.

이제 이유 s1s2변경 값은 둘 다 동일한 내부 문자열을 참조하기 때문입니다. 컴파일러는 이것을 수행합니다 (다른 답변에서 언급했듯이).

그 이유는 s3않습니다 하지 나는 그것이 공유 할 생각으로, 실제로 나에게 의외 조금했다 value배열 ( 은 자바의 이전 버전에서했던 자바 7u6 전에). 그러나의 소스 코드 를 보면 하위 문자열 Stringvalue문자 배열이 실제로 복사되어 Arrays.copyOfRange(..)있음을 알 수 있습니다. 이것이 변경되지 않는 이유입니다.

SecurityManager이러한 일을하는 악성 코드를 피하기 위해를 설치할 수 있습니다 . 그러나 일부 라이브러리는 이러한 종류의 리플렉션 트릭 (일반적으로 ORM 도구, AOP 라이브러리 등) 사용에 의존합니다.

*) 나는 처음에 Strings가 실제로 불변이 아니라 단지 "불변 불변"이라고 썼다. 현재 구현 String에서 value배열이 실제로 표시되어 있는 경우이 오류가 발생할 수 있습니다 private final. 그래도 Java에서 배열을 변경할 수없는 것으로 선언 할 수있는 방법이 없기 때문에 적절한 액세스 수정자를 사용해도 클래스 외부에 배열을 노출시키지 않도록주의해야합니다.


이 주제가 압도적으로 인기가있는 것처럼, 여기에 더 읽을 거리가 있습니다 : JavaZone 2009 의 Heinz Kabutz의 Reflection Madness 강연 은 OP의 많은 문제와 다른 반성 ... 음 ... 광기.

왜 이것이 유용한 지에 대해 다룹니다. 왜 대부분의 경우 피해야합니다. :-)


7
실제로, Stringinterning은 JLS의 일부입니다 ( "문자열 리터럴은 항상 String 클래스의 동일한 인스턴스를 참조합니다" ). 그러나 나는 String클래스 의 구현 세부 사항에 의존하는 것이 좋지 않다는 것에 동의합니다 .
haraldK

3
어쩌면 substring기존 배열의 "섹션"을 사용하지 않고 복사 하는 이유 는 거대한 문자열이 s있고 t그로부터 호출 된 작은 하위 문자열을 꺼내고 나중에 포기 s했지만 유지 t하면 거대한 배열이 살아남을 수 있기 때문입니다. (가비지 수집되지 않음). 따라서 각 문자열 값에 자체 관련 배열이있는 것이 더 자연 스럽습니까?
Jeppe Stig Nielsen

10
문자열과 하위 문자열간에 배열을 공유한다는 것은 모든 String 인스턴스가 참조 된 배열과 길이로 오프셋을 기억하기위한 변수를 가져야한다는 것을 암시했습니다 . 총 문자열 수와 응용 프로그램에서 일반 문자열과 하위 문자열 간의 일반적인 비율을 고려할 때 무시하지 않는 것이 오버 헤드입니다. 그들은 속도를 늦추고 의미 모든 문자열 작업에 대해 평가해야했다 때문에 모든 방금의 이익을 위해 문자열 작업을 하나 명의 작업, 싼 문자열.
Holger

2
@Holger-그렇습니다. 최근 JVM에서 오프셋 필드가 삭제되었습니다. 그리고 그것이 존재하더라도 그것은 자주 사용되지 않았습니다.
핫 릭

2
@ supercat : 네이티브 코드가 있는지 여부는 중요하지 않습니다. 동일한 JVM 내에서 문자열 및 하위 문자열에 대해 다른 구현을하거나 byte[]ASCII 문자열에 char[]대한 문자열을 가지고 있으며 다른 작업에서는 모든 작업이 이전에 어떤 종류의 문자열인지 확인해야 함을 의미합니다 운영. 이는 문자열을 사용하여 코드를 메소드에 인라인하는 데 방해가되며, 이는 호출자의 컨텍스트 정보를 사용하여 추가 최적화의 첫 번째 단계입니다. 이것은 큰 영향입니다.
Holger

93

Java에서 두 문자열 기본 변수가 동일한 리터럴로 초기화되면 두 변수에 동일한 참조를 지정합니다.

String Test1="Hello World";
String Test2="Hello World";
System.out.println(test1==test2); // true

초기화

그것이 비교가 true를 반환하는 이유입니다. 세 번째 문자열은 substring()동일한 문자열을 가리키는 대신 새 문자열을 만드는 데 사용 됩니다.

하위 문자열

리플렉션을 사용하여 문자열에 액세스하면 실제 포인터가 나타납니다.

Field field = String.class.getDeclaredField("value");
field.setAccessible(true);

따라서 이것으로 변경하면 포인터를 보유하는 문자열이 변경되지만 s3새 문자열로 작성 substring()되므로 변경되지 않습니다.

변화


이것은 리터럴에만 적용되며 컴파일 타임 최적화입니다.
SpacePrez

2
@ Zaphod42 사실이 아닙니다. intern리터럴이 아닌 문자열을 수동으로 호출 하여 이점을 얻을 수도 있습니다.
Chris Hayes

그러나 intern신중하게 사용하고 싶습니다 . 모든 것을 인턴하는 것은 큰 도움이되지 않으며 믹스에 반사를 추가 할 때 헤드 스크래칭 순간의 원천이 될 수 있습니다.
cHao

Test1Test1상충 test1==test2및 자바 명명 규칙을 따르지 않습니다.
c0der

50

String의 불변성을 피하기 위해 리플렉션을 사용하고 있습니다. 이것은 "공격"의 한 형태입니다.

이와 같이 만들 수있는 예제가 많이 있습니다 (예 : 객체를 인스턴스화 할 수도Void 있음). 문자열이 "불변"이 아님을 의미하지는 않습니다.

이 유형의 코드가 유리한 순간에 (GC 이전) 메모리에서 암호를 지우는 것과 같이 "유용한 코딩"이 될 수있는 사용 사례가 있습니다 .

보안 관리자에 따라 코드를 실행하지 못할 수 있습니다.


30

리플렉션을 사용하여 문자열 객체의 "구현 정보"에 액세스하고 있습니다. 불변성은 객체의 공용 인터페이스 기능입니다.


24

가시성 수정 자 및 최종 (즉, 불변성)은 Java의 악성 코드에 대한 측정이 아닙니다. 그것들은 단지 실수로부터 보호하고 코드를 유지 관리하기 쉽게 만드는 도구 일뿐입니다 (시스템의 큰 판매 포인트 중 하나). 따라서 백업 문자 배열과 같은 내부 구현 세부 정보에 액세스 할 수 있습니다.String 리플렉션을 통해 .

당신이 보는 두 번째 효과는 모든 String것이 변하는 동안 변화하는 것 s1입니다. Java String 리터럴의 특정 속성은 자동으로 인터 닝, 즉 캐시됩니다. 동일한 값을 가진 두 개의 문자열 리터럴은 실제로 동일한 객체입니다. 문자열을 만들면 new자동으로 구속되지 않으며이 효과가 나타나지 않습니다.

#substring최근까지 (Java 7u6) 비슷한 방식으로 작동하여 질문의 원래 버전에서 동작을 설명했습니다. 새로운 백업 문자 배열을 만들지 않았지만 원래 문자열의 배열을 재사용했습니다. 방금 오프셋과 길이를 사용하여 해당 배열의 일부만 나타내는 새로운 String 객체를 만들었습니다. 이것을 피하지 않으면 일반적으로 문자열을 변경할 수 없습니다. #substring또한 이 속성은 더 짧은 하위 문자열에서 생성 된 전체 원본 문자열을 가비지 수집 할 수 없음을 의미했습니다.

현재 Java 및 현재 버전의 질문에서 이상한 동작은 없습니다 #substring.


2
사실, 가시성 수정이 있습니다 (또는 적어도이었다)을 보호 againts 악성 코드로 구성 - 그러나 당신은 보안 관리자가 (System.setSecurityManager ()) 보호를 활성화하기 위해 설정해야합니다. 이것이 실제로 얼마나 안전한가는 또 다른 질문입니다 ...
sleske

2
액세스 수정자는 코드를 '보호' 하기위한 것이 아니라는 점을 강조하기 때문에 공감 이 필요 합니다. 이것은 Java와 .NET 모두에서 널리 이해되는 것으로 보입니다. 이전 의견은 그와 상충되지만; Java에 대해서는 많이 모르지만 .NET에서는 이것이 사실입니다. 어느 언어로도 사용자는 이것이 코드를 해킹 방지한다고 가정해서는 안됩니다.
Tom W

final성찰을 통해서도 계약을 위반하는 것은 불가능합니다 . 또한 다른 답변에서 언급했듯이 Java 7u6부터 #substring배열을 공유하지 않습니다.
ntoskrnl

실제로, 행동은 final시간이 지남에 따라 변했습니다 ... : -O Heinz의 "Reflection Madness"강연에 따르면 다른 스레드에 게시되었습니다. finalJDK 1.1, 1.3 및 1.4에서 최종을 의미하지만 항상 1.2를 사용하여 반사를 사용하여 수정할 수 있습니다. , 그리고 대부분의 경우 1.5와 6 에서 ...
haraldK

1
final필드는 native직렬화 된 인스턴스의 필드를 읽을 때 System.setOut(…)최종 System.out변수 를 수정할 때 직렬화 프레임 워크에서 수행 한 코드를 통해 변경 될 수 있습니다 . 후자는 액세스 대체로 리플렉션이 static final필드를 변경할 수 없으므로 가장 흥미로운 기능 입니다.
Holger

11

문자열 불변성은 인터페이스 관점에서입니다. 리플렉션을 사용하여 인터페이스를 무시하고 String 인스턴스의 내부를 직접 수정합니다.

s1그리고 s2그들은 모두 동일한 "인턴"문자열 인스턴스에 할당되기 때문에 모두 변경됩니다. 이 기사 에서 문자열 평등과 인턴에 대한 부분에 대해 좀 더 자세히 알 수 있습니다 . 샘플 코드에서 다음을 s1 == s2반환 한다는 사실에 놀랄 수도 있습니다 true!


10

어떤 버전의 Java를 사용하고 있습니까? Java 1.7.0_06부터 Oracle은 String, 특히 하위 문자열의 내부 표현을 변경했습니다.

오라클의 인용은 Java의 내부 문자열 표현을 조정합니다 .

새로운 패러다임에서 문자열 오프셋 및 개수 필드가 제거되었으므로 하위 문자열은 더 이상 기본 char [] 값을 공유하지 않습니다.

이 변경으로 인해 반사 (???)없이 발생할 수 있습니다.


2
OP가 이전 Sun / Oracle JRE를 사용중인 경우 마지막 명령문은 "Java!"로 표시됩니다. (실수로 게시 한대로). 이는 문자열과 하위 문자열 간의 값 배열 공유에만 영향을줍니다. 리플렉션과 같은 트릭 없이도 값을 변경할 수 없습니다.
haraldK

7

여기에는 실제로 두 가지 질문이 있습니다.

  1. 문자열은 정말 불변인가?
  2. s3가 왜 변경되지 않습니까?

포인트 1 : ROM을 제외하고는 컴퓨터에 불변 메모리가 없습니다. 오늘날 ROM조차도 때때로 쓰기 가능합니다. 메모리 주소에 쓸 수있는 코드가 항상 있습니다 (커널 또는 네이티브 코드가 관리되는 환경을 스테핑하는지 여부). 따라서 "현실"에서 절대로 변하지 않는 것은 아닙니다 .

포인트 2 : 이것은 부분 문자열이 아마도 새로운 문자열 인스턴스를 할당하고 있기 때문에 배열을 복사하기 때문입니다. 복사하지 않는 방식으로 하위 문자열을 구현할 수 있지만 그렇다고해서 그런 것은 아닙니다. 트레이드 오프가 관련되어 있습니다.

예를 들어, reallyLargeString.substring(reallyLargeString.length - 2)많은 양의 메모리가 살아 있거나 몇 바이트 만 유지되도록 참조를 보유해야 합니까?

그것은 부분 문자열이 어떻게 구현되는지에 달려 있습니다. 딥 카피는 적은 메모리를 유지하지만 약간 느리게 실행됩니다. 얕은 복사본은 더 많은 메모리를 유지하지만 더 빠릅니다. 딥 카피를 사용하면 2 개의 개별 힙 할당이 아닌 문자열 개체와 해당 버퍼를 한 블록에 할당 할 수 있으므로 힙 조각화를 줄일 수 있습니다.

어쨌든 JVM이 하위 문자열 호출에 깊은 사본을 사용하기로 선택한 것 같습니다.


3
실제 ROM은 플라스틱으로 포장 된 사진 인쇄만큼이나 변하지 않습니다. 웨이퍼 (또는 인쇄)가 화학적으로 현상 될 때 패턴은 영구적으로 설정된다. RAM 칩을 포함하여 전기적으로 변경 가능한 메모리 는 회로에 추가 전기 연결을 추가하지 않고 쓰기 위해 필요한 제어 신호에 전원을 공급할 수없는 경우 "참"ROM으로 작동 할 수 있습니다. 내장 장치에 출고시 설정되어 있고 백업 배터리로 유지 관리되는 RAM이 포함되어 있으며 배터리가 고장난 경우 출고시 내용을 다시로드해야하는 RAM이 포함되는 경우는 드 not니다.
supercat

3
@supercat : 컴퓨터는 임베디드 시스템 중 하나가 아닙니다. :) 진정한 유선 배선 ROM은 PC에서 10 년 또는 2 년 동안 일반적이지 않았습니다. 요즘 모든 것이 EEPROM이고 플래시입니다. 기본적으로 메모리를 나타내는 모든 사용자가 볼 수있는 주소는 쓰기 가능한 메모리를 나타냅니다.
cHao

@cHao : 많은 플래시 칩을 사용하여 부분을 쓰기 방지 할 수 있으며, 되돌릴 수없는 경우 정상적인 작동에 필요한 전압과 다른 전압을 적용해야합니다 (마더 보드에는 장착되어 있지 않음). 마더 보드가이 기능을 사용할 것으로 기대합니다. 또한 오늘날의 컴퓨터에 대해서는 확신 할 수 없지만 역사적으로 일부 컴퓨터에는 부팅 단계에서 쓰기 방지 된 RAM 영역이 있었으며 재설정으로 만 보호 할 수 없었습니다 (ROM에서 실행을 강제로 시작 함).
supercat

2
@supercat 나는 당신이 주제의 요점을 잃어 버렸다고 생각합니다. RAM에 저장된 문자열은 절대 불변이 될 것입니다.
스콧 Wisniewski

5

@haraldK의 답변에 추가하려면 앱에 심각한 영향을 줄 수있는 보안 해킹입니다.

첫 번째는 문자열 풀에 저장된 상수 문자열을 수정하는 것입니다. string String s = "Hello World";이로 선언되면 잠재적 인 재사용을 위해 특수 객체 풀에 배치됩니다. 문제는 컴파일러가 컴파일 타임에 수정 된 버전에 대한 참조를 배치하고 사용자가 런타임에이 풀에 저장된 문자열을 수정하면 코드의 모든 참조가 수정 된 버전을 가리 킵니다. 이로 인해 다음과 같은 버그가 발생합니다.

System.out.println("Hello World"); 

인쇄합니다 :

Hello Java!

그런 위험한 문자열에 대해 무거운 계산을 구현할 때 경험 한 또 다른 문제가있었습니다. 계산 중에 1000000 번 중 1 번과 같은 버그가 발생하여 결과가 결정적이지 않았습니다. JIT를 꺼서 문제를 찾을 수있었습니다. JIT를 끈 상태에서 항상 같은 결과를 얻었습니다. 내 생각 엔 JIT 최적화 계약을 위반 한이 문자열 보안 해킹이 원인이었습니다.


JIT없이 실행 시간이 느려지고 동시성이 줄어드는 스레드 안전 문제 일 수 있습니다.
Ted Pennings 2012 년

@TedPennings 내 설명으로는 너무 자세하게 설명하고 싶지 않았습니다. 실제로 현지화를 시도하는 데 며칠이 걸렸습니다. 두 개의 다른 언어로 작성된 두 텍스트 사이의 거리를 계산하는 단일 스레드 알고리즘이었습니다. 문제에 대한 두 가지 가능한 수정 사항을 발견했습니다. 하나는 JIT를 끄고 다른 하나는 String.format("")내부 루프 중 하나에 문자 그대로 no-op를 추가하는 것이 었습니다 . 다른 JIT 실패 문제 일 가능성이 있지만 JIT라고 생각합니다.이 no-op를 추가 한 후에는이 문제가 다시는 재현되지 않기 때문입니다.
Andrey Chaschev

JDK ~ 7u9의 초기 버전 으로이 작업을 수행 했으므로 가능합니다.
Andrey Chaschev

1
@Andrey Chaschev :“이 문제에 대해 가능한 두 가지 수정 사항을 찾았습니다”… String내부 를 해킹하지 않는 세 번째 가능한 수정 사항이 마음에 들지 않았습니까?
Holger

1
@Ted Pennings : 스레드 안전성 문제와 JIT 문제는 종종 매우 동일합니다. JIT는 다음에 의존하는 코드를 생성 할 수 있습니다.final 객체 생성 후 데이터를 수정할 때 중단되는 필드 스레드 안전 보장 있습니다. 따라서 원하는대로 JIT 문제 또는 MT 문제로 볼 수 있습니다. 실제 문제는 String변경 불가능한 것으로 예상되는 데이터 를 해킹 하고 수정하는 것입니다.
Holger

5

풀링 개념에 따라 동일한 값을 포함하는 모든 문자열 변수는 동일한 메모리 주소를 가리 킵니다. 따라서 같은 값의 "Hello World"를 포함하는 s1과 s2는 동일한 메모리 위치 (예 : M1)를 가리 킵니다.

반면에 s3에는 "World"가 포함되어 있으므로 다른 메모리 할당 (예 : M2)을 가리 킵니다.

이제 일어나고있는 일은 S1의 값이 변경되고 있다는 것입니다 (char [] 값을 사용하여). 따라서 s1과 s2가 가리키는 메모리 위치 M1의 값이 변경되었습니다.

결과적으로, 메모리 위치 M1이 수정되어 s1 및 s2의 값이 변경됩니다.

그러나 위치 M2의 값은 변경되지 않은 상태로 유지되므로 s3에는 동일한 원래 값이 포함됩니다.


5

s3이 실제로 변경되지 않는 이유는 Java에서 하위 문자열을 수행 할 때 하위 문자열에 대한 값 문자 배열이 내부적으로 복사되기 때문입니다 (Arrays.copyOfRange () 사용).

s1과 s2는 동일합니다. Java에서는 둘 다 동일한 내부 문자열을 참조하기 때문입니다. Java에서 의도적으로 설계된 것입니다.


2
이 답변이 어떻게 답변에 추가 되었습니까?
Gray

또한 이것은 매우 새로운 행동이며 어떤 사양에서도 보장되지는 않습니다.
Paŭlo Ebermann

String.substring(int, int)Java 7u6 으로 변경된 구현 . 7u6 전에, JVM은 원본에 대한 포인터 유지하는 것 Stringchar[]인덱스와 길이와 함께합니다. 7u6 이후에는 하위 문자열을 새 항목으로 복사합니다 String. 장단점이 있습니다.
Eric Jablow

2

문자열은 변경할 수 없지만 리플렉션을 통해 String 클래스를 변경할 수 있습니다. String 클래스를 실시간으로 변경 가능하도록 재정의했습니다. 원하는 경우 메소드를 공용 또는 개인용 또는 정적으로 재정의 할 수 있습니다.


2
필드 / 메소드의 가시성을 변경하면 컴파일 타임에 비공개이기 때문에 유용하지 않습니다.
보헤미안

1
메소드의 접근성을 변경할 수는 있지만 공개 / 개인 상태를 변경할 수 없으며 정적 상태로 만들 수 없습니다.
그레이

1

[면책 조항 이것은 "집에서 아이들에게 이것을하지 마십시오"라는 대답이 더 필요하다고 생각하기 때문에 의도적으로 의견이 많은 답변 스타일입니다.]

죄는 field.setAccessible(true);개인 분야에 대한 접근을 허용함으로써 공개 API를 위반한다고 말하는 선 입니다. 보안 관리자를 구성하여 잠글 수있는 거대한 보안 허점입니다.

문제의 현상은 리플렉션을 통해 액세스 수정자를 위반하기 위해 위험한 코드 라인을 사용하지 않을 때 볼 수없는 구현 세부 사항입니다. 분명히 두 개의 (보통) 불변 문자열은 동일한 문자 배열을 공유 할 수 있습니다. 하위 문자열이 동일한 배열을 공유하는지 여부는 하위 배열이 공유 배열을 공유 할 수 있는지 여부에 따라 다릅니다. 일반적으로 이것들은 보이지 않는 구현 세부 사항이며 해당 코드 줄로 헤드를 통해 액세스 수정자를 촬영하지 않는 한 알 필요가 없습니다.

리플렉션을 사용하여 액세스 수정자를 위반하지 않고는 경험할 수없는 이러한 세부 사항에 의존하는 것은 좋은 생각이 아닙니다. 해당 클래스의 소유자는 일반적인 공개 API 만 지원하며 향후 구현을 자유롭게 변경할 수 있습니다.

코드 라인은 총이 당신에게 그런 위험한 일을 강요하도록 머리를 잡을 때 정말 매우 유용하다고 말했습니다. 이 백도어를 사용하는 것은 일반적으로 죄를 짓지 않아도되는 더 나은 라이브러리 코드로 업그레이드해야하는 코드 냄새입니다. 그 위험한 코드 라인의 또 다른 일반적인 사용은 "부두 프레임 워크"(orm, injection container, ...)를 작성하는 것입니다. 많은 사람들이 그러한 프레임 워크에 대해 종교적 관계를 갖기 때문에 (그 둘 모두에 반대), 대다수의 프로그래머가 거기에 갈 필요는 없다고 말함으로써 화염 전쟁을 피하는 것을 피할 것입니다.


1

문자열은 JVM 힙 메모리의 영구 영역에 작성됩니다. 예, 변경이 불가능하며 생성 후에는 변경할 수 없습니다. JVM에는 세 가지 유형의 힙 메모리가 있기 때문에 1. 젊은 세대 2. 이전 세대 3. 영구 생성.

객체가 생성되면 젊은 세대 힙 영역과 문자열 풀링을 위해 예약 된 PermGen 영역으로 이동합니다.

가비지 콜렉션이 Java에서 작동하는 방식에서 자세한 정보를 얻을 수 있습니다 .


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.