从JVM内存管理的角度谈谈静态方法和静态属性

本文从JVM内存管理的角度探讨静态方法和静态属性,讲解了栈和堆的区别,以及它们如何影响静态和非静态方法、属性的行为。静态方法不需要对象实例即可调用,静态属性可能存储在指令区或堆中,而动态属性始终保存在堆中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作者     robbin   (http://hibernate.fankai.com/站长)   
    
  我试着从JVM的内存管理原理的角度来谈一下静态方法和静态属性的问题,不对的地方请指正。   
  JVM的内存分为两部分:stack和heap:   
    
  stack(栈)是JVM的内存指令区。stack管理很简单,push一定长度字节的数据或者指令,stack指针压栈相应的字节位移;pop一定字节长度数据或者指令,stack指针弹栈。stack的速度很快,管理很简单,并且每次操作的数据或者指令字节长度是已知的。所以Java基本数据类型,Java指令代码,常量都保存在stack中。   
    
  heap(堆)是JVM的内存数据区。heap的管理很复杂,每次分配不定长的内存空间,专门用来保存对象的实例。在heap中分配一定的内存来保存对象实例,实际上也只是保存对象实例的属性值,属性的类型和对象本身的类型标记等,并不保存对象的方法(方法是指令,保存在stack中),在heap中分配一定的内存保存对象实例和对象的序列化比较类似。而对象实例在heap中分配好以后,需要在stack中保存一个4字节的heap内存地址,用来定位该对象实例在heap中的位置,便于找到该对象实例。   
    
  由于stack的内存管理是顺序分配的,而且定长,不存在内存回收问题;而heap则是随机分配内存,不定长度,存在内存分配和回收的问题;因此在JVM中另有一个GC进程,定期扫描heap,它根据stack中保存的4字节对象地址扫描heap,定位heap中这些对象,进行一些优化(例如合并空闲内存块什么的),并且假设heap中没有扫描到的区域都是空闲的,统统refresh(实际上是把stack中丢失了对象地址的无用对象清除了),这就是垃圾收集的过程。   
    
  我们首先要搞清楚的是什么是数据,什么是指令?然后要搞清楚对象的方法和对象的属性分别保存在哪里?   
    
  为了便于描述,我简单的统称:   
    
  1)方法本身是指令的操作码部分,保存在stack中;   
    
  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中的(基本类型保存在stack中,对象类型地址保存在stack,值保存在heap中),而不同于动态属性保存在heap中。正因为都是在stack中,而stack中指令和数据都是定长的,因此很容易算出偏移量,也因此不管什么指令(类的方法),都可以访问到类的静态属性。也正因为静态属性被保存在stack中,所以具有了全局属性。   
    
  总结一下:静态属性保存在stack指令内存区,动态属性保存在heap数据内存区。

 

以下是关于这篇文章的一些讨论:

20 楼fankai(fankai)回复于 2003-09-23 11:54:01 得分 10

Hi,   大家好,我就是本文的作者robbin,真要彻底明确无误搞清楚静态方法和静态属性的问题,就必须去读JVM的C++源代码才能够说自己的结论是正确的,舍此别无它法。   
    
  不过我不会C++,所以也没有达到那种境界,只是根据自己的   计算机指令系统和CPU原理的知识,结合Java的实际经验进行反向推测,所以也许会有不准确,甚至错误的地方。   

 

……

 21 楼fankai(fankai)回复于 2003-09-23 12:08:23 得分 5

静态属性如果是基本类型,或者是这样的样子:   
    
  static   String   str   =   "Hello   World";   
    
  在声明的同时就赋值,那么是存放在类的常量池中的,也就是存放在内存指令区,如果是   
    
  static   String   str   ;   
    
  ....   
    
  str   =   "Hello   World";   
    
  那么静态属性的地址指针存放在内存指令区(stack),而内容放在heap中,地址指针是一个4字节的定长数据结构。  

 
22 楼fankai(fankai)回复于 2003-09-23 12:09:57 得分 5

>>   类的静态方法在   classLoader是   push   stack,那么在什么时候弹出   stack   
    
  方法是指令代码段,只要load进而JVM内存,就不会再释放。指令执行的时候有一个程序计数器,按照一定的顺序读程序,所谓pop   stack,只是不在PC指示的代码段中而已。

 

39 楼wolfsquare(狼平方 Swing报表工人)回复于 2003-09-23 21:57:22 得分 5

为了避免误导,还是由我来总结一下:   
  1.非静态方法和静态方法的不同:非静态方法有一个隐含的传入参数,该参数是JVM给它的类实例,   
  静态方法也有一个隐含的传入参数,该参数是该类的Class对象的引用,这里有点难理解,需要弄明白Class   A   和   A.class这两个概念的不一样.   
  2.静态属性和动态属性:   
      正式的说法是静态成员变量和动态成员变量,从以下可以看出静态成员变量和动态成员变量都是存放在堆中的.   
  The   Java   virtual   machine   has   a   heap   that   is   shared   among   all   Java   virtual   machine   threads.   The   heap   is   the   runtime   data   area   from   which   memory   for   all   class   instances   and   arrays   is   allocated.    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值