专栏原创出处:github-源笔记文件 (opens new window) ,github-源码 (opens new window),欢迎 Star,转载请附上原文出处链接和本声明。
Java JVM-虚拟机专栏系列笔记,系统性学习可访问个人复盘笔记-技术博客 Java JVM-虚拟机 (opens new window)
# 一、前言
熟悉了虚拟机的「内存数据区域」后,大多数的理解力提留在概念层面,找一段代码细细分析分配过程突然会感觉力不从心。
公网上流出许多错误的概念,比如 基础类型分配在栈上,对象分配在堆上!
这个错误的概念,虽然不足以影响我们编程,但是在虚拟机层面的深入学习会有一定误区。
# 代码级别内存分配总结
java 文件编译为 class 文件后,class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表(Constant Pool Table),用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
术语说明: Memory m = new Memory();
- m 为「实例引用」
- m 对应的对象数据为「实例数据」
针对 JDK 8 细化:
class 文件类的版本、字段、方法、接口等描述信息、常量池表->方法区(本地内存)
字符串常量池->方法区(堆)
类静态成员-基础数据(常量)->方法区
类静态成员-实例引用->方法区
类静态成员-实例数据->堆
类非静态成员-基础数据->堆
类非静态成员-实例引用->堆
类非静态成员-实例数据->堆
方法内部-基础数据->栈帧
方法内部-实例引用->栈帧
方法内部-实例数据->堆(逃逸分析,直接将实例数据分配在栈帧上)
# 逃逸分析
public class EscapeAnalysis { private int age; public EscapeAnalysis(int age) { this.age = age; } public int escape() { // 优化为:age 直接在栈上分配,return age + 8; return new EscapeAnalysis(10).age + 8; } }
Copied!
# 写一段简单的实战代码
public class MemoryAllocation { /* 类静态成员变量 */ private static final int DEFAULT_A = 2147483647; private static final String DEFAULT_B = "static-variable"; private static final byte[] DEFAULT_BYTE = new byte[100 * 1024]; private static final MemoryAllocation MEMORY_ALLOCATION = new MemoryAllocation(); /* 类非静态成员变量 */ private int a; private String b; private byte[] bytes; public MemoryAllocation() {} public MemoryAllocation(int a, String b, byte[] bytes) { this.a = a; this.b = b; this.bytes = bytes; } /* 类静态成员方法 */ public static MemoryAllocation defaultNoneInstance() { return MEMORY_ALLOCATION; } /* 类静态成员方法 */ public static MemoryAllocation defaultInstance() { return new MemoryAllocation(DEFAULT_A, DEFAULT_B, DEFAULT_BYTE); } }
Copied!
声明术语:Java 中除了基础数据类型后,其余的都看做是对象类型(包括数组,String)
下面的这些数据分配在哪儿?
类静态成员变量-基础数据类型(DEFAULT_A、DEFAULT_B)
类静态成员变量-对象数据类型(DEFAULT_BYTE、MEMORY_ALLOCATION)
类静态成员方法-(defaultNoneInstance、defaultInstance)
类非静态成员变量-基础数据类型(a、b)
类非静态成员变量-对象数据类型(byte)
类静态成员方法-(构造方法)
我们使用 javap -c -v MemoryAllocation.class
反汇编成字节码,保留关键信息逐步分析
# 运行时常量池(Constant pool)分析
运行时常量池是方法区的一部分,也就是 JDK 8 后的元空间(本地内存)的一部分。
public class MemoryAllocation Constant pool: #1 = Methodref #13.#39 // java/lang/Object."<init>":()V #2 = Fieldref #6.#40 // MemoryAllocation.a:I #3 = Fieldref #6.#41 // MemoryAllocation.b:Ljava/lang/String; #4 = Fieldref #6.#42 // MemoryAllocation.bytes:[B #5 = Fieldref #6.#43 // MemoryAllocation.MEMORY_ALLOCATION:LMemoryAllocation; #6 = Class #44 // MemoryAllocation #7 = Integer 2147483647 #8 = String #45 // static-variable #9 = Fieldref #6.#46 // MemoryAllocation.DEFAULT_BYTE:[B #10 = Methodref #6.#47 // MemoryAllocation."<init>":(ILjava/lang/String;[B)V #11 = Integer 102400 #12 = Methodref #6.#39 // MemoryAllocation."<init>":()V #13 = Class #48 // java/lang/Object #14 = Utf8 DEFAULT_A #15 = Utf8 I #16 = Utf8 ConstantValue #17 = Utf8 DEFAULT_B #18 = Utf8 Ljava/lang/String; #19 = Utf8 DEFAULT_BYTE #20 = Utf8 [B #21 = Utf8 MEMORY_ALLOCATION #22 = Utf8 LMemoryAllocation; #23 = Utf8 a #24 = Utf8 b #25 = Utf8 bytes #26 = Utf8 <init> #27 = Utf8 ()V #28 = Utf8 Code #29 = Utf8 LineNumberTable #30 = Utf8 LocalVariableTable #31 = Utf8 this #32 = Utf8 (ILjava/lang/String;[B)V #33 = Utf8 defaultNoneInstance #34 = Utf8 ()LMemoryAllocation; #35 = Utf8 defaultInstance #36 = Utf8 <clinit> #37 = Utf8 SourceFile #38 = Utf8 MemoryAllocation.java #39 = NameAndType #26:#27 // "<init>":()V #40 = NameAndType #23:#15 // a:I #41 = NameAndType #24:#18 // b:Ljava/lang/String; #42 = NameAndType #25:#20 // bytes:[B #43 = NameAndType #21:#22 // MEMORY_ALLOCATION:LMemoryAllocation; #44 = Utf8 MemoryAllocation #45 = Utf8 static-variable #46 = NameAndType #19:#20 // DEFAULT_BYTE:[B #47 = NameAndType #26:#32 // "<init>":(ILjava/lang/String;[B)V #48 = Utf8 java/lang/Object
Copied!
静态成员变量分析:
#7 = Integer 2147483647
为 DEFAULT_A 的常量#8 = String #45 // static-variable
为 DEFAULT_B 的常量#9 = Fieldref #6.#46 // MemoryAllocation.DEFAULT_BYTE:[B
DEFAULT_BYTE 字段对应符号引用指向了类.变量名符号.类型符号
非静态成员变量(也叫类实例变量)分析:
#2 = Fieldref #6.#40 // MemoryAllocation.a:I
引用符号#3 = Fieldref #6.#41 // MemoryAllocation.b:Ljava/lang/String;
引用符号#4 = Fieldref #6.#42 // MemoryAllocation.bytes:[B
引用符号
静态成员方法 defaultInstance 分析:
/* 类静态成员方法 */ public static MemoryAllocation defaultInstance() { return new MemoryAllocation(DEFAULT_A, DEFAULT_B, DEFAULT_BYTE); } Code: stack=5, locals=0, args_size=0 0: new #6 // 创建一个对象, 并将其引用引用值压入栈顶 3: dup // 复制栈顶数值并将复制值压入栈顶 4: ldc #7 // 将 2147483647 常量值从常量池中推送至栈顶 6: ldc #8 // 将 static-variable 常量值从常量池中推送至栈顶 8: getstatic #9 // Field DEFAULT_BYTE:[B 获取指定类的静态域 DEFAULT_BYTE , 并将其压入栈顶 11: invokespecial #10 // Method "<init>":(ILjava/lang/String;[B)V 构造方法 14: areturn // 返回对象引用 LineNumberTable: line 39: 0
Copied!
{ public MemoryAllocation(); 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 23: 0 line 24: 4 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LMemoryAllocation; public MemoryAllocation(int, java.lang.String, byte[]); descriptor: (ILjava/lang/String;[B)V flags: ACC_PUBLIC Code: stack=2, locals=4, args_size=4 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: iload_1 6: putfield #2 // Field a:I 9: aload_0 10: aload_2 11: putfield #3 // Field b:Ljava/lang/String; 14: aload_0 15: aload_3 16: putfield #4 // Field bytes:[B 19: return LineNumberTable: line 26: 0 line 27: 4 line 28: 9 line 29: 14 line 30: 19 LocalVariableTable: Start Length Slot Name Signature 0 20 0 this LMemoryAllocation; 0 20 1 a I 0 20 2 b Ljava/lang/String; 0 20 3 bytes [B public static MemoryAllocation defaultNoneInstance(); descriptor: ()LMemoryAllocation; flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=0, args_size=0 0: getstatic #5 // Field MEMORY_ALLOCATION:LMemoryAllocation; 3: areturn LineNumberTable: line 34: 0 public static MemoryAllocation defaultInstance(); descriptor: ()LMemoryAllocation; flags: ACC_PUBLIC, ACC_STATIC Code: stack=5, locals=0, args_size=0 0: new #6 // class MemoryAllocation 3: dup 4: ldc #7 // int 2147483647 6: ldc #8 // String static-variable 8: getstatic #9 // Field DEFAULT_BYTE:[B 11: invokespecial #10 // Method "<init>":(ILjava/lang/String;[B)V 14: areturn LineNumberTable: line 39: 0 static {}; descriptor: ()V flags: ACC_STATIC Code: stack=2, locals=0, args_size=0 0: ldc #11 // int 102400 2: newarray byte 4: putstatic #9 // Field DEFAULT_BYTE:[B 7: new #6 // class MemoryAllocation 10: dup 11: invokespecial #12 // Method "<init>":()V 14: putstatic #5 // Field MEMORY_ALLOCATION:LMemoryAllocation; 17: return LineNumberTable: line 15: 0 line 16: 7 } SourceFile: "MemoryAllocation.java"
Copied!
# 参考
- 字节码翻译字典-字节码指令集 (opens new window)
Gitalking ...