JVM启动之后就会从操作系统申请一大块内存区域,然后需要根据功能,把这个内存分成不同的小区域。
一个java进程中,可能包含着多个线程,多个线程之间,共用同一份堆和方法区,但是每个线程有自己的栈和程序计数器;每个进程有自己的堆,多个进程之间不共享❗
它是内存中最小的区域:保存了当前线程下一条要执行的指令的地址在哪
指令,也就是字节码,程序想要运行,JVM就得把字节码加载起来,放到内存中,程序就会一条一条把指令从内存取出来,放到CPU上执行,也就需要随时记住当前执行到哪一条了。这是因为CPU是并发式的执行程序的,不是只给你一个进程提供服务的,要伺候所有的进程。(这就相当于我们平时看书的时候,最后看到哪一页,就把书签放到那一页,以便于我们下一次就可以直接从那一页开始看了)
而因为操作系统是以线程为单位进行调度执行的,每个线程都得记录自己的执行位置,所以程序计数器是每个线程都有一个的。
里面放的主要是局部变量和方法调用信息
方法调用的时候,每次调用一个新的方法,就都涉及到“入栈”操作;每次执行完了一个方法,都涉及到“出栈”操作。
注意:栈空间其实是比较小的,虽说在JVM中可以配置栈空间的大小,但是一般也就是几M或者几十M,因此栈是很有可能会满了的。正常写代码,一般没事,就怕递归,并且递归条件没整好,那就直接栈溢出了StackOverflowException
栈是每个线程都有一份的
一个进程只有一份,多个线程共用一个堆,它也是内存中空间最大的区域
new出来的对象,就是在堆中,那么对象的成员变量自然也是在堆中了
这里我们要区分一个比较容易搞混的东西:
内置类型的变量在栈上,引用类型的变量在堆上 ,这个说法是否正确❓❓❓
答案是不正确,局部变量在栈上,成员变量和new的对象在堆上,跟是什么类型的变量没有关系❗
方法区中,放的是“类对象”。
所谓的“类对象”就是:我们所写的.java这样的代码会变成 .class(二进制字节码),.class 会被加载到内存中,也就被JVM构造成了类对象(加载的过程就成为“类加载”)
类对象里还有个很重要的东西,静态成员——被static修饰的成员,成为了“类属性”,而普通的成员,叫做“实例属性”
类对象就描述了这个类长啥样:类的名字是啥,里面有哪些成员,有哪些方法,每个成员叫啥名字是啥类型,是public/private,每个方法叫啥名字是啥类型,是public/private,方法里面包含的指令等等