JVM-方法區
1. 方法區的作用
- 當類加載器加載完成類之後,會將類信息、運行時常量池、靜態變量(此處指的是指針,如果是一個對象對象的分配還是在堆中)等存儲在方法區;但在JDK不同版本對字符串常量和靜態變量的存儲有所不同,這部分內容後續列出
2. 不同版本的方法區
- JDK6:在JDK6以前方法區也就是HotSpot虛擬機中的永久代,此時類信息、運行時常量池、靜態變量等存儲在方法區
- JDK7:在JDK7中法區也是HotSpot虛擬機中的永久代,此時類信息以及其他信息存儲在永久代,但是靜態變量以及字符串常量已經存儲在堆中
- JDK8: 在這個版本中HotSpot中已經不存在永久代了,相對應的是MetaSpace也就是元數據空間,此時類信息以及其他信息存儲在元數據空間,但是靜態變量以及字符串常量已經存儲在堆中; 並且此時元數據空間不再使用的虛擬機內存而是使用的直接內存
3. 參數調整
- JDK7以前:
- -XX:PermSize, 默認20.75M
- -XX:MaxPermSize, 32位系統默認64M, 64位默認82M
- 8以後
- -XX:MetaspaceSize, 默認21M
- -XX:MaxMetaspaceSize,默認-1沒有限制
4.方法區是否GC?
- 方法區是需要GC的
- 回收目標
- 常量
- 被加載的類信息
5. 什麼是常量池?
-
常量池其實是類字節碼文件的一部分,如下java代碼對應字節碼文件中的
Constant pool:
中部分內容即是常量池,裏面主要使用類似key-value的形式記錄對應編號對應的數據;這些編號也就是在打嗎中引用的符號import java.util.Arrays; import java.util.List; public class hello { private static String name = "hello"; static { name = "world"; } public static void main(String[] args) { System.out.println(name); } }
Classfile /E:/workSpace/czProject/spring-framework-5.0.6.RELEASE/spring-framework-5.0.6.RELEASE/spring-hgy/out/production/classes/com/hgy/hello.class Last modified 2020年5月24日; size 654 bytes SHA-256 checksum 1813059a44947f95617719e673f9b09d5565c1e7e3c8d710fef3547d23de571e Compiled from "hello.java" public class com.hgy.hello minor version: 0 major version: 52 flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #7 // com/hgy/hello super_class: #8 // java/lang/Object interfaces: 0, fields: 1, methods: 3, attributes: 1 Constant pool: #1 = Methodref #8.#25 // java/lang/Object."<init>":()V #2 = Fieldref #26.#27 // java/lang/System.out:Ljava/io/PrintStream; #3 = Fieldref #7.#28 // com/hgy/hello.name:Ljava/lang/String; #4 = Methodref #29.#30 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = String #31 // hello #6 = String #32 // world #7 = Class #33 // com/hgy/hello #8 = Class #34 // java/lang/Object #9 = Utf8 name #10 = Utf8 Ljava/lang/String; #11 = Utf8 <init> #12 = Utf8 ()V #13 = Utf8 Code #14 = Utf8 LineNumberTable #15 = Utf8 LocalVariableTable #16 = Utf8 this #17 = Utf8 Lcom/hgy/hello; #18 = Utf8 main #19 = Utf8 ([Ljava/lang/String;)V #20 = Utf8 args #21 = Utf8 [Ljava/lang/String; #22 = Utf8 <clinit> #23 = Utf8 SourceFile #24 = Utf8 hello.java #25 = NameAndType #11:#12 // "<init>":()V #26 = Class #35 // java/lang/System #27 = NameAndType #36:#37 // out:Ljava/io/PrintStream; #28 = NameAndType #9:#10 // name:Ljava/lang/String; #29 = Class #38 // java/io/PrintStream #30 = NameAndType #39:#40 // println:(Ljava/lang/String;)V #31 = Utf8 hello #32 = Utf8 world #33 = Utf8 com/hgy/hello #34 = Utf8 java/lang/Object #35 = Utf8 java/lang/System #36 = Utf8 out #37 = Utf8 Ljava/io/PrintStream; #38 = Utf8 java/io/PrintStream #39 = Utf8 println #40 = Utf8 (Ljava/lang/String;)V { public com.hgy.hello(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 6: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/hgy/hello; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: getstatic #3 // Field name:Ljava/lang/String; 6: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 9: return LineNumberTable: line 14: 0 line 16: 9 LocalVariableTable: Start Length Slot Name Signature 0 10 0 args [Ljava/lang/String; static {}; descriptor: ()V flags: (0x0008) ACC_STATIC Code: stack=1, locals=0, args_size=0 0: ldc #5 // String hello 2: putstatic #3 // Field name:Ljava/lang/String; 5: ldc #6 // String world 7: putstatic #3 // Field name:Ljava/lang/String; 10: return LineNumberTable: line 8: 0 line 11: 5 line 12: 10 } SourceFile: "hello.java"
-
常量池中包含了各種字面量和對類型、域和方法的符號引用; 也就是統一了一個地方存放對象的引用避免重複存儲包括權限修飾符也是一個引用;實際上常量池就相當於一張表,存儲了當前類裏面所有的符號引用真實的地址,以及字面量等信息;
-
此時常量池中存的只是符號引用而已(System),但並不知道這些對象具體在內存什麼位置,這是類加載最後一步的事情,也就是解析