前两天去面试,和面试官聊到jvm的内存结构,因为这部分内容是比较基础的,太长时间不接触对细节总有点模糊,所以今天花了点时间总结一下。
1. jvm的内存可分为三个区域:栈(stack)、堆(heap)和方法区(method area)。
说明:方法区也是放到堆里的,只是因为存储的内容比较特殊,所以需要单独的说明。
2. 栈
2.1:栈是系统自动分配的一段连续的内存空间。
2.2:栈是和线程进行绑定的,jvm会为每个线程都创建一个栈,用于存放该线程执行方法的信息,主要存储的是实际的参数和局部变量等。
2.3:栈因为与线程进行绑定,所以是私有的,不能多个线程共享。
2.4:栈的存储内容是后进先出(LIFO)或先进后出(FILO)的。
2.5:栈是方法执行的内存模型,当每个方法被调用的时候都会创建一个栈帧,而栈帧里会存储局部变量、操作数和方法的出口等。
3. 堆
3.1:存储创建好的对象。
3.2:jvm只有一个堆,所有线程共享。
3.3:不连续的空间。
4. 方法区(静态区)
4.1:jvm只有个方法区,所有线程共享。
4.2:方法区是堆的一部分,只是用来存储类和常量相关的信息。用来存放程序中不变或唯一的内容。比如,类的信息,静态变量和字符常量等。
5. 简单的对象内存结构分析
先上代码:
public class Person { int id; String name; int age; void study() { System.out.println("学习"); } void play() { System.out.println("玩游戏"); } public static void main(String[] args) { Person p = new Person(); p.id = 1; p.name = "唐僧"; p.age = 18; p.play(); p.study(); } }
代码很简单,主要是用这段代码分析下它在内存里的结构。
下面解释下,
5.1:当jvm在启动的时候会进行类的装载,这个时候会把person这个类的代码信息装载到方法区里,也会把类里面的一些静态的方法变量和常量都装载到方法区里。(这个时候没有栈,在堆里也没有person的实例对象,只有方法区)
5.2:创建线程,创建与线程绑定的栈。
5.3:运行main方法(这个main方法的信息也是保存在方法区的),这个时候会在栈里面创建一个main方法的栈帧(15db9742为对象在内存里的地址),这个时候对象里的属性都是默认值(注意,这里Person的构造方法执行完后对应的栈帧也会被移除掉)。然后把p执行这个新创建的实例化对象的地址。
5.4:15db9742这个对象的属性赋值操作,基本数据类型是修改属性的值,而String类型是把成员变量指向了方法区里的常量。
5.4:main方法运行 执行Person p = new Person();这行代码的时候,会现在main方法的栈帧里创建一个p,此时为null。然后调用Person的构造方法,这个时候会在栈里创建Person的构造方法的栈帧,这个栈帧执行完后会在堆里创建一个Person的实例对象
5.5:代码继续执行到方法play和study。执行方法的时候进行数据打印输出,操作的时候内存里会从方法区的常量里进行寻找需要输出的内容。
结束。