Pre
JVM - 结合代码示例彻底搞懂Java内存区域_线程栈 | 本地方法栈 | 程序计数器
中我们探讨了线程栈中的内部结构 ,大家有么有想过main方法呢? 我们继续来看下main方法
示例demo
package com.gof.test; public class Artisan { public static final int FIVE = 5 ; // final静态变 public static User user = new User();// 静态变量 public static void main(String[] args) { Artisan artisan = new Artisan(); artisan.doSomething(); } public int doSomething() { int a = 1 ; int b = 2 ; int c = (a + b) * 10 ; return c; } }
final对象和static对象 ,我们知道都会存放在方法区(元空间)中的运行时常量池。 User 和 FIVE 会存放在该区域内。
方法区#运行时常量池 ,是方法区的一部分。 Class文件中的常量池表用于存放编译期间生成的各种字面量和符号引用,这部分内容将在类加载后放到方法区的运行时常量池中。
反汇编 ,附带附件信息
E:\Program Files\Java\jdk1.8.0_161\bin> ./javap -c -v D:\IdeaProjects\GOF23\target\classes\com\gof\test\Artisan.class > Artisan.txt PS E:\Program Files\Java\jdk1.8.0_161\bin>
可以看到 Constant Pool
Constant pool: #1 = Methodref #8.#35 // java/lang/Object."<init>":()V #2 = Class #36 // com/gof/test/Artisan #3 = Methodref #2.#35 // com/gof/test/Artisan."<init>":()V #4 = Methodref #2.#37 // com/gof/test/Artisan.doSomething:()I #5 = Class #38 // com/gof/test/User #6 = Methodref #5.#35 // com/gof/test/User."<init>":()V #7 = Fieldref #2.#39 // com/gof/test/Artisan.user:Lcom/gof/test/User; #8 = Class #40 // java/lang/Object #9 = Utf8 FIVE #10 = Utf8 I #11 = Utf8 ConstantValue #12 = Integer 5 #13 = Utf8 user #14 = Utf8 Lcom/gof/test/User; #15 = Utf8 <init> #16 = Utf8 ()V #17 = Utf8 Code #18 = Utf8 LineNumberTable #19 = Utf8 LocalVariableTable #20 = Utf8 this #21 = Utf8 Lcom/gof/test/Artisan; #22 = Utf8 main #23 = Utf8 ([Ljava/lang/String;)V #24 = Utf8 args #25 = Utf8 [Ljava/lang/String; #26 = Utf8 artisan #27 = Utf8 doSomething #28 = Utf8 ()I #29 = Utf8 a #30 = Utf8 b #31 = Utf8 c #32 = Utf8 <clinit> #33 = Utf8 SourceFile #34 = Utf8 Artisan.java #35 = NameAndType #15:#16 // "<init>":()V #36 = Utf8 com/gof/test/Artisan #37 = NameAndType #27:#28 // doSomething:()I #38 = Utf8 com/gof/test/User #39 = NameAndType #13:#14 // user:Lcom/gof/test/User; #40 = Utf8 java/lang/Object
找下User和 FIVE
总体关系
Note: FIVE 是 final的,上图少写了
代码示例论证
当我们执行main方法
public static void main(String[] args) { Artisan artisan = new Artisan(); artisan.doSomething(); }
Artisan artisan = new Artisan(); 这个对象会在堆上分配一块内存空间用来存储该对象。
main也是个方法,也得有方法栈, 那方法栈中的局部变量表中存放的artisan是个啥呢? 其实是 对象的引用 ,也就是对象的内存地址 。
这样,线程栈和堆的关系就产生了。
当类在编译阶段,静态类User , 会被分配到方法区 , 那 new User() 存放在 堆中,方法区的User 和 堆中的User 什么关系呢? 引用的关系,方法区中的User仅仅是个符号引用,指向真正的堆内存中的User对象。
这样 方法区和堆的关系就产生了 。
反汇编
Classfile /D:/IdeaProjects/GOF23/target/classes/com/gof/test/Artisan.class Last modified 2020-6-22; size 772 bytes MD5 checksum 89d38013e132ab14203f796617191dd9 Compiled from "Artisan.java" public class com.gof.test.Artisan minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #8.#35 // java/lang/Object."<init>":()V #2 = Class #36 // com/gof/test/Artisan #3 = Methodref #2.#35 // com/gof/test/Artisan."<init>":()V #4 = Methodref #2.#37 // com/gof/test/Artisan.doSomething:()I #5 = Class #38 // com/gof/test/User #6 = Methodref #5.#35 // com/gof/test/User."<init>":()V #7 = Fieldref #2.#39 // com/gof/test/Artisan.user:Lcom/gof/test/User; #8 = Class #40 // java/lang/Object #9 = Utf8 FIVE #10 = Utf8 I #11 = Utf8 ConstantValue #12 = Integer 5 #13 = Utf8 user #14 = Utf8 Lcom/gof/test/User; #15 = Utf8 <init> #16 = Utf8 ()V #17 = Utf8 Code #18 = Utf8 LineNumberTable #19 = Utf8 LocalVariableTable #20 = Utf8 this #21 = Utf8 Lcom/gof/test/Artisan; #22 = Utf8 main #23 = Utf8 ([Ljava/lang/String;)V #24 = Utf8 args #25 = Utf8 [Ljava/lang/String; #26 = Utf8 artisan #27 = Utf8 doSomething #28 = Utf8 ()I #29 = Utf8 a #30 = Utf8 b #31 = Utf8 c #32 = Utf8 <clinit> #33 = Utf8 SourceFile #34 = Utf8 Artisan.java #35 = NameAndType #15:#16 // "<init>":()V #36 = Utf8 com/gof/test/Artisan #37 = NameAndType #27:#28 // doSomething:()I #38 = Utf8 com/gof/test/User #39 = NameAndType #13:#14 // user:Lcom/gof/test/User; #40 = Utf8 java/lang/Object { public static final int FIVE; descriptor: I flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL ConstantValue: int 5 public static com.gof.test.User user; descriptor: Lcom/gof/test/User; flags: ACC_PUBLIC, ACC_STATIC public com.gof.test.Artisan(); descriptor: ()V flags: 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 4: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/gof/test/Artisan; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=1 0: new #2 // class com/gof/test/Artisan 3: dup 4: invokespecial #3 // Method "<init>":()V 7: astore_1 8: aload_1 9: invokevirtual #4 // Method doSomething:()I 12: pop 13: return LineNumberTable: line 9: 0 line 10: 8 line 11: 13 LocalVariableTable: Start Length Slot Name Signature 0 14 0 args [Ljava/lang/String; 8 6 1 artisan Lcom/gof/test/Artisan; public int doSomething(); descriptor: ()I flags: ACC_PUBLIC Code: stack=2, locals=4, args_size=1 0: iconst_1 1: istore_1 2: iconst_2 3: istore_2 4: iload_1 5: iload_2 6: iadd 7: bipush 10 9: imul 10: istore_3 11: iload_3 12: ireturn LineNumberTable: line 13: 0 line 14: 2 line 15: 4 line 16: 11 LocalVariableTable: Start Length Slot Name Signature 0 13 0 this Lcom/gof/test/Artisan; 2 11 1 a I 4 9 2 b I 11 2 3 c I static {}; descriptor: ()V flags: ACC_STATIC Code: stack=2, locals=0, args_size=0 0: new #5 // class com/gof/test/User 3: dup 4: invokespecial #6 // Method com/gof/test/User."<init>":()V 7: putstatic #7 // Field user:Lcom/gof/test/User; 10: return LineNumberTable: line 6: 0 } SourceFile: "Artisan.java"