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),但并不知道这些对象具体在内存什么位置,这是类加载最后一步的事情,也就是解析