JVM 内存管理

来源:互联网 发布:java list转jsonarray 编辑:程序博客网 时间:2024/04/29 21:34

内存分stack和heap

stack(栈)是JVM的内存指令区。

存储数据或指令,数据或指令的长度已知。存储方式,push一定长度字节的数据或者指令,stack指针压栈相应的字节位移;pop一定字节长度数据或者指令,stack指针弹栈。速度快。
所以Java 基本数据类型,Java 指令代码,常量都保存在stack中。

heap (堆)是JVM的内存数据区。

管理复杂,每次分配不定长的内存空间,专门用来存储对象实例,实际上只存储对象实例的属性值、属性的类型、对象本身的类型标记等(方法是指令保存在stack中)。对象实例在heap 中分配好以后,需要在stack中保存一个4字节的heap 内存地址,用来定位该对象实例在heap 中的位置,便于找到该对象实例。

内存回收GC

由于stack的内存管理是顺序分配的,而且定长,不存在内存回收问题;而heap 则是随机分配内存,不定长度,存在内存分配和回收的问题;因此在JVM中另有一个GC进程,定期扫描heap ,它根据stack中保存的4字节对象地址扫描heap ,定位heap 中这些对象,进行一些优化(例如合并空闲内存块什么的),并且假设heap 中没有扫描到的区域都是空闲的,统统refresh(实际上是把stack中丢失了对象地址的无用对象清除了),这就是垃圾收集的过程。

数据

什么是数据什么是指令

方法本身是指令的操作码部分,保存在stack中;
方法内部变量作为指令的操作数部分,跟在指令的操作码之后,保存在stack中(实际上是简单类型保存在stack中,对象类型在stack中保存地址,在heap 中保存值);
上述的指令操作码和指令操作数构成了完整的Java 指令。
对象实例包括其属性值作为数据,保存在数据区heap 中,而对象实例必须通过stack中保存的地址指针才能访问到。因此能否访问到对象实例以及它的非静态属性值完全取决于能否获得对象实例在stack中的地址指针。

静态方法与非静态方法以及ClassLoader

静态方法有一个和非静态方法很重大的不同:非静态方法有一个隐含的传入参数,该参数是JVM给它的,和我们怎么写代码无关,这个隐含的参数就是对象实例在stack中的地址指针。因此非静态方法(在stack中的指令代码)总是可以找到自己的专用数据(在heap 中的对象属性值)。当然非静态方法也必须获得该隐含参数,因此非静态方法在调用前,必须先new一个对象实例,获得stack中的地址指针,否则JVM将无法将隐含参数传给非静态方法。
而静态方法无此隐含参数,因此也不需要new对象,只要class文件被ClassLoader load进入JVM的stack,该静态方法即可被调用。当然此时静态方法是存取不到heap 中的对象属性的。
总结一下该过程:当一个class文件被ClassLoader load进入JVM后,方法指令保存在stack中,此时heap 区没有数据。然后程序计数器开始执行指令,如果是静态方法,直接依次执行指令代码,当然此时指令代码是不能访问heap 数据区的;如果是非静态方法,由于隐含参数没有值,会报错。因此在非静态方法执行前,要先new对象,在heap 中分配数据,并把stack中的地址指针交给非静态方法,这样程序技术器依次执行指令,而指令代码此时能够访问到heap 数据区了。

静态属性和动态属性

前面提到对象实例以及动态属性都是保存在heap 中的,而heap 必须通过stack 中的地址指针才能够被指令(类的方法)访问到。因此可以推断出:静态属性是保存在stack 中的,而不同于动态属性保存在heap 中。正因为都是在stack中,而stack中指令和数据都是定长的,因此很容易算出偏移量,也因此不管什么指令(类的方法),都可以访问到类的静态属性。也正因为静态属性被保存在stack中,所以具有了全局属性。

总结一下

静态属性保存在stack指令内存区,动态属性保存在heap 数据内存区。

  1. 在java中有一个字符常量的池,专用来存储常量
  2. 基本类型是保存在stack 栈中,例如:int a=9; 先在栈中寻找是否有这个值,有的话将引用指向它,没有的话在上面创建这个值,接着把引用指向它
  3. 栈的存取速度较快 仅次于位于cpu中的寄存器
    但位于其中的数据大小和生存期必须是确定的 缺乏了灵活性 一般用于存储声明的变量
    堆由于可动态分配内存 使其速度慢于栈 一般用于开辟对象空间
    还由于生存期不必事先告诉编译器 事后得由gc收集

非静态方法是否共用同一块内存?

我们用field表示字段,用method表示方法,那么加上static区分后就 有四种:

class field:有用static修饰的fieldclass method:有用static修饰的methodinstance field:没有用static修饰的fieldinstance method:没有用static修饰的method

那么他们在内存中的表示为:
class field:共用一块记忆体
class method:共用一块记忆体
instance field:随着每个instance各有一块记忆体
instance method:共用一块记忆体
Java是根据this关键字做到的共享instance method的,通过this指针找到对应的数据。虽然他们两个对象共用一个方法,但是他们的方法中所产生的数据(不仅仅是实例属性)是私有的。这是因为参数被传进来变成call stack内的entry,而各个对象都有不同call stack,所以不会混淆。

JVM自动内存管理

0 0
原创粉丝点击