JAVA内存区域-介绍
- 线程共享区域
- 堆(Heap):实例数据存储的区域;
- 方法区(Method Area):类信息与运行时常量池存储;
- 线程私有区域
- 虚拟机栈(VM Stack):Java线程栈分配区域;
- 本地方法栈(Native Method Stack):同虚拟机栈;
- 程序计数器(Program Counter Register):同PC寄存器;
- Direct Buffer (堆外内存):NIO中可以在堆外分配内存,只有在System.gc()的时候会回收;
JAVA内存区域-相关JVM参数说明
1. 堆(Heap)相关
- -Xms:初始化堆大小设置
- -Xmx:最大堆大小设置
- -Xmn:新生代内存大小设置
- -XX:SurvivoRatio 设置新生代与survivor的大小比例
2. 方法区(Method Area)相关
- -XX:PermSize: 设置持久带初始值
- -XX:MaxPermSize:设置持久带最大值
3. 虚拟机栈相关
- -Xss:设置每个线程栈的大小
JAVA内存区域-图例说明
字节码文件通过类加载器加载,经过验证、准备、解析存入方法区。
当类被初次调用的时候JVM堆该类初始化操作,然后给调用者使用,而实例化后的数据存在堆中,局部变量则存在线程栈中。而各种类型数据在内存的具体表现见下图:
图中Person.class字节码文件通过类加载器被加载到方法区,方法去中的Person.class为该类的类信息(字段属性、字段类型、访问权限、方法、方法访问权限以及注解描述等等)。在该类被初始化化调用时,运行时常量池中的instance被赋值。这里我们可以通过以下这段程序来证明:
`public class Demo1{
private static final String name ;
static{
name=”Dog”;
System.out.println(name);
}
}
新建一个类来调用Demo1
public class Main{
public static void main(String… args){
Demo1 demo = new Demo1();// 类Demo1在此处被初始化,而常量name在初始化时被赋值
}
}
当执行Main.main()方法的时候,将会输出”Dog”。
`
执行过程
- jvm在虚拟机栈中创建了一个主线程的栈,且在方法区Person.class类信息中找到main方法,并压入一个main方法的栈帧。
- 当main方法执行调用start()方法时,执行引擎又到方法区中的Person.class类信息中找到start()方法相关信息,并压入线程栈一个start方法的栈帧。
- 而start方法执行过程中,又调用getInstance方法,同前面两步执行引擎会在线程栈中压入getInstance()方法栈帧,当执行完毕就会将其弹出。
- 然后start()方法执行完毕弹出,接着main()方法执行完毕弹出。
线程栈内部分析说明
- 线程栈中主要是由栈帧构成,而位与栈顶的栈帧称为当前栈帧,每一个栈帧对应一个方法,每一个方法中的局部变量保存在栈帧中的局部变量表中。
- 栈帧构成:局部变量表、操作数栈、返回地址、动态连接等
- 局部变量表说明:a.局部变量表中第一个索引0对应的slot保存的是当前所属对象的引用;b.boolean、char、byte、short、int、float、reference、returnAddress都占有一个Slot;而long、double占用2个slot。如上图中的start()方法栈中:第一个Slot中是一个reference类型数据,其只占用一个slot且指向当前对象内存中实例的引用;而因为该案例是单利所以第二个person也同样是reference类型指向同一个引用;而name是字符串,也是reference类型,其指向Person内存实例中name对象数据的引用;age是一个long类型的基本数据,所以在占用两个Slot且其值为0,直接保存在栈中。
- 其中Person类中由一个常量instance,它在类初始化的时候被赋值,而这个变量是被保存在运行时常量池中的,其值为Person实例的一个引用地址值。