Java Constant Pool은 오래 지속되는 주제이며 면접관이 가장 좋아하는 주제입니다. 많은 종류의 질문이 있습니다. 이번에는 요약하겠습니다.
이론
먼저 JVM 가상 메모리 분포를 표현해 봅시다.
프로그램 카운터는 JVM이 프로그램을 실행하여 점프 지침을 저장하는 파이프 라인입니다. 이것은 너무 심오하고 이해가 안 돼요.
로컬 메소드 스택은 JVM이 운영 체제 방법을 호출하는 데 사용하는 스택입니다.
가상 머신 스택은 JVM이 Java 코드를 실행하는 데 사용하는 스택입니다.
이 방법 영역은 일부 상수, 정적 변수, 클래스 정보 등을 저장하며 메모리에서 클래스 파일의 저장 위치로 이해할 수 있습니다.
가상 머신 힙은 JVM이 Java 코드를 실행하는 데 사용하는 힙입니다.
Java의 상수 수영장은 실제로 정적 상수 풀 과 런타임 상수 풀 의 두 가지 형태로 나뉩니다.
소위 정적 상수 풀 은 *.class 파일의 상수 풀입니다. 클래스 파일의 상수 풀에는 문자열 (번호) 리터럴이 포함되어있을뿐만 아니라 클래스 및 메소드에 대한 정보가 포함되어 있으며 클래스 파일의 대부분을 차지합니다.
런타임 상수 풀 은 클래스로드 작업을 완료 한 후 JVM 가상 머신이 클래스 파일의 상수 풀을 메모리에로드하고 메소드 영역 에 저장됩니다. 우리가 종종 호출하는 상수 풀은 방법 영역의 런타임 상수 풀을 나타냅니다.
다음으로, 우리는 인터넷에서 인기있는 끊임없는 풀의 몇 가지 예를 인용 한 다음 설명합니다.
문자열 s1 = "hello"; 문자열 s2 = "hello"; 문자열 s3 = "hel" + "lo"; 문자열 s4 = "hel" + new String ( "lo"); 문자열 s5 = 새 문자열 ( "hello"); 문자열 s6 = s5.intern (); 문자열 s7 = "h"; 문자열 s8 = "Ello"; 문자열 s9 = s7 + s8; system.out.println (s1 == s2); // truesystem.out.println (s1 == s3); // truesystem.out.println (s1 == s4); // falsesystem.out.println (s1 == s9); // falsesystem.out.println (s4 == s5); // falsesystem.out.println (s1 == s6); // 진실
우선, Java에서 == 연산자는 직접 사용되며 내용이 아닌 두 문자열의 기준 주소를 비교합니다. 내용을 비교하려면 String.equals ()를 사용하십시오.
S1 == S2는 이해하기 쉽습니다. S1과 S2가 할당되면 문자열 리터럴을 사용합니다. 무뚝뚝하게 말하면, 그들은 직접 문자열을 죽음으로 씁니다. 편집하는 동안이 리터럴은 클래스 파일의 상수 풀에 직접 배치하여 재사용을 실현합니다. 런타임에서 일정한 풀을로드 한 후 S1과 S2는 동일한 메모리 주소를 가리므로 동일합니다.
S1 == S3에는 구덩이가 있습니다. S3은 동적으로 접합 된 문자열이지만, 스 플라이 싱에 관련된 모든 부분은 알려진 리터럴입니다. 컴파일 기간 동안이 스 플라이 싱이 최적화되며 컴파일러는 직접 접합하는 데 도움이됩니다. 따라서 문자열 s3 = "hel" + "lo"; 문자열 s3 = "hello"에 최적화됩니다. 클래스 파일에서는 S1 == S3이 사실입니다.
S1 == S4는 물론 같지 않습니다. S4도 스 플라이 싱되지만 새 문자열 ( "LO") 부분은 알려진 문자가 아니라 예측할 수없는 부분입니다. 컴파일러는이를 최적화하지 않습니다. 결과를 결정하려면 실행할 때까지 기다려야합니다. 문자열 불변 정리와 결합하여 S4가 할당되는 위치를 알고 있으므로 주소가 달라야합니다. 아이디어를 명확히하기위한 간단한 그림 :
S1 == S9는 동일하지 않으며 그 이유는 비슷합니다. 값을 할당 할 때 S7 및 S8이 사용하는 문자 리터럴은 S9, S7 및 S8에 스 플라이 싱 할 때 예측할 수 없습니다. 결국, 컴파일러는 컴파일러이며 통역사로 사용할 수 없으므로 최적화되지 않습니다. 실행되면 S7 및 S8에 스 플라이 싱 된 새 문자열은 힙에 확실하지 않으며 메소드 영역의 일정한 풀에서 S1 주소와 동일 할 수 없습니다.
S4 == S5는 더 이상 설명 할 필요가 없으며, 모두 같지 않으며, 둘 다 힙에 있지만 주소는 다릅니다.
S1 == S6의 동등성은 인턴 방법에 완전히 기인합니다. S5는 힙에 있고 내용은 안녕하세요입니다. 인턴 방법은 헬로 문자열을 상수 풀에 추가하고 상수 수영장에서 주소를 반환하려고합니다. 상수 수영장에 헬로 문자열이 있기 때문에 인턴 방법은 주소를 직접 반환합니다. S1은 컴파일 기간 동안 이미 일정한 풀을 가리키므로 S1과 S6은 동일한 주소를 가리 킵니다.
이 시점에서 우리는 세 가지 매우 중요한 결론을 도출 할 수 있습니다.
끊임없는 풀을 더 잘 이해하려면 편집 기간 동안의 행동에주의를 기울여야합니다.
런타임 상수 풀의 상수는 기본적으로 각 클래스 파일의 상수 풀에서 나옵니다.
프로그램이 실행될 때 JVM은 상수 풀에 상수를 수동으로 추가하지 않는 한 (예 : 인턴 메서드를 호출하는 경우) 자동으로 상수를 상수 풀에 자동으로 추가하지 않습니다.
위는 문자열 상수 풀 만 포함됩니다. 실제로 정수 상수 풀, 부동 소수점 상수 풀 등이 있지만 비슷하지만 수치 유형의 일정한 풀은 수동으로 추가 될 수 없습니다. 상수 풀의 상수는 프로그램이 시작될 때 결정됩니다. 예를 들어, 정수 상수 풀의 상수 범위는 -128 ~ 127입니다. 이 범위의 숫자 만 상수 풀에 사용할 수 있습니다.
관행
너무 많은 이론을 말하면서, 진짜 끊임없는 수영장을 만지겠습니다.
앞에서 언급했듯이 클래스 파일에는 정적 상수 풀이 있습니다. 이 상수 풀은 컴파일러에 의해 생성되며 Java 소스 파일에 리터럴을 저장하는 데 사용됩니다 (이 기사는 리터럴에만 중점을 둡니다). 다음 Java 코드가 있다고 가정합니다.
문자열 s = "hi";
편의상, 그것은 간단합니다. 맞습니다! 클래스 파일로 코드를 컴파일 한 후 Winhex를 사용하여 Binary Format Class 파일을 엽니 다. 그림과 같이 :
클래스 파일의 구조를 간단히 설명해 봅시다. 처음에 4 바이트는 클래스 파일의 마법 번호이며,이를 클래스 파일로 식별하는 데 사용됩니다. 무뚝뚝하게 말하면, 그것은 파일 헤더입니다.
다음 4 바이트는 Java의 버전 번호이고 여기의 버전 번호는 34입니다. 저자는 JDK8로 컴파일되고 버전 번호는 JDK 버전 수준에 해당하기 때문입니다. 더 높은 버전은 하위 버전과 호환 될 수 있지만 더 낮은 버전은 더 높은 버전을 실행할 수 없습니다. 따라서 독자가 JDK 버전의 다른 사람들의 수업 파일이 무엇인지 알고 싶다면이 4 바이트를 볼 수 있습니다.
다음은 끊임없는 풀 입구입니다. 상수 풀 상수의 수는 입구의 2 바이트로 식별됩니다. 이 예에서 값은 00 1a입니다. 10 진수로 번역되고 26입니다. 이는 25 개의 상수가 있음을 의미합니다. 0 번째 상수는 특별한 값이므로 상수는 25 개뿐입니다.
상수 수영장은 다양한 유형의 상수를 저장합니다. 그들은 모두 자신의 유형과 고유 한 스토리지 사양을 가지고 있습니다. 이 기사는 문자열 상수에만 중점을 둡니다. 문자열 상수는 01 (1 바이트)로 시작한 다음 문자열 길이를 2 바이트로 기록한 다음 문자열의 실제 내용을 기록합니다. 이 경우 : 01 00 02 68 69입니다.
다음으로 런타임 상수 풀에 대해 이야기 해 봅시다. 런타임 상수 풀이 메소드 영역에 있으므로 JVM 매개 변수를 통해 메소드 영역 크기를 설정할 수 있습니다. -xx : permsize, -xx : maxpermsize는 일정한 풀 크기를 간접적으로 제한 할 수 있습니다.
JVM 스타트 업 매개 변수가 -xx : permsize = 2m -xx : maxpermsize = 2m이라고 가정하고 다음 코드를 실행한다고 가정합니다.
// 자동 쓰레기 수집 목록을 방지하기 위해 참조를 유지하십시오. 목록 = new Arraylist <string> (); int i = 0; while (true) {// constant list.add (string.valueof (i ++). 인턴 ());}를 수동으로 추가합니다.이 프로그램은 즉시 다음과 같습니다. 예외는 스레드 "main"java.lang.outofMemoryError : Permgen Space Exception. Permgen Space는 방법 영역이며, 이는 상수 풀이 방법 영역에 있음을 나타 내기에 충분합니다.
JDK8에서 방법 영역이 제거되고 Metaspace 영역이 교체되었습니다. 따라서 새로운 JVM 매개 변수 : -xx : maxmetaspacesize = 2m을 사용하고 위의 코드를 계속 실행해야합니다. 마찬가지로, 런타임 상수 풀이 Metaspace 영역으로 나뉩니다. Metaspace 영역에 대한 구체적인 지식은 직접 검색하십시오.
이 기사의 모든 코드는 JDK7 및 JDK8에 따라 테스트 및 전달되었습니다. 다른 버전의 JDK는 약간의 차이가있을 수 있습니다. 직접 탐색하십시오.