jvm是为GC做铺垫。
JVM内存模型分为5个区域。
在java运行时,JVM虚拟机拿到自己能支配的内存后,将内存进行割分为5个区域:
- 栈区:存储函数当前运行过程中的临时变量。
- 堆区:主要存对象
- 本地方法栈:存储c++等native运行时候的栈区
- 程序计数器:指向程序当前运行的位置。
- 方法区: 存储一些元数据信息。JDK7之前叫永久代。JDK8+改为元数据空间。主要存储一些静态的方法和变量、类加载器、全局的变量等。
栈区、本地方法栈、程序计数器3部分合起来:线程私有。
即每个线程在运行和开辟的过程中会单独的创建这样一份内存。有多少个线程,可能就会有多少个这3部分。
而
堆区、方法区是全局共享的。
栈区存储函数当前运行过程中的临时变量和堆区存储对象不矛盾:
栈区存储的对象实际存储的是对象的引用类型,即存储的是对象的地址。
最终是指向堆区中实际存储的对象的。
小例子理解函数运行过程中内存是如何调用的:
1.值类型:
以fun()为例,首先在运行过程中它拿到了一个参数a,此时:
然后又定义了一个临时变量b,并打印了a+b,且最后给a赋值为11:
然后fun()运行结束,会将申请出来的空间删除掉。会先删除b,再删除a,最后将这块区域删除。
为什么打印了a为10?
是因为上面图中的栈区是fun()的栈区,因为是main中调用fun()的,所以fun()栈区前面紧跟main()的栈区,
。
打印a的时候其实已经运行完fun(),fun()申请的区域都删除了。所以打印a=10实参。
2.对象类型
还是看fun():
当执行到
因为new是开辟内存,会到堆上开辟:
此时默认age = 0 , name = null
这里的g是引用类型/指针/地址,g只占4字节
当运行:
会找到哦堆内存并赋值。
注意:String 不是值类型,是对象。
所以当执行g.name= "少女",会
String对象中char[]就是值类型了。
java的基础数据类型都是值类型,指针也是值类型,因而是直接存到内存,不是存地址去寻址。
当fun()执行完毕就清空。
但是栈清空掉会发现堆上的空间怎么清空【涉及GC机制。】
还有一点提的是main()就存储在方法区。
此处的i也是在方法区。
思考:
在main()就申请了g,在栈上存放了g的地址,在堆上开辟空间并赋值age = 21
运行到fun()就创建了g的副本(形参)其也在栈上存了地址且和main()中的地址指向相同。
当g.age = 22会去找当前栈中p变量寻址去堆上赋值22,然后fun()的栈区销毁。执行
此时是去堆上找,发现是已经修改过的。所以其打印22.