자바 상수 본질 및 세 가지 상수 탱크(소결)탐구
자바 의 상수 탱크 는 세 가지 형태 로 나 뉘 는데 그것 이 바로 정적 상수 탱크,문자열 상수 탱크 와 운행 시 상수 탱크 이다.
정적 상수 탱크
정적 상수 탱크,즉*.class 파일 의 상수 탱크 입 니 다.class 파일 의 상수 탱크 는 문자열(숫자)의 글자 만 포함 하 는 것 이 아니 라 클래스,방법 에 대한 정 보 를 포함 하여 class 파일 의 대부분 공간 을 차지 합 니 다.
이러한 상수 탱크 는 주로 두 가지 상수:글자 의 양(Literal)과 기호 인 용 량(Symbolic References)을 저장 하 는 데 사용 된다.글자 의 양 은 자바 언어 차원 의 상수 개념 에 해당 한다.예 를 들 어 텍스트 문자열,final 이 라 고 밝 힌 상수 값 등 이다.기호 인용 은 원래 의 이론 적 개념 을 컴 파일 하 는 데 다음 과 같은 세 가지 유형의 상수 에 속한다.
류 와 인터페이스의 전체 제한 명칭필드 이름과 설명자방법 명칭 과 설명자
그리고 실행 시 상 당량 탱크 는 jvm 가상 컴퓨터 가 클래스 로드 작업 을 마 친 후에 class 파일 의 상 당량 탱크 를 메모리 에 불 러 오고 방법 구역 에 저장 합 니 다.우리 가 흔히 말 하 는 상 당량 탱크 는 방법 구역 의 운행 시 상 당량 탱크 를 말 합 니 다.
런 타임 풀 은 Class 파일 상수 풀 에 비해 또 다른 중요 한 특징 은 동태 성 을 가 진 다 는 것 입 니 다.자바 언어 는 상수 가 반드시 컴 파일 기간 만 발생 하 는 것 을 요구 하지 않 습 니 다.즉,class 파일 에 미리 설 치 된 상수 풀 의 내용 이 방법 구역 에 들 어가 서 런 타임 풀 을 실행 할 수 있 는 것 이 아니 라 런 타임 에 새로운 상수 풀 에 넣 을 수도 있 습 니 다.이런 특성 을 개발 자 들 이 많이 이용 하 는 것 이 바로 String 류 의 intern()방법 이다.
String 의 intern()방법 은 상수 탱크 에 equal 같은 문자열 이 존재 하 는 지 찾 습 니 다.있 으 면 이 문자열 의 인용 을 되 돌려 주 고 없 으 면 자신의 문자열 을 상수 탱크 에 추가 합 니 다.
이렇게 보면 정적 상수 탱크,즉*.class 파일 의 상수 탱크 를 통 해 상수 의 의 미 를 더욱 탐구 할 수 있 습 니 다.
다음은 코드 를 보 겠 습 니 다.
public class Main {
public static void main(String[] args) {
System.out.println(Father.str);
}
}
class Father{
public static String str = "Hello,world";
static {
System.out.println("Father static block");
}
}
출력 결과또 다른 것 보기:
package com.company;
public class Main {
public static void main(String[] args) {
System.out.println(Father.str);
}
}
class Father{
public static final String str = "Hello,world";
static {
System.out.println("Father static block");
}
}
결과:단 하나
깜짝 놀 랐 죠?
우 리 는 두 번 째 프 리 젠 테 이 션 의 코드 블록 을 역 컴 파일 했다.
D:\CodePractise\untitled\out\production\untitled\com\company>javap -c Main.class
Compiled from "Main.java"
public class com.company.Main {
public com.company.Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #4 // String Hello,world
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}
여기 메 인(Main)이 있어 요.구조 방법 이 고 다음은 메 인 방법 이에 요.0:getstatic\#2 는 System.out 에 대응 합 니 다.
3:ldc\#4 에 대응 하 는 값 은 Hello 입 니 다.World 에서 확 정 된 값 은 Father 클래스 에서 꺼 내지 않 았 습 니 다.
ldc 는 int,float 또는 String 형식의 상수 값 을 상수 탱크 에서 스 택 꼭대기 로 푸 시 하 는 것 을 표시 합 니 다.
없다 니!!!Father.class 파일 을 삭제 하 더 라 도 이 코드 를 실행 할 수 있 습 니 다.Father 류 와 는 아무런 관계 가 없습니다.
실제로 컴 파일 단계 에서 상수 가 이 상수 호출 방법 이 있 는 상수 탱크 에 저 장 됩 니 다.
이 예 에서 알 수 있 듯 이 여기 의 str 는 상수 로 이 상수 를 호출 하 는 방법 은 main 방법 main 방법 이 있 는 유형 은 Main 이다.즉,컴 파일 한 후에 str 는 이러한 상수 탱크 에 놓 여 있다.
본질 적 으로 호출 클래스 는 정 의 된 상수 클래스 에 직접 인용 되 지 않 았 기 때문에 정 의 된 상수 클래스 의 초기 화 를 촉발 하지 않 습 니 다.
클래스 의 초기 화 는 클래스 의 로드 메커니즘 과 관련 되 어 있 습 니 다.
문자열 상수 풀(string pool 에 도 string literal pool 이 라 고 함)
전역 문자열 풀 의 내용 은 클래스 로 딩 이 완료 되 었 습 니 다.검증 을 거 친 후 준비 단계 에서 문자열 대상 인 스 턴 스 를 생 성 한 다음 에 이 문자열 대상 인 스 턴 스 의 인용 값 을 string pool 에 저장 합 니 다.(기억:string pool 에 저 장 된 것 은 구체 적 인 인 인 인 스 턴 스 대상 이 아 닌 인용 값 입 니 다.구체 적 인 인 인 인 스 턴 스 대상 은 더미 에서 열 린 공간 에 저 장 됩 니 다.)
문자열 상수 탱크 의 위치 에 대한 설명 이 정확 하지 않 습 니 다.
JDK 6.0 및 이전 버 전에 서 문자열 상수 탱크 는 Perm Gen 구역(즉 방법 구역)에 놓 여 있 습 니 다.
JDK 7.0 버 전에 서 문자열 상수 탱크 가 더미 로 옮 겨 졌 습 니 다.
HotSpot VM 에서 실 현 된 string pool 기능 은 StringTable 류 입 니 다.해시 표 입 니 다.상주 문자열(즉,우리 가 흔히 말 하 는 두 따옴표 로 묶 은 것)의 인용(상주 문자열 인 스 턴 스 자체 가 아 닌)이 저 장 됩 니 다.즉,쌓 여 있 는 일부 문자열 의 인 스 턴 스 가 이 StringTable 에 인 용 된 후에'상주 문자열'의 신분 이 부 여 된 것 과 같 습 니 다.이 StringTable 은 모든 HotSpot VM 의 인 스 턴 스 가 하나 밖 에 없 으 며 모든 클래스 에 공 유 됩 니 다.
실행 상수 풀 로 돌아 가기(runtime constant pool)
jvm 는 특정한 종 류 를 실행 할 때 반드시 로드,연결,초기 화 를 거 쳐 야 하 며 연결 은 검증,준비,해석 세 단 계 를 포함한다.
한편,클래스 가 메모리 에 불 러 오 면 jvm 은 정적 상수 탱크 의 내용 을 실행 시 상수 탱크 에 저장 합 니 다.이 를 통 해 알 수 있 듯 이 실행 시 상수 탱크 도 모든 종류 가 있 습 니 다.
정적 상수 탱크 에 저 장 된 것 은 글자 의 양 과 기호 참조 입 니 다.즉,대상 의 인 스 턴 스 가 아니 라 대상 의 기호 참조 값 입 니 다.그리고 해석(resolve)을 거 친 후에 기호 인용 을 직접 인용 으로 바 꾸 고 해석 하 는 과정 에서 문자열 상수 탱크 를 조회 합 니 다.즉,우리 가 위 에서 말 한 StringTable 은 상수 탱크 에서 인용 한 문자열 과 문자열 상수 탱크 에서 사용 하 는 문자열 이 일치 하도록 합 니 다.
예 를 하나 봅 시다.
import java.util.UUID;
public class Test {
public static void main(String[] args) {
System.out.println(TestValue.str);
}
}
class TestValue{
public static final String str = UUID.randomUUID().toString();
static {
System.out.println("TestValue static code");
}
}
결과:성명 자체 의 str 는 모두 상수 이다.관건 적 인 것 은 이 상수 의 값 이 컴 파일 시기 에 확 정 될 수 있 는 지 하 는 것 이다.분명히 이곳 의 예 는 컴 파일 기간 에 확실히 확정 되 지 않 는 다.운행 기간 에 만 확정 할 수 있 습 니 다.이 는 목표 류 를 초기 화 해 야 합 니 다.
상수 의 값 이 컴 파일 기간 에 확정 되 지 않 으 면 호출 클래스 의 상수 탱크 에 들 어가 지 않 습 니 다.
이 때 프로그램 이 실 행 될 때 이 상수 가 있 는 종 류 를 주동 적 으로 사용 하 게 되 고 이 종 류 를 초기 화 할 수 있 습 니 다.
(이것 은 클래스 의 로드 메커니즘 과 관련 되 어 있 으 며,뒤에 여기에 표 시 를 할 것 입 니 다)
역 컴 파일 탐구:
Compiled from "Test.java"
class com.leetcodePractise.tstudy.TestValue {
public static final java.lang.String str;
com.leetcodePractise.tstudy.TestValue();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
static {};
Code:
0: invokestatic #2 // Method java/util/UUID.randomUUID:()Ljava/util/UUID;
3: invokevirtual #3 // Method java/util/UUID.toString:()Ljava/lang/String;
6: putstatic #4 // Field str:Ljava/lang/String;
9: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
12: ldc #6 // String TestValue static code
14: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
17: return
}
TestValue 류 가 초기 화 될 것 이 분명 합 니 다.상수 소개 가 끝 난 후에 여기에 반 컴 파일 및 보조 문자 의 필 기 를 기록 하 세 요.
package com.company;
public class Main {
public static void main(String[] args) {
System.out.println(Father.str);
System.out.println(Father.s);
}
}
class Father{
public static final String str = "Hello,world";
public static final short s = 6;
static {
System.out.println("Father static block");
}
}
public class com.company.Main {
public com.company.Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #4 // String Hello,world
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: bipush 6
13: invokevirtual #6 // Method java/io/PrintStream.println:(I)V
16: return
}
bipush 는 단일 바이트(-128-127)의 상수 치 를 스 택 꼭대기 로 미 루 는 것 을 표시 합 니 다.더욱 가입 한다
package com.company;
public class Main {
public static void main(String[] args) {
System.out.println(Father.str);
System.out.println(Father.s);
System.out.println(Father.t);
}
}
class Father{
public static final String str = "Hello,world";
public static final short s = 6;
public static final int t = 128;
static {
System.out.println("Father static block");
}
}
역 컴 파일 을 진행 하 다
public class com.company.Main {
public com.company.Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #4 // String Hello,world
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: bipush 6
13: invokevirtual #6 // Method java/io/PrintStream.println:(I)V
16: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
19: sipush 128
22: invokevirtual #6 // Method java/io/PrintStream.println:(I)V
25: return
}
sipush 는 짧 은 정형 상수 치(-32768~32767)를 창고 꼭대기 로 밀어 넣 는 것 을 표시 합 니 다.재 변경
package com.company;
public class Main {
public static void main(String[] args) {
System.out.println(Father.str);
System.out.println(Father.t);
}
}
class Father{
public static final String str = "Hello,world";
public static final int t = 1;
static {
System.out.println("Father static block");
}
}
public class com.company.Main {
public com.company.Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #4 // String Hello,world
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: bipush 6
13: invokevirtual #6 // Method java/io/PrintStream.println:(I)V
16: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
19: sipush 128
22: invokevirtual #6 // Method java/io/PrintStream.println:(I)V
25: return
}
D:\CodePractise\untitled\out\production\untitled\com\company>javap -c Main.class
Compiled from "Main.java"
public class com.company.Main {
public com.company.Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #4 // String Hello,world
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iconst_1
12: invokevirtual #6 // Method java/io/PrintStream.println:(I)V
15: return
}
이곳 은 iconst 로 변 했다.1iconst 1 은 int 형식 1 을 스 택 꼭대기 로 전송 하 는 것 을 표시 합 니 다(iconstm1-iconst_5)
5 이상 이면 bipush 로 바 뀌 어 요.
m1 대응 은-1
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JPA + QueryDSL 계층형 댓글, 대댓글 구현(2)이번엔 전편에 이어서 계층형 댓글, 대댓글을 다시 리팩토링해볼 예정이다. 이전 게시글에서는 계층형 댓글, 대댓글을 구현은 되었지만 N+1 문제가 있었다. 이번에는 그 N+1 문제를 해결해 볼 것이다. 위의 로직은 이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.