内存分析
栈空间(stack)
- 连续的存储空间,遵循后进先出的原则,用于存放局部变量。
- 存放基本变量类型(包含这个基本类型的具体数值)
- 引用对象的变量(会存放这个引用在堆里面的具体地址)
堆空间(heap)
- 不连续的空间
- 存放new的对象和数组
- 可以被所有的线程共享,不会存放别的对象引用
方法区(method)
- 方法区在堆空间内
- 可以被所有线程共享
- 类的代码信息
- 静态变量和方法
- 常量池
Java中除了基本数据类型,其他的均是引用类型,包括类、数组等等。
数据类型的默认值,基本数据类型默认值:
数值型:0
浮点型:0.0
布尔型:false
字符型:\u0000
引用类型:null
变量初始化: 成员变量可不初始化,系统会自动初始化; 局部变量必须由程序员显式初始化,系统不会自动初始化。
简单通俗的讲,一个完整的Java程序运行过程会涉及以下内存区域:
1、程序计数器(Program Counter Register),让虚拟机中的字节码解释器通过改变计数器的值来获取下一条代码指令,比如分支、循环、跳转、异常处理、线程恢复等;
2、Java 虚拟机栈(Java Virtual Machine Stacks),栈顶存放当前方法,里面有局部变量表,
3、本地方法栈(Native Method Stacks),本地方法栈则,是为虚拟机使用到的Native方法服务,作用同虚拟机栈。
4、Java 堆(Java Heap)是Java 虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
5、方法区(Method Area)与Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
实例进行分析,创建类:
public class Student {
int score;
int age;
String name;
Computer computer;
public void study() {
System.out.println("studying...");
}
}
public class Computer {
int price;
String brand;
}
public class Test {
public static void main(String[] args) {
Student stu = new Student();
stu.name = "xiaoming";
stu.age = 10;
stu.study();
Computer c = new Computer();
c.brand = "Hasse";
System.out.println(c.brand);
stu.computer = c;
System.out.println(stu.computer.brand);
}
代码分析:
我们知道,程序的入口是main(),因而从main方法从上到下、从左到右进行分析。
Student stu = new Student();
-
首先,Java虚拟机(
JVM)去方法区寻找是否有Test类的代码信息,如果存在,直接调用。如果没有,通过类加载器(ClassLoader)把.class字节码加载到内存中,并把静态变量和方法、常量池加载(xiaoming,Hasse)。 -
走到
Student,以同样的逻辑对Student类进行加载;静态成员;常量池(studying)。 -
走到
stu,stu在main方法内部,因而是局部变量,存放在栈空间中。 -
走到
new Student,new出的对象(实例),存放在堆空间中,以方法区的类信息为模板创建实例。 -
=赋值操作,把new Student的地址告诉stu变量,stu通过四字节的地址(十六进制),引用该实例。 -
stu.name = “xiaoming”;,stu通过引用new Student实例的name属性,该name属性通过地址指向常量池的"xiaoming"常量。 -
stu.age = 10;,s实例的age属性是基本数据类型,基本数据类型直接赋值。 -
stu.study();,调用实例的方法时,并不会在实例对象中生成一个新的方法,而是通过地址指向方法区中类信息的方法。Computer c = new Computer();同stu变量的生成过程。c.brand = “Hasse”;同stu.name = "xiaoming"过程。stu.computer = c; -
把
c对象对Computer实例的引用赋值给Student实例的computer属性。亦即:该Student实例的computer属性指向该Computer类的实例。