注:之前看过的文章保存到word里面,具体文章的出处我也忘了,转帖!优化
在JVM中,内存分为两个部分,Stack(栈)和Heap(堆),这里,咱们从JVM的内存管理原理的角度来认识Stack和Heap,并经过这些原理认清Java中静态方法和静态属性的问题。spa
通常,JVM的内存分为两部分:Stack和Heap。指针
Stack(栈)是JVM的内存指令区。Stack管理很简单,push必定长度字节的数据或者指令,Stack指针压栈相应的字节位移;pop必定字节长度数据或者指令,Stack指针弹栈。Stack的速度很快,管理很简单,而且每次操做的数据或者指令字节长度是已知的。因此Java 基本数据类型,Java 指令代码,常量都保存在Stack中。orm
Heap(堆)是JVM的内存数据区。Heap 的管理很复杂,每次分配不定长的内存空间,专门用来保存对象的实例。在Heap 中分配必定的内存来保存对象实例,实际上也只是保存对象实例的属性值,属性的类型和对象自己的类型标记等,并不保存对象的方法(方法是指令,保存在Stack中),在Heap 中分配必定的内存保存对象实例和对象的序列化比较相似。而对象实例在Heap 中分配好之后,须要在Stack中保存一个4字节的Heap 内存地址,用来定位该对象实例在Heap 中的位置,便于找到该对象实例。对象
因为Stack的内存管理是顺序分配的,并且定长,不存在内存回收问题;而Heap 则是随机分配内存,不定长度,存在内存分配和回收的问题;所以在JVM中另有一个GC进程,按期扫描Heap ,它根据Stack中保存的4字节对象地址扫描Heap ,定位Heap 中这些对象,进行一些优化(例如合并空闲内存块什么的),而且假设Heap 中没有扫描到的区域都是空闲的,通通refresh(其实是把Stack中丢失了对象地址的无用对象清除了),这就是垃圾收集的过程;关于垃圾收集的更深刻讲解请参考bitscn以前的文章《JVM内存模型及垃圾收集策略解析》。进程
JVM的体系结构内存
咱们首先要搞清楚的是什么是数据以及什么是指令。而后要搞清楚对象的方法和对象的属性分别保存在哪里。ci
1)方法自己是指令的操做码部分,保存在Stack中;it
2)方法内部变量做为指令的操做数部分,跟在指令的操做码以后,保存在Stack中(其实是简单类型保存在Stack中,对象类型在Stack中保存地址,在Heap 中保存值);上述的指令操做码和指令操做数构成了完整的Java 指令。内存管理
3)对象实例包括其属性值做为数据,保存在数据区Heap 中。
非静态的对象属性做为对象实例的一部分保存在Heap 中,而对象实例必须经过Stack中保存的地址指针才能访问到。所以可否访问到对象实例以及它的非静态属性值彻底取决于可否得到对象实例在Stack中的地址指针。
非静态方法和静态方法的区别:
非静态方法有一个和静态方法很重大的不一样:非静态方法有一个隐含的传入参数,该参数是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中,因此具备了全局属性。
在JVM中,静态属性保存在Stack指令内存区,动态属性保存在Heap数据内存区。