在JVM的角度来分析内存:
Java JVM运行时数据:
JVM的执行过程:
1.加载.Class文件
2.管理并分配内存
3.执行垃圾收集
JVM由5个部分组成,分别是寄存器、栈、废区收集堆、存储区、指令集,
(借用网上的图片)
程序计数器:
解释器的依靠,通过线程行号记录,让线程轮流切换工作,
线程私有生命周期和线程相同,随JVM启动而生,JVM关闭而死
线程执行Java方法时,记录正在执行的虚拟机字节码指令地址
因为CPU只会执行一条线程中的指令,为了线程切换后能恢复到正确的执行位置,
所以每条线程都需要一个独立的程序计数器,各个线程之间的计数互不影响,独立 存储,这类内存区域就是“线程私有”的内存。
如果正在执行的是Natvie方法,计数器则为空。
Java虚拟机栈:
也是线程私有的,生命周期与线程相同;
用于存储局部变量,操作栈,动态链接(这个我还不知道是什么意思),方法出口
操作栈:
每一个方法被执行时就会创建一个栈帧(Stack Frame)
每一个方法被调用至执行完成的过程,就对应着一个栈帧入栈到出栈帧程
局部变量:
局部变量表存放了编译器克制的各种基本数据类型(Boolean,byte,char,int,short,long,double,float),引用对象和字节码指令地址
此处局部变量在java定义的规范中有两种异常:
StackOverflowError异常、OutOfMemoryError异常
如果线程请求的栈深度大于虚拟机所允许的深度则会抛出第一种异常;
如果虚拟机可以动态扩展,当扩展时无法申请到足够的内存就会抛出第二种异常。
本地方法栈:
与虚拟机栈类似,一个是为虚拟机执行java方法服务,一个是为虚拟机使用到的本地方法服务。
java堆:
是虚拟机管理的内存最大一块;
是线程共享的,在虚拟机启动时创建,也是垃圾收集器的主要战场。
可以处于物理上连续或不连续的内存空间。只要逻辑上连续即可,和磁盘一样。
堆是可扩展的,在堆中没有完成实例分配,并且无法扩展时,会抛出OutOfMemoryError异常。
方法区:
也是线程共享的,用于存储已经被虚拟机加载的类信息,常量,静态变量,编译器编译后的代码等。
这个内存区域的限制很松,可以选择不实现垃圾收集,同时和堆一样可以不需要连续的内存空间。
当方法区无法满足内存分配需求时,会抛出OutOfMemoryError异常。
在java内存的角度来看:
先了解一下java内存情况:
Java执行代码的时候,有下面4个区域: heap(堆内存)、stack(栈内存)、data segment(静态变量或常量存放区)、code segment(代码区)。
举例(借用网上的例子):
执行步骤:程序开始执行使,首先将要执行的代码从硬盘Load到内存中的code segment区中,然后找到main方法开始执行,执行过程根据语句中的变量的类型分配不同的内存空间。
public class Cat{
private static int sid=0;
private String name;
int id;
Cat(String name){
this.name=name;
id=sid++;
}
public static void main(String[] args){
Cat.sid=100;
Cat mimi=new Cat("mimi");
}
}
内存图 如下:
注意: String不是基本变量,它的值都是是字符串,所以会存储在data segment的字符常量区。我们创建一个string类型的变量可以选择使用new或者不使用,这是有区别的,使用new的情况下,会在堆中分配一个空间存放着new String()指向常量区的内容(如上图),而不使用则是直接从栈中直接指向常量区的内容。
l Code segment: 是用来存放方法的,在JVM加载类的时候,code segment记录了类的基本信息(类全名,该类是接口还是一个有内容类,该类的修饰符等等);类的详细信息(运行时常量,字段信息,方法信息,静态变量,改类的classloader(类加载器)的引用,该类的引用)。--------------------------------这里就扯到了java反射机制
l Stack:简单的理解就是控制程序的逻辑的地方,上面已经讲过一个线程就对应一个栈帧;
对于方法调用,Java栈内存,以帧的形式存放本地方法的调用状态,包括方法调用的 参数、局部变量、中间结果等(方法都是以方法帧的形式存放在方 法区的),每调用 一个方法就将对应该方法的方法帧压入Java 栈,成
为当前方法帧。
所以,基本变量和对象的引用变量都在方法的栈内存中分配,当方法的执行完成过后,java会释放掉对象的引用,同时堆内存里面的对象实例也会被gc对象清理。
这样也就清楚了,什么是局部变量,什么是成员变量。局部变量是方法自己的,方法执行完后,就会释放,成员变量只要对象还在,则不会消失,所以声明局部变量可以使程序更高效。
l Heap
java的堆是一个运行时的数据区,用来存储数据的单元,存放通过new关键字新建的对象和数组,对象从中分配内存。
在堆中声明的对象,是不能直接访问的,必须通过在栈中声明的指向该引用的变量来调用。引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。
由此就可以知道,对象的引用和对象的实例是完全不同的。